私の目標は、純粋な C++ ネイティブ Windows API レベル プログラム (マネージド C++ または C++/CLI ではない) から .NET Windows フォーム メッセージ ボックスを表示することです。
つまり、学習目的で、以下のコメントに示されている C# コードを純粋な C++ で実装したいと考えています。
/*
// 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 <stdexcept>
#include <string>
#include <iostream>
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#undef UNICODE
#define UNICODE
#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::_ObjectHandlePtr ObjectHandlePtr;
typedef mscorlib::_AssemblyPtr AssemblyPtr;
bool throwX( std::string const& s ) { throw std::runtime_error( s ); }
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 );
}
};
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" );
// This fails because Load requires a fully qualified assembly name.
// I want to load the assembly given only name below + relevant .NET version.
AssemblyPtr pFormsAssembly;
pAppDomain->Load_2( _bstr_t( "System.Windows.Forms" ), &pFormsAssembly )
>> Is< HrSuccess >()
|| throwX( "Loading System.Windows.Forms assembly failed" );
// ... more code here, not yet written.
}
int main()
{
try
{
cppMain();
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
}
return EXIT_FAILURE;
}
計画は、アセンブリをロードした後、MessageBox
クラスに進み、呼び出しShow
ます。ただ、これは間違ったやり方かもしれません。したがって、アセンブリの完全修飾名を見つけずに(もちろん、その完全修飾名をハードコーディングせずに)それを行う方法を示す回答にも同様に満足しています。
このgacutil
ユーティリティは明らかに完全修飾名を見つけることができます:
C:\test> gacutil /l System.Windows.Forms Microsoft (R) .NET グローバル アセンブリ キャッシュ ユーティリティ。バージョン 4.0.30319.1 Copyright (c) Microsoft Corporation. 全著作権所有。 グローバル アセンブリ キャッシュには、次のアセンブリが含まれています。 System.Windows.Forms、バージョン = 2.0.0.0、カルチャ = ニュートラル、PublicKeyToken = b77a5c561934e089、processorArchitecture = MSIL System.Windows.Forms、バージョン = 1.0.3300.0、カルチャ = ニュートラル、PublicKeyToken = b77a5c561934e089 System.Windows.Forms、バージョン = 1.0.5000.0、カルチャ = ニュートラル、PublicKeyToken = b77a5c561934e089 System.Windows.Forms、バージョン = 4.0.0.0、カルチャ = ニュートラル、PublicKeyToken = b77a5c561934e089、processorArchitecture = MSIL アイテム数 = 4 C:\テスト> _
ただし、前述のように、何もハードコードしたくありません。C++ コードにハードコードされた .NET 情報は、上部のコメントに示されている C# ソース コードと、サポートされている最小の .NET バージョンを超えてはなりません。
ティア、