Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/unity3d/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# UnityEngine中的AppDomain支持已失效,是否有卸载dll的方法?_C#_Unity3d_Mono_Appdomain - Fatal编程技术网

C# UnityEngine中的AppDomain支持已失效,是否有卸载dll的方法?

C# UnityEngine中的AppDomain支持已失效,是否有卸载dll的方法?,c#,unity3d,mono,appdomain,C#,Unity3d,Mono,Appdomain,我有点被困在无法处理进程中的.NET3.5DLL上。 unity中的AppDomain支持已关闭,无法使用.NETAPI从进程中卸载dll,因为C#函数未实现 任何人都可以给我一些提示,告诉我应该如何/从哪里开始从内存/进程中删除dll,这样我就可以随时重新加载dll了?好吧,在这之后,我认为这是一项繁重的研究,没有相关的出版物。这就是你要做的。首先,决定你需要哪一个mono版本。在我的例子中,我是为一个老游戏做这件事的,我需要2014版的mono 我的项目已停止,因此我没有必要将此作为秘密。下

我有点被困在无法处理进程中的.NET3.5DLL上。 unity中的AppDomain支持已关闭,无法使用.NETAPI从进程中卸载dll,因为C#函数未实现


任何人都可以给我一些提示,告诉我应该如何/从哪里开始从内存/进程中删除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();