Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
161 views
in Technique[技术] by (71.8m points)

.net - Late binding of COM->NET call with enum value argument(s)

First, note that this is just a learning exercise for me. The intent is to produce a .NET messagebox using nothing but pure C++ (not C++/CLI) and late binding as necessary.

This works fine with just a title and text in the messagebox.

However, when I try to specify buttons I get back 2147942487 "The parameter is incorrect". On the .NET side MessageBox.Show has this as an enum type argument. I am guessing that somehow the integer supplied as argument from C++, isn't automatically converted typewise.

I've tried to obtain an "object" of the enum type by calling Enum.ToObject. However, what's returned back to the COM/C++ side of things is just a VARIANT with type I4, i.e. a 32-bit integer.

Also, I've tried to Google this, but all I've found has been about how to obtain numerical values for .NET enum values. I have the numerical values. The problem appears to be how to get those numerical values automatically converted to enum-type arguments over in .NET?

However, I could be totally wrong about what the problem is.

So any help appreciated!

Code below (the hardcoded path will probably need adjustment on ur system):

/*
    // C# code that this C++ program should implement:
    using System.Windows.Forms;

    namespace hello
    {
     class Startup
     {
      static void Main( string[] args )
      {
       MessageBox.Show(
        "Hello, world!",
        ".NET app:",
        MessageBoxButtons.OK,
        MessageBoxIcon.Information
        );
      }
     }
    }
*/

#include <assert.h>
#include <algorithm>        // std::swap
#include <iostream>
#include <stddef.h>         // ptrdiff_t
#include <sstream>
#include <stdexcept>
#include <stdlib.h>         // EXIT_SUCCESS, EXIT_FAILURE
#include <string>
using std::swap;

//#undef  UNICODE
//#define UNICODE
//#undef  NOMINMAX
//#define NOMINMAX
//#undef  STRICT
//#define STRICT
//#include <windows.h>

#include <Mscoree.h>
#include <comdef.h>
_COM_SMARTPTR_TYPEDEF( ICorRuntimeHost, IID_ICorRuntimeHost );      // ICorRuntimeHostPtr

// #import is an MS extension, generates a header file. Will be replaced with #include.
#import "C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorlib.tlb" 
    raw_interfaces_only rename( "ReportEvent", "reportEvent" )

typedef mscorlib::_AppDomainPtr     AppDomainPtr;
typedef mscorlib::_AssemblyPtr      AssemblyPtr;
typedef mscorlib::_AssemblyNamePtr  AssemblyNamePtr;
typedef mscorlib::_MethodInfoPtr    MethodInfoPtr;
typedef mscorlib::_ObjectPtr        ObjectPtr;
typedef mscorlib::_TypePtr          TypePtr;


typedef ptrdiff_t   Size;
typedef Size        Index;

bool throwX( std::string const& s ) { throw std::runtime_error( s ); }


bool startsWith( wchar_t const prefix[], wchar_t const s[] )
{
    while( *prefix != 0 && *prefix == *s ) { ++prefix; ++s; }
    return (*prefix == 0);
}

template< class Predicate >
struct Is: Predicate
{};

template< class Type, class Predicate >
bool operator>>( Type const& v, Is< Predicate > const& check )
{
    return check( v );
}

struct HrSuccess
{
    bool operator()( HRESULT hr ) const
    {
        ::SetLastError( hr );
        return SUCCEEDED( hr );
    }
};

struct Text
{
private:
    std::ostringstream  stream;

    Text( Text const& );
    Text& operator=( Text const& );

public:
    Text() {}

    template< class Type >
    Text& operator<<( Type const& v )
    {
        stream << v;
        return *this;
    }

    operator std::string () const
    {
        return stream.str();
    }
};

template< class Type >
struct VariantType;

template<>
struct VariantType< IUnknown* >
{
    static VARENUM const    id  = VT_UNKNOWN;
};

template<>
struct VariantType< VARIANT >
{
    static VARENUM const    id  = VT_VARIANT;
};

class OleVector
{
private:
    typedef OleVector   ThisClass;
    SAFEARRAY*  descriptor_;
    Index       lowerBound_;
    Index       upperBound_;

    ThisClass( ThisClass const& );
    ThisClass& operator=( ThisClass const& );

    Index lowerBound() const
    {
        if( descriptor_ == 0 ) { return 0; }
        long result;
        SafeArrayGetLBound( descriptor_, 1, &result )
            >> Is< HrSuccess >()
            || throwX( "OleVector::lowerBound: SafeArrayGetLBound failed" );
        return result;
    }

    Index upperBound() const
    {
        if( descriptor_ == 0 ) { return 0; }
        long result;
        SafeArrayGetUBound( descriptor_, 1, &result )
            >> Is< HrSuccess >()
            || throwX( "OleVector::upperBound: SafeArrayGetUBound failed" );
        return result;
    }

public:
    OleVector(): descriptor_( 0 ) {}

    explicit OleVector( SAFEARRAY* descriptor )
        : descriptor_( descriptor )
        , lowerBound_( 0 )
        , upperBound_( 0 )
    {
        assert( descriptor_ == 0 || ::SafeArrayGetDim( descriptor_ ) == 1 );
        lowerBound_ = lowerBound();
        upperBound_ = upperBound();
    }

    template< class ElemType >
    OleVector( Size size, VariantType< ElemType > )
        : descriptor_( ::SafeArrayCreateVector( VariantType< ElemType >::id, 0, size ) )
        , lowerBound_( 0 )
        , upperBound_( size - 1 )
    {
        assert( descriptor_ != 0 && ::SafeArrayGetDim( descriptor_ ) == 1 );
        lowerBound_ = lowerBound();
        upperBound_ = upperBound();
    }

    ~OleVector()
    {
        if( descriptor_ != 0 )
        {
            ::SafeArrayDestroy( descriptor_ );
        }
    }

    SAFEARRAY* asSafeArray() const
    {
        return descriptor_;
    }

    VARENUM elemType() const
    {
        VARTYPE     result  = 0;
        if( descriptor_ != 0 )
        {
            ::SafeArrayGetVartype( descriptor_, &result )
                >> Is< HrSuccess >()
                || throwX( "OleVector::elemType: SafeArrayGetVartype failed" );
        }
        return VARENUM( result );
    }

    void swapWith( OleVector& other )
    {
        swap( descriptor_, other.descriptor_ );
        swap( lowerBound_, other.lowerBound_ );
        swap( upperBound_, other.upperBound_ );
    }

    void clear()
    {
        OleVector().swapWith( *this );
    }

    Size count() const
    {
        return (upperBound_ + 1) - lowerBound_;
    }

    Size elemSize() const
    {
        return (descriptor_ == 0? 0 : ::SafeArrayGetElemsize( descriptor_ ));
    }

    void getElement( Index i, void* pResult ) const
    {
        long internalIndex  = i + lowerBound_;
        ::SafeArrayGetElement( descriptor_, &internalIndex, pResult )
            >> Is< HrSuccess >()
            || throwX( "OleVector::getElement: SafeArrayGetElement failed" );
    }

    void setElement( Index i, void* pData )
    {
        long internalIndex  = i + lowerBound_;
        ::SafeArrayPutElement( descriptor_, &internalIndex, pData )
            >> Is< HrSuccess >()
            || throwX( "OleVector::setElement: SafeArrayPutElement failed" );
    }
};

template< class ElemType >
class ElemAccess
{
private:
    OleVector*  v_;

    template< class T >
    static void* safeArrayPutArg( T& v ) { return &v; }

    template<>
    static void* safeArrayPutArg( IUnknown*& v ) { return v; }

public:
    ElemAccess( OleVector& v )
        : v_( &v )
    {
        assert( VariantType< ElemType >::id == v_->elemType() );
        assert( v_->elemSize() == sizeof( ElemType ) );
    }

    ElemType elem( Index i ) const
    {
        ElemType    result;
        v_->getElement( i, &result );
        return result;
    }

    void setElem( Index i, ElemType v )
    {
        v_->setElement( i, safeArrayPutArg( v ) );
    }
};


void cppMain()
{
    ICorRuntimeHostPtr  pCorRuntimeHost;
    CorBindToRuntimeEx(
        L"v1.1.4322",         // LPWSTR pwszVersion,  // RELEVANT .NET VERSION.
        L"wks",                 // LPWSTR pwszBuildFlavor, // "wks" or "svr"
        0,                      // DWORD flags,
        CLSID_CorRuntimeHost,   // REFCLSID rclsid,
        IID_ICorRuntimeHost,    // REFIID riid,
        reinterpret_cast<void**>( &pCorRuntimeHost )
        )
        >> Is< HrSuccess >()
        || throwX( "CorBindToRuntimeEx failed" );

    pCorRuntimeHost->Start()    // Without this GetDefaultDomain fails.
        >> Is< HrSuccess >()
        || throwX( "CorRuntimeHost::Start failed" );

    IUnknownPtr     pAppDomainIUnknown;
    pCorRuntimeHost->GetDefaultDomain( &pAppDomainIUnknown )
        >> Is< HrSuccess >()
        || throwX( "CorRuntimeHost::GetDefaultDomain failed" );

    AppDomainPtr    pAppDomain  = pAppDomainIUnknown;
    (pAppDomain != 0)
        || throwX( "Obtaining _AppDomain interface failed" );

    AssemblyPtr     pCoreLibAssembly;
    {
        SAFEARRAY*  rawAssembliesArray;
        pAppDomain->GetAssemblies( &rawAssembliesArray )
            >> Is< HrSuccess >()
            || throwX( "_AppDomain::GetAssemblies failed" );

        OleVector   assemblies( rawAssembliesArray );
        Size const  n       = assemblies.count();

        std::cout << n << " assemblies loaded." << std::endl;
        if( n > 0 )
        {
            std::cout << "Array elemtype " << assemblies.elemType() << "." << std::endl;

            ElemAccess< IUnknown* >     elems( assemblies );
            for( Index i = 0;  i < n;  ++i )
            {
                IUnknownPtr         pUnknown( elems.elem( i ) );
                AssemblyPtr         pAssembly( pUnknown );

                _bstr_t     displayName;
                pAssembly->get_ToString( displayName.GetAddress() )
                    >> Is< HrSuccess >()
                    || throwX( "_Assembly::get_ToString failed" );
                std::cout
                    << i + 1 << ": "
                    << """ << displayName.operator char const*() << """
                    << std::endl;
                if( startsWith( L"mscorlib,", displayName ) )
                {
                    pCoreLibAssembly = pAssembly;
                }
            }
        }
    }
    (pCoreLibAssembly != 0)
        || throwX( "mscorlib was not loaded by the .NET initialization" );

    TypePtr     pStringType;
    pCoreLibAssembly->GetType_2( _bstr_t( L"System.String" ), &pStringType )
        >> Is< HrSuccess >()
        || throwX( "_Assembly::GetType failed (for System.String)" );
    (pStringType != 0 )
        || throwX( "System.String type not found" );

    TypePtr     pEnumType;
    pCoreLibAssembly->GetType_2( _bstr_t( L"System.Enum" ), &pEnumType )
        >> Is< HrSuccess >()
        || throwX( "_Assembly::GetType failed (for System.Enum)" );
    (pEnumType != 0 )
        || throwX( "System.Enum type not found" );

    AssemblyPtr     pFormsAssembly;
    pAppDomain->Load_2( _bstr_t( L"System.Windows.Forms, Version=1.1.4322.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" ), &pFormsAssembly )
        >> Is< HrSuccess >()
        || throwX( "Loading System.Windows.Forms assembly failed" );
    std::cout << "Loaded th

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I think your problem is that you're invoking an older version of the .Net framework (v1.1 is over 7 years old), and I suspect that its marshalling or method binding is a bit restrictive. The versions I tested (v2.0, v4.0) let me just pass an int for any enum types to Invoke, which should be sufficient for your little test.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...