AppDomain 支持在 UnityEngine 中已死,有什么方法可以卸载 dll? [英] AppDomain support is dead in UnityEngine, any way to unload dll?

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

问题描述

我有点坚持无法从进程中处理 .NET 3.5 dll.AppDomain 支持在 unity 中关闭,无法使用 .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 版的单声道.

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.

我的项目终止,所以我没有必要对此保密.以下示例将向您展示一种方法,但对于您来说,在较新的单声道版本上获得所需的内容可能还不够.

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 不支持 AppDomains,但默认情况下单声道支持.您只需要对其进行一些改进和扩展.您需要执行以下操作:

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_domainmono_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 并使其看起来如下所示.这在较新的单声道版本中可能有所不同.=) 我们在这里所做的基本上是确保接收到的对象有一个 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.

让我们创建域处理程序.确保在单声道项目下创建它.

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 中添加了一些层.Find 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 的加载:

Handle loading of a new 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();

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

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