You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何访问进程中已激活的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 18:50:25