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
相关产品推荐
相关产品推荐

