C# UnityEngine中的AppDomain支持已失效,是否有卸载dll的方法?
我有点被困在无法处理进程中的.NET3.5DLL上。 unity中的AppDomain支持已关闭,无法使用.NETAPI从进程中卸载dll,因为C#函数未实现C# UnityEngine中的AppDomain支持已失效,是否有卸载dll的方法?,c#,unity3d,mono,appdomain,C#,Unity3d,Mono,Appdomain,我有点被困在无法处理进程中的.NET3.5DLL上。 unity中的AppDomain支持已关闭,无法使用.NETAPI从进程中卸载dll,因为C#函数未实现 任何人都可以给我一些提示,告诉我应该如何/从哪里开始从内存/进程中删除dll,这样我就可以随时重新加载dll了?好吧,在这之后,我认为这是一项繁重的研究,没有相关的出版物。这就是你要做的。首先,决定你需要哪一个mono版本。在我的例子中,我是为一个老游戏做这件事的,我需要2014版的mono 我的项目已停止,因此我没有必要将此作为秘密。下
任何人都可以给我一些提示,告诉我应该如何/从哪里开始从内存/进程中删除dll,这样我就可以随时重新加载dll了?好吧,在这之后,我认为这是一项繁重的研究,没有相关的出版物。这就是你要做的。首先,决定你需要哪一个mono版本。在我的例子中,我是为一个老游戏做这件事的,我需要2014版的mono 我的项目已停止,因此我没有必要将此作为秘密。下面的示例将向您展示一种方法,但它可能不足以让您在更新的mono版本上获得所需的功能 使用这种方式,您可以卸载新AppDomain中的每个dll。您可以扩展它来创建多个AppDomain,或者只卸载特定的dll。如果它是在一个较新的单声道版本,然后请让我知道!积分归我所有,4g3v。
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);
一旦这样做了,您将需要完全相同的环境和该版本的编译器。在我的例子中,那是VisualStudio2010编译器,我不需要重构大部分东西
你将需要的不仅仅是按照我的指示,你将不得不玩单声道,并了解该项目的工作原理
因此,如前所述:C#API不支持AppDomains,但mono默认支持。您只需要对其进行一些改进和扩展。以下是您需要做的:
例如,在mono.def中定义两个新函数
mono\u rb\u创建域和
mono\u rb\u unload\u域
上述两个功能将负责创建和处置域
转到:mono/metadata/object.c
查找函数mono\u运行时\u未处理的\u异常\u策略\u集并添加(我们将在下文稍后创建函数):
上面的代码将定义一个C#函数,该函数将能够处理在我们自己的AppDomain中加载的自定义DLL的加载。确保您的C#类和函数是公共的。提醒:这应该已经在你的unity项目中了。(例如,组合件CSharp或其他)这是至关重要的,因为这将处理新DLL的加载,它们将转到新的appdomain。
[MethodImpl(MethodImplOptions.InternalCall)]
public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);
好的,我们必须添加一些额外的检查,以避免在unity/unity\u liveness.c查找函数mono\u add\u process\u object中崩溃,并使其如下所示。这在较新的mono版本中可能有所不同。=)我们在这里所做的基本上是确保接收到的对象有一个VTable(应该是类),并且它不是null
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\u assembly\u load\u reference此方法使用程序集的引用,查找调用引用变量的位置并添加:
if(reference == NULL && strstr(image->name, "data-"))
{
reference = (MonoAssembly*)g_hash_table_lookup(pluginHashTable, aname.name);
}
提示:Mono将数据添加到所有dll中,这样我们就可以使用内存指针来查找引用。基本上修复了未解决的referencess
前往mono/metadata/class.c并找到:mono\u class\u可从中分配
Before函数将使用最后一个函数返回数据,检查类名是否相等。
最后一次返回的结果如下所示:
return mono_class_has_parent (oklass, klass);
加:
上述代码将为ScriptableObject强制转换异常添加修复
你差不多完了。您可能会遇到其他问题。下面是它在C#中的工作原理:
处理新dll的加载:
var icalls = new Icalls();
int domaincreation = RustBuster.mono_rb_create_domain();
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);
我敢肯定,在.NET3.5或更低版本中,唯一的方法就是卸载appdomain。也许Unity本身有什么东西可以填补这个空白。我可能在自定义mono版本中找到了一个解决方案。@DreTaX您介意分享您的解决方案吗?我们正在研究,因此它有点复杂,您最终需要有自己的mono.dll。这是我们为我们的CIL修改版游戏所做的事情,该游戏有我们的自定义C#客户端,该客户端是私有的,但我肯定会告诉您mono部分的解决方案,这是您唯一需要的。@vexe该解决方案已添加。
[DllImport("mono.dll")]
internal static extern int mono_rb_create_domain();
[DllImport("mono.dll")]
internal static extern int mono_rb_unload_domain();
var icalls = new Icalls();
int domaincreation = RustBuster.mono_rb_create_domain();
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();