title: Late binding of COM->NET call with enum value argument(s)
body: <p>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.</p>
  
  <p>This works fine with just a title and text in the messagebox.</p>
  
  <p>However, when I try to specify buttons I get back 2147942487 "The parameter is incorrect". On the .NET side <code>MessageBox.Show</code> has this as an <code>enum</code> type argument. I am guessing that somehow the integer supplied as argument from C++, isn't automatically converted typewise.</p>
  
  <p>I've tried to obtain an "object" of the enum type by calling <code>Enum.ToObject</code>. However, what's returned back to the COM/C++ side of things is just a <code>VARIANT</code> with type <code>I4</code>, i.e. a 32-bit integer.</p>
  
  <p>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 <em>appears</em> to be how to get those numerical values automatically converted to enum-type arguments over in .NET?</p>
  
  <p>However, <em>I could be totally wrong about what the problem is</em>.</p>
  
  <p>So any help appreciated!</p>
  
  <p>Code below (the hardcoded path will probably need adjustment on ur system):</p>
  
  <pre><code>/*
      // 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 &lt;assert.h&gt;
  #include &lt;algorithm&gt;        // std::swap
  #include &lt;iostream&gt;
  #include &lt;stddef.h&gt;         // ptrdiff_t
  #include &lt;sstream&gt;
  #include &lt;stdexcept&gt;
  #include &lt;stdlib.h&gt;         // EXIT_SUCCESS, EXIT_FAILURE
  #include &lt;string&gt;
  using std::swap;
  
  //#undef  UNICODE
  //#define UNICODE
  //#undef  NOMINMAX
  //#define NOMINMAX
  //#undef  STRICT
  //#define STRICT
  //#include &lt;windows.h&gt;
  
  #include &lt;Mscoree.h&gt;
  #include &lt;comdef.h&gt;
  _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&amp; s ) { throw std::runtime_error( s ); }
  
  
  bool startsWith( wchar_t const prefix[], wchar_t const s[] )
  {
      while( *prefix != 0 &amp;&amp; *prefix == *s ) { ++prefix; ++s; }
      return (*prefix == 0);
  }
  
  template&lt; class Predicate &gt;
  struct Is: Predicate
  {};
  
  template&lt; class Type, class Predicate &gt;
  bool operator&gt;&gt;( Type const&amp; v, Is&lt; Predicate &gt; const&amp; 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&amp; );
      Text&amp; operator=( Text const&amp; );
  
  public:
      Text() {}
  
      template&lt; class Type &gt;
      Text&amp; operator&lt;&lt;( Type const&amp; v )
      {
          stream &lt;&lt; v;
          return *this;
      }
  
      operator std::string () const
      {
          return stream.str();
      }
  };
  
  template&lt; class Type &gt;
  struct VariantType;
  
  template&lt;&gt;
  struct VariantType&lt; IUnknown* &gt;
  {
      static VARENUM const    id  = VT_UNKNOWN;
  };
  
  template&lt;&gt;
  struct VariantType&lt; VARIANT &gt;
  {
      static VARENUM const    id  = VT_VARIANT;
  };
  
  class OleVector
  {
  private:
      typedef OleVector   ThisClass;
      SAFEARRAY*  descriptor_;
      Index       lowerBound_;
      Index       upperBound_;
  
      ThisClass( ThisClass const&amp; );
      ThisClass&amp; operator=( ThisClass const&amp; );
  
      Index lowerBound() const
      {
          if( descriptor_ == 0 ) { return 0; }
          long result;
          SafeArrayGetLBound( descriptor_, 1, &amp;result )
              &gt;&gt; Is&lt; HrSuccess &gt;()
              || throwX( "OleVector::lowerBound: SafeArrayGetLBound failed" );
          return result;
      }
  
      Index upperBound() const
      {
          if( descriptor_ == 0 ) { return 0; }
          long result;
          SafeArrayGetUBound( descriptor_, 1, &amp;result )
              &gt;&gt; Is&lt; HrSuccess &gt;()
              || 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&lt; class ElemType &gt;
      OleVector( Size size, VariantType&lt; ElemType &gt; )
          : descriptor_( ::SafeArrayCreateVector( VariantType&lt; ElemType &gt;::id, 0, size ) )
          , lowerBound_( 0 )
          , upperBound_( size - 1 )
      {
          assert( descriptor_ != 0 &amp;&amp; ::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_, &amp;result )
                  &gt;&gt; Is&lt; HrSuccess &gt;()
                  || throwX( "OleVector::elemType: SafeArrayGetVartype failed" );
          }
          return VARENUM( result );
      }
  
      void swapWith( OleVector&amp; 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_, &amp;internalIndex, pResult )
              &gt;&gt; Is&lt; HrSuccess &gt;()
              || throwX( "OleVector::getElement: SafeArrayGetElement failed" );
      }
  
      void setElement( Index i, void* pData )
      {
          long internalIndex  = i + lowerBound_;
          ::SafeArrayPutElement( descriptor_, &amp;internalIndex, pData )
              &gt;&gt; Is&lt; HrSuccess &gt;()
              || throwX( "OleVector::setElement: SafeArrayPutElement failed" );
      }
  };
  
  template&lt; class ElemType &gt;
  class ElemAccess
  {
  private:
      OleVector*  v_;
  
      template&lt; class T &gt;
      static void* safeArrayPutArg( T&amp; v ) { return &amp;v; }
  
      template&lt;&gt;
      static void* safeArrayPutArg( IUnknown*&amp; v ) { return v; }
  
  public:
      ElemAccess( OleVector&amp; v )
          : v_( &amp;v )
      {
          assert( VariantType&lt; ElemType &gt;::id == v_-&gt;elemType() );
          assert( v_-&gt;elemSize() == sizeof( ElemType ) );
      }
  
      ElemType elem( Index i ) const
      {
          ElemType    result;
          v_-&gt;getElement( i, &amp;result );
          return result;
      }
  
      void setElem( Index i, ElemType v )
      {
          v_-&gt;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&lt;void**&gt;( &amp;pCorRuntimeHost )
          )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "CorBindToRuntimeEx failed" );
  
      pCorRuntimeHost-&gt;Start()    // Without this GetDefaultDomain fails.
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "CorRuntimeHost::Start failed" );
  
      IUnknownPtr     pAppDomainIUnknown;
      pCorRuntimeHost-&gt;GetDefaultDomain( &amp;pAppDomainIUnknown )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "CorRuntimeHost::GetDefaultDomain failed" );
  
      AppDomainPtr    pAppDomain  = pAppDomainIUnknown;
      (pAppDomain != 0)
          || throwX( "Obtaining _AppDomain interface failed" );
  
      AssemblyPtr     pCoreLibAssembly;
      {
          SAFEARRAY*  rawAssembliesArray;
          pAppDomain-&gt;GetAssemblies( &amp;rawAssembliesArray )
              &gt;&gt; Is&lt; HrSuccess &gt;()
              || throwX( "_AppDomain::GetAssemblies failed" );
  
          OleVector   assemblies( rawAssembliesArray );
          Size const  n       = assemblies.count();
  
          std::cout &lt;&lt; n &lt;&lt; " assemblies loaded." &lt;&lt; std::endl;
          if( n &gt; 0 )
          {
              std::cout &lt;&lt; "Array elemtype " &lt;&lt; assemblies.elemType() &lt;&lt; "." &lt;&lt; std::endl;
  
              ElemAccess&lt; IUnknown* &gt;     elems( assemblies );
              for( Index i = 0;  i &lt; n;  ++i )
              {
                  IUnknownPtr         pUnknown( elems.elem( i ) );
                  AssemblyPtr         pAssembly( pUnknown );
  
                  _bstr_t     displayName;
                  pAssembly-&gt;get_ToString( displayName.GetAddress() )
                      &gt;&gt; Is&lt; HrSuccess &gt;()
                      || throwX( "_Assembly::get_ToString failed" );
                  std::cout
                      &lt;&lt; i + 1 &lt;&lt; ": "
                      &lt;&lt; "\"" &lt;&lt; displayName.operator char const*() &lt;&lt; "\""
                      &lt;&lt; std::endl;
                  if( startsWith( L"mscorlib,", displayName ) )
                  {
                      pCoreLibAssembly = pAssembly;
                  }
              }
          }
      }
      (pCoreLibAssembly != 0)
          || throwX( "mscorlib was not loaded by the .NET initialization" );
  
      TypePtr     pStringType;
      pCoreLibAssembly-&gt;GetType_2( _bstr_t( L"System.String" ), &amp;pStringType )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_Assembly::GetType failed (for System.String)" );
      (pStringType != 0 )
          || throwX( "System.String type not found" );
  
      TypePtr     pEnumType;
      pCoreLibAssembly-&gt;GetType_2( _bstr_t( L"System.Enum" ), &amp;pEnumType )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_Assembly::GetType failed (for System.Enum)" );
      (pEnumType != 0 )
          || throwX( "System.Enum type not found" );
  
      AssemblyPtr     pFormsAssembly;
      pAppDomain-&gt;Load_2( _bstr_t( L"System.Windows.Forms, Version=1.1.4322.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" ), &amp;pFormsAssembly )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "Loading System.Windows.Forms assembly failed" );
      std::cout &lt;&lt; "Loaded the System.Windows.Forms assembly." &lt;&lt; std::endl;
  
      _bstr_t     formsAssemblyDisplayName;
      pFormsAssembly-&gt;get_ToString( formsAssemblyDisplayName.GetAddress() )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_Assembly::get_ToString failed" );
      std::cout &lt;&lt; "\"" &lt;&lt; formsAssemblyDisplayName.operator char const*() &lt;&lt; "\"" &lt;&lt; std::endl;
  
      TypePtr     pMessageBoxButtonsType;
      pFormsAssembly-&gt;GetType_2( _bstr_t( L"System.Windows.Forms.MessageBoxButtons" ), &amp;pMessageBoxButtonsType )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_Assembly::GetType failed (for System.Windows.Forms.MessageBoxButtons)" );
      (pMessageBoxButtonsType != 0 )
          || throwX( "System.Windows.Forms.MessageBoxButtons type not found" );
  
      TypePtr     pMessageBoxIconType;
      pFormsAssembly-&gt;GetType_2( _bstr_t( L"System.Windows.Forms.MessageBoxIcon" ), &amp;pMessageBoxIconType )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_Assembly::GetType failed (for System.Windows.Forms.MessageBoxIcon)" );
      (pMessageBoxIconType != 0 )
          || throwX( "System.Windows.Forms.MessageBoxIcon type not found" );
  
      TypePtr     pMessageBoxType;
      pFormsAssembly-&gt;GetType_2( _bstr_t( L"System.Windows.Forms.MessageBox" ), &amp;pMessageBoxType )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || 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&lt; IUnknown* &gt;() );
      ElemAccess&lt; IUnknown*&gt;      argTypes( argTypesVec );
      argTypes.setElem( 0, static_cast&lt;IUnknown*&gt;( pStringType ) );
      argTypes.setElem( 1, static_cast&lt;IUnknown*&gt;( pStringType ) );
      if( nArgs &gt; 2 ) { argTypes.setElem( 2, static_cast&lt;IUnknown*&gt;( pMessageBoxButtonsType ) ); }
      if( nArgs &gt; 3 ) { argTypes.setElem( 3, static_cast&lt;IUnknown*&gt;( pMessageBoxIconType ) ); }
  
      MethodInfoPtr   pShowMethod;
      pMessageBoxType-&gt;GetMethod_5(
          _bstr_t( L"Show" ), argTypesVec.asSafeArray(), &amp;pShowMethod
          )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "MessageBox::GetMethod failed for method Show" );
  
      _bstr_t     showMethodDescription;
      pShowMethod-&gt;get_ToString( showMethodDescription.GetAddress() )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( "_MethodInfo::get_ToString failed" );
      std::cout &lt;&lt; "MethodInfo: \"" &lt;&lt; showMethodDescription.operator char const*() &lt;&lt; "\"" &lt;&lt; std::endl;
  
      _variant_t      okButtonIdAsVariant;
  
  
  
      //OleVector               toObjectArgsVec( 2, VariantType&lt; VARIANT &gt;() );
      //ElemAccess&lt; VARIANT &gt;   toObjectArgs( toObjectArgsVec );
  
      //toObjectArgs.setElem( 0, _variant_t( static_cast&lt;IUnknown*&gt;( pMessageBoxButtonsType ) ).GetVARIANT() );
      //toObjectArgs.setElem( 1, _variant_t( MB_OKCANCEL ) );
      //pEnumType-&gt;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(),
      //    &amp;okButtonIdAsVariant
      //    )
      //    &gt;&gt; Is&lt; HrSuccess &gt;()
      //    || throwX( Text() &lt;&lt; "_Type::InvokeMember failed, member = ToObject, code = " &lt;&lt; GetLastError() );
  
      //ObjectPtr   pOkButtonId( okButtonIdAsVariant );   // The variant is just I4 with the id value.
  
      _variant_t              targetObject( static_cast&lt; IUnknown* &gt;( pMessageBoxType ) );
      _variant_t              result;
      OleVector               argsVec( nArgs, VariantType&lt; VARIANT &gt;() );
      ElemAccess&lt; VARIANT &gt;   args( argsVec );
  
      args.setElem( 0, _variant_t( L"Hello, world!" ).GetVARIANT() );
      args.setElem( 1, _variant_t( L"C++ app using .NET:" ).GetVARIANT() );
      if( nArgs &gt; 2 ) { args.setElem( 2, _variant_t( MB_OK ) ); }
      if( nArgs &gt; 3 ) { args.setElem( 3, _variant_t( MB_ICONINFORMATION ) ); }
      std::cout &lt;&lt; argsVec.count() &lt;&lt; " args." &lt;&lt; std::endl;
  
      //pMessageBoxType-&gt;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(),
      //    &amp;result
      //    )
      //    &gt;&gt; Is&lt; HrSuccess &gt;()
      //    || throwX( Text() &lt;&lt; "_Type::InvokeMember failed, code = " &lt;&lt; GetLastError() );
  
      pShowMethod-&gt;Invoke_3( targetObject.GetVARIANT(), argsVec.asSafeArray(), &amp;result )
          &gt;&gt; Is&lt; HrSuccess &gt;()
          || throwX( Text() &lt;&lt; "MethodInfo::Invoke failed for MessageBox::Show, code = " &lt;&lt; GetLastError() );
  
      std::cout &lt;&lt; "Result type " &lt;&lt; result.vt &lt;&lt; std::endl;
      std::cout &lt;&lt; "Finished!" &lt;&lt; std::endl;
  }
  
  int main()
  {
      try
      {
          cppMain();
          return EXIT_SUCCESS;
      }
      catch( std::exception const&amp; x )
      {
          std::cerr &lt;&lt; "!" &lt;&lt; x.what() &lt;&lt; std::endl;
      }
      return EXIT_FAILURE;
  }
  </code></pre>