代码之家  ›  专栏  ›  技术社区  ›  Cheers and hth. - Alf

带有枚举值参数的com->net调用的后期绑定

  •  -1
  • Cheers and hth. - Alf  · 技术社区  · 15 年前

    首先,注意这对我来说只是一个学习练习。目的是生成一个.NET消息框,只需要纯C++(不是C++ +CLI)和后期绑定。

    只需在消息框中添加一个标题和文本就可以了。

    但是,当我尝试指定按钮时,我返回2147942487“参数不正确”。在.NET端 MessageBox.Show 把这个当作 enum 类型参数。我猜想,以C++为参数提供的整数不一定是自动转换成类型的。

    我试图通过调用 Enum.ToObject . 然而,返回到COM/C++方面的东西只是一个 VARIANT 带类型 I4 ,即32位整数。

    另外,我也尝试过谷歌搜索,但我所发现的只是如何获得.NET枚举值的数值。我有数值。问题 出现 如何在.NET中自动将这些数值转换为枚举类型参数?

    然而, 我可能完全错了

    所以感谢您的帮助!

    下面的代码(硬编码路径可能需要在UR系统上进行调整):

    /*
        // 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 the System.Windows.Forms assembly." << std::endl;
    
        _bstr_t     formsAssemblyDisplayName;
        pFormsAssembly->get_ToString( formsAssemblyDisplayName.GetAddress() )
            >> Is< HrSuccess >()
            || throwX( "_Assembly::get_ToString failed" );
        std::cout << "\"" << formsAssemblyDisplayName.operator char const*() << "\"" << std::endl;
    
        TypePtr     pMessageBoxButtonsType;
        pFormsAssembly->GetType_2( _bstr_t( L"System.Windows.Forms.MessageBoxButtons" ), &pMessageBoxButtonsType )
            >> Is< HrSuccess >()
            || throwX( "_Assembly::GetType failed (for System.Windows.Forms.MessageBoxButtons)" );
        (pMessageBoxButtonsType != 0 )
            || throwX( "System.Windows.Forms.MessageBoxButtons type not found" );
    
        TypePtr     pMessageBoxIconType;
        pFormsAssembly->GetType_2( _bstr_t( L"System.Windows.Forms.MessageBoxIcon" ), &pMessageBoxIconType )
            >> Is< HrSuccess >()
            || throwX( "_Assembly::GetType failed (for System.Windows.Forms.MessageBoxIcon)" );
        (pMessageBoxIconType != 0 )
            || throwX( "System.Windows.Forms.MessageBoxIcon type not found" );
    
        TypePtr     pMessageBoxType;
        pFormsAssembly->GetType_2( _bstr_t( L"System.Windows.Forms.MessageBox" ), &pMessageBoxType )
            >> Is< HrSuccess >()
            || throwX( "_Assembly::GetType failed" );
        (pMessageBoxType != 0 )
            || throwX( "System.Windows.Forms.MessageBox type not found" );
    
    
        Size const  nArgs = 2;      // With 3 args (3rd is a button spec), the invocation fails.
    
        OleVector                   argTypesVec( nArgs, VariantType< IUnknown* >() );
        ElemAccess< IUnknown*>      argTypes( argTypesVec );
        argTypes.setElem( 0, static_cast<IUnknown*>( pStringType ) );
        argTypes.setElem( 1, static_cast<IUnknown*>( pStringType ) );
        if( nArgs > 2 ) { argTypes.setElem( 2, static_cast<IUnknown*>( pMessageBoxButtonsType ) ); }
        if( nArgs > 3 ) { argTypes.setElem( 3, static_cast<IUnknown*>( pMessageBoxIconType ) ); }
    
        MethodInfoPtr   pShowMethod;
        pMessageBoxType->GetMethod_5(
            _bstr_t( L"Show" ), argTypesVec.asSafeArray(), &pShowMethod
            )
            >> Is< HrSuccess >()
            || throwX( "MessageBox::GetMethod failed for method Show" );
    
        _bstr_t     showMethodDescription;
        pShowMethod->get_ToString( showMethodDescription.GetAddress() )
            >> Is< HrSuccess >()
            || throwX( "_MethodInfo::get_ToString failed" );
        std::cout << "MethodInfo: \"" << showMethodDescription.operator char const*() << "\"" << std::endl;
    
        _variant_t      okButtonIdAsVariant;
    
    
    
        //OleVector               toObjectArgsVec( 2, VariantType< VARIANT >() );
        //ElemAccess< VARIANT >   toObjectArgs( toObjectArgsVec );
    
        //toObjectArgs.setElem( 0, _variant_t( static_cast<IUnknown*>( pMessageBoxButtonsType ) ).GetVARIANT() );
        //toObjectArgs.setElem( 1, _variant_t( MB_OKCANCEL ) );
        //pEnumType->InvokeMember_3(
        //    _bstr_t( L"ToObject" ),
        //    mscorlib::BindingFlags(
        //        mscorlib::BindingFlags_Public | mscorlib::BindingFlags_Static | mscorlib::BindingFlags_InvokeMethod
        //        ),
        //    0,      // Binder
        //    _variant_t(), //targetObject.GetVARIANT(),
        //    toObjectArgsVec.asSafeArray(),
        //    &okButtonIdAsVariant
        //    )
        //    >> Is< HrSuccess >()
        //    || throwX( Text() << "_Type::InvokeMember failed, member = ToObject, code = " << GetLastError() );
    
        //ObjectPtr   pOkButtonId( okButtonIdAsVariant );   // The variant is just I4 with the id value.
    
        _variant_t              targetObject( static_cast< IUnknown* >( pMessageBoxType ) );
        _variant_t              result;
        OleVector               argsVec( nArgs, VariantType< VARIANT >() );
        ElemAccess< VARIANT >   args( argsVec );
    
        args.setElem( 0, _variant_t( L"Hello, world!" ).GetVARIANT() );
        args.setElem( 1, _variant_t( L"C++ app using .NET:" ).GetVARIANT() );
        if( nArgs > 2 ) { args.setElem( 2, _variant_t( MB_OK ) ); }
        if( nArgs > 3 ) { args.setElem( 3, _variant_t( MB_ICONINFORMATION ) ); }
        std::cout << argsVec.count() << " args." << std::endl;
    
        //pMessageBoxType->InvokeMember_3(
        //    _bstr_t( L"Show" ),
        //    mscorlib::BindingFlags(
        //        mscorlib::BindingFlags_Public | mscorlib::BindingFlags_Static | mscorlib::BindingFlags_InvokeMethod
        //        ),
        //    0,      // Binder
        //    _variant_t(), //targetObject.GetVARIANT(),
        //    argsVec.asSafeArray(),
        //    &result
        //    )
        //    >> Is< HrSuccess >()
        //    || throwX( Text() << "_Type::InvokeMember failed, code = " << GetLastError() );
    
        pShowMethod->Invoke_3( targetObject.GetVARIANT(), argsVec.asSafeArray(), &result )
            >> Is< HrSuccess >()
            || throwX( Text() << "MethodInfo::Invoke failed for MessageBox::Show, code = " << GetLastError() );
    
        std::cout << "Result type " << result.vt << std::endl;
        std::cout << "Finished!" << std::endl;
    }
    
    int main()
    {
        try
        {
            cppMain();
            return EXIT_SUCCESS;
        }
        catch( std::exception const& x )
        {
            std::cerr << "!" << x.what() << std::endl;
        }
        return EXIT_FAILURE;
    }
    
    1 回复  |  直到 15 年前
        1
  •  3
  •   Gabe Timothy Khouri    15 年前

    我认为您的问题是调用了旧版本的.NET框架(v1.1已经超过7年了),我怀疑它的封送或方法绑定有点限制。我测试的版本(2.0版、4.0版)只需将任何枚举类型的int传递给 Invoke 对你的小测试来说就足够了。

    推荐文章