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 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;
}