查看链接:https://code.msdn.microsoft.com/CppHostCLR-e6581ee0
参考链接:https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/01918c6x(v=vs.100)
简介
公共语言运行时 (CLR) 允许自身与主机之间的集成级别。此 C++ 代码示例演示使用 .NET Framework 4.0 的托管界面在进程中托管特定版本的 CLR、加载 .NET 程序集以及调用程序集中的类型。
此代码示例还演示了 .NET Framework 4 中新的进程内并行功能。.NET Framework 4 运行时和所有未来的运行时都能够在进程内与另一个并行运行。.NET Framework 4 运行时及更高版本也能够在进程内与任何单个较早的运行时并行运行。换句话说,你将能够在同一个进程中加载 4.0 和 2.0,但无法在同一个进程中加载 1.1 和 2.0。此代码示例并行托管 .NET 运行时 4.0 和 2.0,并将 .NET2.0 程序集加载到两个运行时中。
生成示例
这 3 个项目的输出路径应该相同。在本示例中,输出路径为 $(SolutionDir)\$(Configuration)\。
运行示例
1. 按 F5 调试此项目,你将会看到
第一步演示如何并行托管 .NET4.0 和 .NET2.0,并将 .NET 2.0 程序集加载到两个运行时中。它使用 .NET Framework 4.0 托管界面托管 .NET 运行时,并使用 .NET v1.x 中提供的 ICorRuntimeHost 接口加载 .NET 程序集并调用其类型。
2. 按 Enter 继续
此步骤演示使用 .NET Framework 4.0 托管界面托管 .NET 运行时,并使用 .NET v2.0 中提供的 ICLRRuntimeHost 接口加载 .NET4.0 程序集并调用其类型。由于 ICLRRuntimeHost 与 .NET 运行时 v1.x 不兼容,因此请求的运行时不能为 v1.x。
使用代码
1.将公共语言运行时加载到进程中。
主机可以通过使用以下过程之一将 CLR 加载到进程中。
a. 调用 CLRCreateInstance 函数获取 ICLRMetaHost 或 ICLRMetaHostPolicy 接口。
C++
HRESULT hr;
ICLRMetaHost *pMetaHost = NULL;
wprintf(L"Load and start the .NET runtime %s \n", pszVersion);
hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
if (FAILED(hr))
{
wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
b. 调用 ICLRMetaHost::EnumerateInstalledRuntimes、ICLRMetaHost::GetRuntime 或 ICLRMetaHostPolicy::GetRequestedRuntime 方法获取有效的 ICLRRuntimeInfo 指针。
C++
ICLRRuntimeInfo *pRuntimeInfo = NULL;
// Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
// supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
if (FAILED(hr))
{
wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Check if the specified runtime can be loaded into the process. This
// method will take into account other runtimes that may already be
// loaded into the process and set pbLoadable to TRUE if this runtime can
// be loaded in an in-process side-by-side fashion.
BOOL fLoadable;
hr = pRuntimeInfo->IsLoadable(&fLoadable);
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
if (!fLoadable)
{
wprintf(L".NET runtime %s cannot be loaded\n", pszVersion);
goto Cleanup;
}
c. 获取适用于 .NET v1.x、.NET2.0 和 .NET 4.0 的 ICorRuntimeHost。此接口在 .NET v1.x 中提供,并且与所有 .NET Frameworks 兼容。
调用 ICLRRuntimeInfo::GetInterface 方法。为 rclsid 参数指定 CLSID_CorRuntimeHost ,为 riid 参数指定 IID_ICorRuntimeHost。
C++
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
// interface that was provided in .NET v1.x, and is compatible with all
// .NET Frameworks.
hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost,
IID_PPV_ARGS(&pCorRuntimeHost));
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Start the CLR.
hr = pCorRuntimeHost->Start();
if (FAILED(hr))
{
wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
goto Cleanup;
}
d. 获取适用于 .NET2.0 和 .NET 4.0 的 ICLRRuntimeHost 。此接口在 .NET2.0 中提供,并且与所有 .NET v1.x 都不兼容。
调用 ICLRRuntimeInfo::GetInterface 方法。为 rclsid 参数指定 CLSID_CLRRuntimeHost,为 riid 参数指定 IID_ICLRRuntimeHost。
C++
// Load the CLR into the current process and return a runtime interface
// pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
// interfaces supported by CLR 4.0. Here we demo the ICLRRuntimeHost
// interface that was provided in .NET v2.0 to support CLR 2.0 new
// features. ICLRRuntimeHost does not support loading the .NET v1.x
// runtimes.
hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
IID_PPV_ARGS(&pClrRuntimeHost));
if (FAILED(hr))
{
wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Start the CLR.
hr = pClrRuntimeHost->Start();
if (FAILED(hr))
{
wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
goto Cleanup;
}
2.加载 .NET 程序集并调用静态方法。
a. 如果我们得到 ICorRuntimeHost 接口,则可以使用以下代码。
C++
IUnknownPtr spAppDomainThunk = NULL;
_AppDomainPtr spDefaultAppDomain = NULL;
// The .NET assembly to load.
bstr_t bstrAssemblyName(pszAssemblyName);
_AssemblyPtr spAssembly = NULL;
// The .NET class to instantiate.
bstr_t bstrClassName(pszClassName);
_TypePtr spType = NULL;
variant_t vtObject;
variant_t vtEmpty;
// The static method in the .NET class to invoke.
bstr_t bstrStaticMethodName(L"GetStringLength");
SAFEARRAY *psaStaticMethodArgs = NULL;
variant_t vtStringArg(L"HelloWorld");
variant_t vtLengthRet;
// The instance method in the .NET class to invoke.
bstr_t bstrMethodName(L"ToString");
SAFEARRAY *psaMethodArgs = NULL;
variant_t vtStringRet;
// Get a pointer to the default AppDomain in the CLR.
hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
if (FAILED(hr))
{
wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
if (FAILED(hr))
{
wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Load the .NET assembly.
wprintf(L"Load the assembly %s\n", pszAssemblyName);
hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
if (FAILED(hr))
{
wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Get the Type of CSSimpleObject.
hr = spAssembly->GetType_2(bstrClassName, &spType);
if (FAILED(hr))
{
wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the static method of the class:
// public static int GetStringLength(string str);
// Create a safe array to contain the arguments of the method. The safe
// array must be created with vt = VT_VARIANT because .NET reflection
// expects an array of Object - VT_VARIANT. There is only one argument,
// so cElements = 1.
psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
LONG index = 0;
hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg);
if (FAILED(hr))
{
wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Invoke the "GetStringLength" method from the Type interface.
hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke GetStringLength w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the static method.
wprintf(L"Call %s.%s(\"%s\") => %d\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrStaticMethodName),
static_cast<PCWSTR>(vtStringArg.bstrVal),
vtLengthRet.lVal);
// Instantiate the class.
hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
if (FAILED(hr))
{
wprintf(L"Assembly::CreateInstance failed w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Call the instance method of the class.
// public string ToString();
// Create a safe array to contain the arguments of the method.
psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
// Invoke the "ToString" method from the Type interface.
hr = spType->InvokeMember_3(bstrMethodName, static_cast<BindingFlags>(
BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
NULL, vtObject, psaMethodArgs, &vtStringRet);
if (FAILED(hr))
{
wprintf(L"Failed to invoke ToString w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the method.
wprintf(L"Call %s.%s() => %s\n",
static_cast<PCWSTR>(bstrClassName),
static_cast<PCWSTR>(bstrMethodName),
static_cast<PCWSTR>(vtStringRet.bstrVal));
上述 C++ 代码与此 C# 代码所实现的功能相同:
C#
Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName);
object length = type.InvokeMember("GetStringLength",
BindingFlags.InvokeMethod | BindingFlags.Static |
BindingFlags.Public, null, null, new object[] { "HelloWorld" });
object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject");
object str = type.InvokeMember("ToString",
BindingFlags.InvokeMethod | BindingFlags.Instance |
BindingFlags.Public, null, obj, new object[] { });
b. 如果我们得到 ICLRRuntimeHost 接口,则可以使用以下代码。
C++
// The invoked method of ExecuteInDefaultAppDomain must have the
// following signature: static int pwzMethodName (String pwzArgument)
// where pwzMethodName represents the name of the invoked method, and
// pwzArgument represents the string value passed as a parameter to that
// method. If the HRESULT return value of ExecuteInDefaultAppDomain is
// set to S_OK, pReturnValue is set to the integer value returned by the
// invoked method. Otherwise, pReturnValue is not set.
hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
if (FAILED(hr))
{
wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
goto Cleanup;
}
// Print the call result of the static method.
wprintf(L"Call %s.%s(\"%s\") => %d\n", pszClassName, pszStaticMethodName,
pszStringArg, dwLengthRet);
3.清理运行时。
C#
if (pMetaHost)
{
pMetaHost->Release();
pMetaHost = NULL;
}
if (pRuntimeInfo)
{
pRuntimeInfo->Release();
pRuntimeInfo = NULL;
}
if (pCorRuntimeHost)
{
// Please note that after a call to Stop, the CLR cannot be
// reinitialized into the same process. This step is usually not
// necessary. You can leave the .NET runtime loaded in your process.
//wprintf(L"Stop the .NET runtime\n");
//pCorRuntimeHost->Stop();
pCorRuntimeHost->Release();
pCorRuntimeHost = NULL;
}
更多信息