Позднее связывание вызова COM-> NET с аргументом (значениями) перечисления - PullRequest
0 голосов
/ 24 октября 2010

Во-первых, обратите внимание, что это всего лишь учебное упражнение для меня.Намерение состоит в том, чтобы создать окно сообщений .NET, используя только чистый C ++ (не C ++ / CLI) и позднее связывание при необходимости.

Это прекрасно работает только с заголовком и текстом в окне сообщения.

Однако, когда я пытаюсь указать кнопки, я получаю сообщение 2147942487 «Параметр неверен».На стороне .NET MessageBox.Show имеет аргумент типа enum.Я предполагаю, что каким-то образом целое число, предоставляемое в качестве аргумента из C ++, не преобразуется автоматически по типу.

Я попытался получить «объект» типа enum, вызвав Enum.ToObject.Однако то, что возвращается на сторону COM / C ++, это просто VARIANT с типом I4, то есть 32-разрядное целое число.

Кроме того, я пытался это от Google, но всеЯ нашел о том, как получить числовые значения для значений перечисления .NET.У меня есть числовые значения.Проблема , по-видимому, заключается в том, как автоматически преобразовать эти числовые значения в аргументы перечислимого типа в .NET?

Однако, Я могу быть совершенно не прав в том, в чем проблема.

Так что любая помощь приветствуется!

Код ниже (жестко закодированный путь, вероятно, потребует корректировки в вашей системе):

/*
    // 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 Ответ

3 голосов
/ 25 октября 2010

Я думаю, что ваша проблема в том, что вы вызываете более старую версию .Net framework (v1.1 более 7 лет), и я подозреваю, что ее сортировка или привязка метода немного ограничивают.Версии, которые я тестировал (v2.0, v4.0), позволяют мне просто передавать int для любых типов перечисления в Invoke, что должно быть достаточно для вашего маленького теста.

...