ICLRRuntimeHost::UnloadAppDomain失败:原生进程加载托管C++/CLI DLL后无法卸载CLR
解决CLR卸载失败(错误0x80131015)的问题
首先直接点出核心问题:你碰到的0x80131015错误,本质是尝试卸载CLR的默认应用域——而默认应用域是CLR的根域,从设计上就不支持卸载操作,这是硬限制。
为什么会出错?
看你的FinitCLR代码,你通过ExecuteInDefaultAppDomain获取的current_appdomain_id是默认应用域的ID,然后调用UnloadAppDomain去卸载它。但CLR明确规定:
- 默认应用域是进程启动CLR时自动创建的,承载了CLR的核心服务,永远不能被卸载
- 只有你手动创建的**自定义应用域(Custom AppDomain)**才支持卸载操作
所以这个错误完全符合预期,因为你在做一件CLR不允许的事。
怎么修复?
要实现可卸载的托管代码加载,你需要改成用自定义应用域来承载你的托管DLL,而不是默认域。下面是具体的修改方案:
1. 先修改托管代码,添加自定义应用域的管理逻辑
在你的Managed.Helpers类里,增加创建、获取、卸载自定义应用域的方法,同时把业务逻辑放到自定义域里执行:
using System; using System.IO; using System.Reflection; namespace Managed { public static class Helpers { private static AppDomain _customAppDomain; // 创建自定义应用域并执行指定方法 public static int CreateCustomDomainAndRun(string args) { // 解析参数:格式为 "DllPath,TypeName,MethodName,MethodArgs" var parts = args.Split(','); if (parts.Length != 4) return -1; var dllPath = parts[0]; var typeName = parts[1]; var methodName = parts[2]; var methodArgs = parts[3]; // 配置应用域基础路径(确保能找到依赖DLL) var setup = new AppDomainSetup { ApplicationBase = Path.GetDirectoryName(dllPath) }; // 创建自定义应用域 _customAppDomain = AppDomain.CreateDomain( "MyManagedAppDomain", null, setup ); // 在自定义域中加载类型并调用方法 var handle = _customAppDomain.CreateInstanceFrom(dllPath, typeName); var instance = handle.Unwrap(); var method = instance.GetType().GetMethod(methodName); return (int)method.Invoke(instance, new object[] { methodArgs }); } // 获取自定义应用域ID(可选,其实托管侧卸载更直接) public static int GetCustomAppDomainId() { return _customAppDomain?.Id ?? -1; } // 卸载自定义应用域 public static void UnloadCustomDomain() { if (_customAppDomain == null) return; AppDomain.Unload(_customAppDomain); _customAppDomain = null; } // 原来的Say方法保留,作为业务逻辑入口 public static int Say(string message) { // 你的原有业务逻辑,比如输出日志或执行操作 return 0; } } }
2. 更新原生引导DLL的代码
修改InitCLR和FinitCLR,不再直接用默认域执行业务逻辑,而是调用托管代码创建自定义域:
#include <windows.h> #include <metahost.h> #include <string> #pragma comment(lib, "mscoree.lib") ICLRRuntimeHost* pCLR = nullptr; const std::wstring managed_DLL_PATH = L"YourManagedDllFullPath.dll"; auto StartCLR(std::wstring clr_version) -> ICLRRuntimeHost* { ICLRMetaHost* meta_host = nullptr; ICLRRuntimeInfo* runtime_info = nullptr; ICLRRuntimeHost* runtime_host = nullptr; if (CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, reinterpret_cast<LPVOID*>(&meta_host)) == S_OK) { if (meta_host->GetRuntime(clr_version.c_str(), IID_PPV_ARGS(&runtime_info)) == S_OK) { BOOL loadable = false; if (runtime_info->IsLoadable(&loadable) == S_OK && loadable) { if (runtime_info->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&runtime_host)) == S_OK) { // 释放临时资源,返回runtime_host meta_host->Release(); runtime_info->Release(); return runtime_host; } } } } // 清理资源 if (meta_host) meta_host->Release(); if (runtime_info) runtime_info->Release(); if (runtime_host) runtime_host->Release(); return nullptr; } extern "C" __declspec(dllexport) int InitCLR() { if (pCLR) { MessageBoxA(NULL, "CLR already initialized", "Info", MB_OK); return 1; } pCLR = StartCLR(L"v4.0.30319"); if (!pCLR) { MessageBoxA(NULL, "StartCLR failed", "Error", MB_OK | MB_ICONERROR); return 1; } if (pCLR->Start() != S_OK) { MessageBoxA(NULL, "Failed to start CLR", "Error", MB_OK | MB_ICONERROR); pCLR->Release(); pCLR = nullptr; return 1; } DWORD result = 0; // 调用托管代码创建自定义域并执行Say方法 std::wstring args = managed_DLL_PATH + L",Managed.Helpers,Say,Hello"; auto err = pCLR->ExecuteInDefaultAppDomain( managed_DLL_PATH.c_str(), L"Managed.Helpers", L"CreateCustomDomainAndRun", args.c_str(), &result ); if (err != S_OK) { MessageBoxA(NULL, "Failed to run in custom app domain", "Error", MB_OK | MB_ICONERROR); pCLR->Stop(); pCLR->Release(); pCLR = nullptr; return 1; } return 0; } extern "C" __declspec(dllexport) int FinitCLR() { if (!pCLR) { MessageBoxA(NULL, "CLR is not initialized", "Info", MB_OK); return 1; } DWORD result = 0; // 调用托管代码卸载自定义应用域 auto err = pCLR->ExecuteInDefaultAppDomain( managed_DLL_PATH.c_str(), L"Managed.Helpers", L"UnloadCustomDomain", L"", &result ); if (err != S_OK) { MessageBoxA(NULL, "Failed to unload custom app domain", "Error", MB_OK | MB_ICONERROR); return 1; } // 停止CLR并释放资源 pCLR->Stop(); pCLR->Release(); pCLR = nullptr; MessageBoxA(NULL, "CLR unloaded successfully", "Info", MB_OK); return 0; }
3. 关键注意事项
- 永远不要尝试卸载默认应用域,这是CLR的设计决定,没有任何绕过的方法
- 自定义应用域卸载时,要确保没有跨域的强引用(比如把自定义域中的对象引用到默认域),否则会导致卸载失败
- 如果你的托管DLL有依赖,一定要设置
AppDomainSetup.ApplicationBase为DLL所在目录,否则CLR可能找不到依赖
补充说明
错误码0x80131015对应的是.NET中的AppDomainUnloadedException,当你尝试访问已卸载的应用域,或者尝试卸载不允许卸载的域时就会抛出。结合你的代码场景,后者就是直接原因。
内容的提问来源于stack exchange,提问作者Asaad Mohammed
相关产品推荐
相关产品推荐

