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 th