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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:37:22