如何访问进程中已激活的CLR而非创建新实例?二次注入问题求解
解决方案
你的问题核心在于两个关键错误:一是Start()方法的调用逻辑完全写反,二是首次注入后调用了ICLRRuntimeHost::Stop()导致CLR进入不可用状态。要实现CLR复用,需按以下步骤修正:
1. 核心错误分析
Start()判断逻辑错误:原代码中当isStarted == TRUE时调用Start(),这完全颠倒——CLR已启动时调用Start()会触发错误,正确逻辑是仅当CLR未启动时才调用Start()。- 调用
Stop()导致CLR永久不可用:Stop()会终止CLR的运行循环,虽然不会卸载CLR,但调用后该进程内的CLR将无法再次启动,后续所有尝试复用的操作都会失败。
2. 修正后的CallCSharp实现
void CallCSharp(std::wstring& cSharpDLLpath) { HRESULT hr = S_OK; ICLRMetaHost* pMetaHost = NULL; ICLRRuntimeInfo* pRuntimeInfo = NULL; ICLRRuntimeHost* pClrRuntimeHost = NULL; DWORD returnValue{}, flags{}; BOOL isLoaded{}, isLoadable{}, isStarted{}; wchar_t newestCLRversion[] = L"v4.0.30319"; // 初始化COM(多线程模型) hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) goto done; // 创建CLR元宿主实例 hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost)); if (FAILED(hr)) goto done; // 获取指定版本的CLR运行时信息 hr = pMetaHost->GetRuntime(newestCLRversion, IID_PPV_ARGS(&pRuntimeInfo)); if (FAILED(hr)) goto done; // 检查CLR是否已加载到进程 hr = pRuntimeInfo->IsLoaded(GetCurrentProcess(), &isLoaded); if (FAILED(hr)) goto done; // 检查CLR是否可加载 hr = pRuntimeInfo->IsLoadable(&isLoadable); if (FAILED(hr)) goto done; // 检查CLR是否已启动 hr = pRuntimeInfo->IsStarted(&isStarted, &flags); if (FAILED(hr)) goto done; // 获取已存在的CLR宿主实例(若CLR已加载,会直接返回现有实例) hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pClrRuntimeHost)); if (FAILED(hr)) goto done; // 仅当CLR未启动时才调用Start() if (!isStarted) { hr = pClrRuntimeHost->Start(); if (FAILED(hr)) goto done; } // 执行C#代码 hr = pClrRuntimeHost->ExecuteInDefaultAppDomain( cSharpDLLpath.c_str(), L"Main.MainClass", L"MainMethod", L"", &returnValue ); done: // 清理COM接口 SafeRelease(pClrRuntimeHost); SafeRelease(pRuntimeInfo); SafeRelease(pMetaHost); // 反初始化COM CoUninitialize(); return; }
3. 关键注意事项
- 禁止调用
Stop():Stop()无法释放CLR的核心资源,也无法卸载应用程序域,且调用后CLR永久无法重启。若需执行清理,应在C#代码中处理(比如释放资源、退出应用程序域),而非调用Stop()。 - 复用CLR实例:
ICLRRuntimeInfo::GetInterface()会自动返回进程中已存在的CLR宿主实例,无需手动保存或全局存储接口指针,只需每次注入时通过该方法获取即可。 - COM线程模型:确保使用
COINIT_MULTITHREADED初始化COM,避免线程模型冲突导致的接口调用失败。
关于ICLRRuntimeHost::Stop()的作用
Stop()的主要作用是终止CLR的托管线程调度和运行循环,释放部分临时托管资源,但它不会:
- 卸载CLR的核心DLL(如mscorwks.dll或clr.dll)
- 卸载已加载的应用程序域
- 释放CLR占用的所有内存资源
一旦调用Stop(),该进程内的CLR将进入不可用状态,任何后续尝试使用CLR的操作都会返回错误,唯一解决办法是重启目标进程。
内容的提问来源于stack exchange,提问作者hanslhansl
相关产品推荐
相关产品推荐

