First, note that this is just a training exercise for me. The goal is to create a .NET mailbox using only pure C ++ (not C ++ / CLI) and late binding as needed.
This works great with the title and text in the message box.
However, when I try to specify the buttons, I return 2147942487 "The parameter is invalid." On the .NET side, MessageBox.Showthis is as a type argument enum. I am assuming that somehow the integer provided as an argument from C ++ is not automatically converted automatically.
I tried to get an "object" of type enumeration by calling Enum.ToObject. However, what came back to the COM / C ++ side is simply VARIANTwith a type I4, i.e. a 32-bit integer.
Also, I tried to do this by Google, but all I found was about how to get numeric values for .NET enum values. I have numerical values. The problem is how to get these numeric values automatically converted to enumeration arguments in .NET?
However, I may be completely wrong in that problem.
So any help is appreciated!
Code below (hard coding path, probably need to configure ur system):
#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;
#include <Mscoree.h>
#include <comdef.h>
_COM_SMARTPTR_TYPEDEF( ICorRuntimeHost, IID_ICorRuntimeHost );
#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",
L"wks",
0,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
reinterpret_cast<void**>( &pCorRuntimeHost )
)
>> Is< HrSuccess >()
|| throwX( "CorBindToRuntimeEx failed" );
pCorRuntimeHost->Start()
>> 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;
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;
_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;
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;
}