我有点坚持不能从该过程中处理.NET 3.5 dll。AppDomain支持统一关闭,基本上有点,脚,并且无法使用.NET api从进程中卸载dll。
我当时正在考虑尝试免费图书馆,但我认为它不符合我的需要。https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-freelibrary
任何人都可以向我提示如何/从何处开始以某种方式从内存/进程中删除该dll,以便我可以在任何需要的时候重新加载该dll?
好吧,在这段时间之后,我认为这是一项繁重的研究,没有与此相关的出版物。这就是您要做的。首先转到https://github.com/mono/mono/branches/all并确定您需要哪个单声道版本。就我而言,我是为一款旧游戏而设计的,我需要2014版的mono。
我的项目是已停产,所以我没有任何秘密要保密。下面的示例将向您展示一种方法,但可能不足以使您在较新的Mono版本上获得所需的功能。
使用这种方式,您可以卸载新AppDomain中的每个dll。您可以扩展它以创建多个appdomain,或仅卸载特定的dll。如果是,请告诉我!归功于我,还有4g3v。
一旦完成,您将需要完全相同的环境以及该版本的编译器。就我而言,那是Visual Studio 2010编译器,我不需要重构大多数东西。
您不仅需要遵循我的指示,还需要玩单声道游戏,并了解项目的工作原理。
因此,如上所述:C#API不支持AppDomain,但是默认情况下单声道支持。您只需要进行一些改进和扩展即可。这是您需要做的:
在mono.def]中定义两个新功能>mono_rb_create_domain和mono_rb_unload_domain
以上两个功能将负责创建和配置域。
转到:mono / metadata / object.c
查找函数mono_runtime_unhandled_exception_policy_set并添加(我们将在下面创建函数):mono_add_internal_call("YourDLLNameSpace.Icalls::mono_rb_load_plugin", ves_icall_mono_rb_load_plugin); //Last mono function unity calls before adding their own icalls (mono_runtime_unhandled_exception_policy_set). Adding them at runtime doesn't work, so this should be a pretty good place.
上面的代码将定义一个C#函数,该函数将能够处理在我们自己的AppDomain中加载的自定义DLL的加载。确保您的C#类和函数是公共的。提醒:这应该已经在您的统一项目中的某个位置。 (例如Assembly-CSharp或其他)。 这很关键,因为这将处理新dll的加载,并且它们将进入新的appdomain。
并使它类似于以下内容。这可能在较新的Mono版本中有所不同。 =)我们在这里所做的基本上是确保接收到的对象具有VTable(应该是类),并且它不为null。[MethodImpl(MethodImplOptions.InternalCall)] public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);
好吧,我们必须添加一些其他检查以避免在unity / unity_liveness.c]中崩溃。>查找函数mono_add_process_object
static void mono_add_process_object (MonoObject* object, LivenessState* state) { gboolean has_references = 0; MonoClass* klass; // Define the class if (object && !IS_MARKED(object)) { klass = GET_VTABLE(object)->klass; // Get the VTable // Ensure that the class isn't f***ed up. Read: https://en.wikipedia.org/wiki/Hexspeak if(klass == NULL || klass == 0xBAADF00D || klass == 0xFEEEFEEE) { return; } has_references = klass->has_references; if(has_references || should_process_value(object,state) != LIVENESS_DONT_PROCESS) { if (array_is_full(state->all_objects)) array_safe_grow(state, state->all_objects); array_push_back(state->all_objects, object); MARK_OBJ(object); } // Check if klass has further references - if not skip adding if (has_references) { if(array_is_full(state->process_array)) array_safe_grow(state, state->process_array); array_push_back(state->process_array, object); } } }
上面的代码将确保已处理的类没有错误,或指向其他应有的类。
让我们创建我们的域处理程序。确保在mono项目下创建此文件。
我的头文件被命名为mono_rustbuster.h
,并且包含:static MonoDomain* rustBusterDomain; GHashTable* pluginHashTable; void mono_method_info_object(); MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen) MONO_INTERNAL;
然后我们创建了mono_rustbuster.c
,并编写了以下内容:
此方法适用于程序集的引用,在调用引用变量的位置查找并添加:#include "metadata\metadata-internals.h" #include "metadata\image.h" #include "metadata\assembly.h" #include "metadata\debug-helpers.h" #include "metadata\class-internals.h" #include "metadata\object-internals.h" static MonoDomain* rustBusterDomain; GHashTable* pluginHashTable; void mono_method_info_object() { } int mono_rb_create_domain() { pluginHashTable = g_hash_table_new(g_str_hash, g_str_equal); rustBusterDomain = mono_domain_create_appdomain("PluginDomain", NULL); return 0x01; } int mono_rb_unload_domain() { mono_domain_unload(rustBusterDomain); return 0x01; } MonoReflectionAssembly* ves_icall_mono_rb_load_plugin(void* objectPtr, char* data, guint32 dataLen) { MonoAssembly* ass; MonoImage* img; MonoImageOpenStatus status; MonoDomain* current; char *assNameBuf; current = mono_domain_get(); mono_domain_set(rustBusterDomain, FALSE); img = mono_image_open_from_data_full(data, dataLen, TRUE, NULL, FALSE); ass = mono_assembly_load_from_full(img, "", &status, FALSE); assNameBuf = (char*)malloc(256); sprintf(assNameBuf, "%s", ass->aname.name); g_hash_table_insert(pluginHashTable, (gpointer)assNameBuf, (gpointer)ass); mono_domain_set(current, FALSE); return mono_assembly_get_object(rustBusterDomain, ass); }
此设置后,您加载的DLL可能会抱怨缺少参考。当您使用这些函数自己全部加载一个新的DLL时,通常会发生这种情况。为此,我们在mono / metadata / assembly.c
中添加了一些图层。查找mono_assembly_load_reference
并找到:mono_class_is_assignable_fromif(reference == NULL && strstr(image->name, "data-")) { reference = (MonoAssembly*)g_hash_table_lookup(pluginHashTable, aname.name); }
提示:将Mono的数据添加到所有dll中,这样我们就可以使用该内存指针来查找引用。基本上修复了未解决的引用。
转到mono / metadata / class.c
Before函数将使用最后一个函数检查是否返回了类名相等的数据。最后的返回看起来像这样:
return mono_class_has_parent (oklass, klass);
添加:
if(!mono_class_has_parent (oklass, klass)) { if(strstr(klass->image->name, "data-")) { if(!strcmp((oklass)->supertypes [(klass)->idepth - 1]->name, klass->name)) { //OutputDebugStringA("mono_class_is_assignable_from(): Class names are equal so true is returned"); return TRUE; } } }
上面的代码将为ScriptableObject强制转换异常添加修复程序。
您已经完成了。您可能会遇到其他问题。这是它在C#中的工作方式:
[DllImport("mono.dll")] internal static extern int mono_rb_create_domain(); [DllImport("mono.dll")] internal static extern int mono_rb_unload_domain();
处理新DLL的加载:
var icalls = new Icalls(); int domaincreation = RustBuster.mono_rb_create_domain(); // Last domain clear is either wrong, or somebody created a new domain. if (domaincreation == 0) { // You should terminate your process, or fix up. return; } byte[] bytes = getyourdllsbytearraysomehow; IntPtr pluginMem = Marshal.AllocHGlobal(bytes.Length); for (int i = 0; i < bytes.Length; i++) { Marshal.WriteByte(pluginMem, i, bytes[i]); } assembly = icalls.mono_rb_load_plugin(pluginMem, (uint) bytes.Length);
卸载:
int domaincheck2 = RustBuster.mono_rb_unload_domain(); // Last domain clear is either wrong, or somebody created a new domain. if (domaincheck2 == 0) { return false; }