在UnityEngine中,AppDomain支持已失效,无法卸载dll吗? [英] AppDomain support is dead in UnityEngine, any way to unload dll?

查看:139
本文介绍了在UnityEngine中,AppDomain支持已失效,无法卸载dll吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有点坚持不能从该过程中处理.NET 3.5 dll. AppDomain支持统一关闭,无法使用.NET api从进程中卸载dll,因为未实现C#函数.

I am kinda stuck on not being able to dispose .NET 3.5 dlls from the process. AppDomain support is off in unity, there is no way to unload a dll from the process using the .NET api, because the C# functions are not implemented.

任何人都可以向我提示如何/从何处开始以某种方式从内存/进程中删除dll,以便我可以随时重新加载dll?

Anyone could get me some hints on how / where should I start to remove the dll from the memory / process somehow, so I can re-load the dll whenever I want?

推荐答案

好了,之后,我认为这是一项繁重的研究,并且没有与此相关的出版物.所以这就是你要做的.首先前往 https://github.com/mono/mono/branches/all并确定您将需要哪个单声道版本.就我而言,我是为一款旧游戏而设计的,所以我需要2014版的mono.

Alright, after this time I thought that this is some sort of heavy research, and there are no publications related to this. So here is what you have to do. First head over to https://github.com/mono/mono/branches/all and determine which mono version you are going to require. In my case I was doing this for an old game, and I needed the 2014 version of mono.

我的项目已中止,所以我没有任何秘密要保密.下面的示例将向您展示一种方法,但可能不足以使您在较新的Mono版本上获得想要的东西.

My project is discontinued so there is no point for me to keep this as a secret. The following examples will show you a way, but It probably won't be enough for you to get what you want on a newer mono version.

使用这种方式,您可以卸载新AppDomain中的每个dll.您可以扩展它以创建多个应用程序域,或仅卸载特定的dll.如果它正在更新的单声道版本上工作,请告诉我!归功于我,还有4g3v.

完成后,您将需要完全相同的环境以及该版本的编译器.就我而言,那是Visual Studio 2010编译器,我不需要重构大多数东西.

Once you have done that you are going to need exactly the same environment, and compilers for that version. In my case that was the Visual Studio 2010 compilers, and I didn't need to refactor most of the things.

您不仅需要按照我的指示进行操作,还需要玩单声道游戏,并了解该项目的工作原理.

You will need more than just following my instructions, you will have to play with mono, and get to know how the project works.

因此,如上所述:C#API不支持AppDomain,但是默认情况下单声道支持.您只需要进行一些改进和扩展即可.这是您需要做的:

So hence as stated: C# API doesn't support AppDomains, but mono by default does. You just need to make some improvements, and extensions for It. Here is what you need to do:

例如,在 mono.def 中定义两个新功能 mono_rb_create_domain mono_rb_unload_domain

Define two new functions in mono.def for example mono_rb_create_domain and mono_rb_unload_domain

以上两个功能将负责创建和处置域.

The above two functions will be responsible to create, and dispose a domain.

前往: mono/metadata/object.c 找到功能 mono_runtime_unhandled_exception_policy_set 并添加(我们将在下面创建该功能):

Head over to: mono/metadata/object.c Find function mono_runtime_unhandled_exception_policy_set and add (We will create the function later below):

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的加载,并且它们将进入新的应用程序域.

The above code will define a C# function that will be able to handle the loading of a custom DLL loaded in our own AppDomain. Make sure that your C# class, and function is public. Reminder: This should be somewhere in your unity project already. (For example Assembly-CSharp or whatever). It is crucial, because this will be handling the loading of your new dlls, and they will go to a new appdomain.

[MethodImpl(MethodImplOptions.InternalCall)]
public extern Assembly mono_rb_load_plugin(IntPtr data, uint dataLen);

好的,我们必须添加一些其他检查以避免在 unity/unity_liveness.c 中崩溃.查找函数 mono_add_process_object 并使其类似于以下内容.这可能在较新的Mono版本中有所不同. =)我们在这里所做的基本上是确保接收到的对象具有VTable(应该是类),并且它不为null.

Alright, we have to add some additional checks to avoid crashes in unity/unity_liveness.c Look for function mono_add_process_object and make it look like the following. This might differ in newer mono versions. =) What we have done here is basically ensuring that the received object has a VTable (which should be the class), and It isn't 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);
        }
    }
}

上面的代码将确保处理后的类没有错误,或指向其他不应存在的类.

The above code will ensure that the processed class isn't faulty, or points somewhere else where It shouldn't.

让我们创建我们的域处理程序. 确保在mono项目下创建该项目.

Let's create our domain handlers. Make sure to create this under the mono project.

我的头文件名为 mono_rustbuster.h ,其中包含:

My header file was named as mono_rustbuster.h, and contained:

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 ,并编写了以下内容:

Then we created mono_rustbuster.c, and wrote the following:

#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 .此方法适用于程序集的引用,在调用引用变量的位置查找并添加:

After this setup your loaded DLLs might whine about missing references. This mostly happens when you load a new DLL in all by yourself using these functions. We added some layers into mono/metadata/assembly.c for this fix. Find mono_assembly_load_reference This method works with the assembly's references, find where It is calling the reference variable and add:

if(reference == NULL && strstr(image->name, "data-"))
{
    reference = (MonoAssembly*)g_hash_table_lookup(pluginHashTable, aname.name);
}

提示:Mono将数据添加到所有dll中,这样我们就可以使用该内存指针来查找引用.基本上修复了未解决的引用.

Hint: Mono add's data- to all of the dll's, so that way we can use that memory pointer to find the references. Basically fixes the unresolved referencess.

转到 mono/metadata/class.c 并找到: mono_class_is_assignable_from

Before函数将使用最后一个函数检查类名称是否相等,以返回数据. 最后的返回看起来像这样:

Before function would return data using the last function check if the class names are equal. The last return looks something like this:

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类型转换异常添加修复程序.

The above code will add fixes to ScriptableObject cast exceptions.

您已经完成了.您可能会遇到其他问题.这是它在C#中的工作方式:

You are sort of done. You may encounter additional issues. Here is how it works in 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();

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();

这篇关于在UnityEngine中,AppDomain支持已失效,无法卸载dll吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆