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

Mono默认程序集路径异常:为何需手动指定全路径及优化方案

Mono程序集加载路径问题与解决

问题描述

使用mono_domain_assembly_open加载Green Hell这款Mono游戏的程序集时,必须指定完整路径才能成功加载,但这会导致解析类、方法和字段时出现明显卡顿。此前处理其他Mono游戏时,无需指定完整路径即可正常解析,且无卡顿问题。

当前使用mono_get_root_domain获取根域,已尝试mono_set_dirs、mono_set_assemblies_path等方法,均未生效。对Mono内部机制仅了解基于其编译的应用程序层面。

完整代码

void Mono::Attach()
{
    domain = mono_get_root_domain();
    mono_thread_attach(domain);
}

void Mono::Initialize()
{
    Memory::Initialize(); // 用于解析导出函数
    API::Initialize();    // 同样用于解析导出函数

    Mono::Attach();
}

void Class::Methods()
{
    void* iter = nullptr;
    MonoMethod* method;
    while (method = mono_class_get_methods(this, &iter))
    {
        std::cout << mono_method_full_name(method, 1) << "\n";
    }
}

std::string GetPath(const char* Asm) 
{
    // 拼接程序集完整路径
    return "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Green Hell\\GH_Data\\Managed\\" + std::string(Asm) + ".dll";
}

Assembly* Assembly::Resolve(const char* Name)
{
    // 必须传入完整路径才能加载程序集
    // 不传入的话会返回nullptr并抛出程序集未找到异常
    return (Assembly*)mono_domain_assembly_open(Mono::domain, API::GetPath(Name).c_str());
}

Class* Class::Resolve(const char* Asm, const char* Namespace, const char* Klass)
{
    return (Class*)mono_class_from_name(Assembly::Resolve(Asm)->GetImage(), Namespace, Klass);
}

DWORD WINAPI MainThread(LPVOID lpReserved)
{
    Mono::Initialize();
    Class::Resolve("Assembly-CSharp", "", "Player")->Methods();

    return TRUE;
}

原因分析

  • 游戏自定义了Mono搜索路径:Green Hell可能修改了根域的默认程序集搜索路径,未将Managed目录加入搜索列表,导致Mono无法通过程序集名称自动定位文件。
  • 完整路径加载的额外开销:使用完整路径加载时,Mono会跳过内部缓存的搜索路径逻辑,直接执行磁盘文件查找与加载,重复的磁盘IO和路径解析操作累积起来就会造成卡顿。

解决办法

1. 提前设置Mono程序集搜索路径

mono_set_assemblies_path必须在获取根域之前调用,否则根域初始化后搜索路径不会更新。修改Mono::Initialize方法:

void Mono::Initialize()
{
    Memory::Initialize();
    API::Initialize();

    // 先设置程序集搜索路径
    const char* managedDir = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Green Hell\\GH_Data\\Managed\\";
    mono_set_assemblies_path(managedDir);

    Mono::Attach();
}

设置完成后,即可直接通过程序集名称调用mono_domain_assembly_open,无需拼接完整路径。

2. 缓存已加载的程序集

如果路径设置暂时无效,通过缓存避免重复加载同一程序集,减少磁盘IO开销:

// 在Assembly类中添加静态缓存容器
static std::unordered_map<std::string, Assembly*> s_assemblyCache;

Assembly* Assembly::Resolve(const char* Name)
{
    std::string asmName(Name);
    // 优先从缓存获取
    if (s_assemblyCache.find(asmName) != s_assemblyCache.end()) {
        return s_assemblyCache[asmName];
    }

    // 缓存不存在则加载并存入缓存
    std::string fullPath = API::GetPath(Name);
    Assembly* assembly = (Assembly*)mono_domain_assembly_open(Mono::domain, fullPath.c_str());
    if (assembly) {
        s_assemblyCache[asmName] = assembly;
    }
    return assembly;
}

3. 尝试获取游戏实际使用的Mono域

部分游戏会创建自定义应用域而非直接使用根域,此时切换到游戏的活跃域可能解决路径问题:

void Mono::Attach()
{
    // 替换根域为当前线程的活跃域
    domain = mono_thread_get_domain();
    // 或尝试获取默认应用域
    // domain = mono_get_default_domain();
    mono_thread_attach(domain);
}

内容的提问来源于stack exchange,提问作者Exam Expert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:53:10