首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Mono:重新加载程序集/映像和域的问题

Mono:重新加载程序集/映像和域的问题
EN

Stack Overflow用户
提问于 2021-12-23 21:30:17
回答 1查看 317关注 0票数 3

我有一个问题,我有一个星期不能处理,你不会相信,我已经检查了一切可能,但它仍然不想正确工作。现在我正在为我的游戏引擎编写一个脚本系统,我需要动态地重新编译/重新加载包含我的库和脚本的.the dll文件。要做到这一点,我要:

  1. Initialize Mono. (我得到一个已为me)
  2. Open创建的应用程序域(MonoDomain*),它是从assembly.
  3. Use mono (我加载内部调用、创建C#对象、调用函数等)生成的图像。

但在某种程度上,我需要重新编译/重新加载这个程序集,因为我更改了脚本代码,这是可以理解的。正如我从大量阅读文档和各种来源中了解到的,在程序集映像处于活动状态时,不必描述它,为此,您首先需要:

  1. Create a domain.
  2. Upload image.
  3. Connect图像到对象程序集(从而使图像状态OK)
  4. And只描述来自最后domain.

的资源)

但是,为了不尝试,或者在mono代码中发生错误,或者现在我已经到了没有错误的阶段,但是这里有一个指向从新加载的程序集(同名)获得的新的映像的指针,结果是mono继续使用相同的图像,并且没有更新的脚本值。

这是我的Mono init代码:

代码语言:javascript
运行
复制
    mono_set_dirs(".", ".");

    // 0. Load first domain, init mono + appdomain + load assembly.
    const char* domainName   = "ForceCS";
    const char* assemblyName = "ForceCS.dll";
    pMonoDomain = mono_jit_init(domainName);
    if (pMonoDomain) {
        pMonoAssembly = mono_domain_assembly_open(pMonoDomain, assemblyName);
        if (pMonoAssembly) {
            pMonoImage = mono_assembly_get_image(pMonoAssembly);
            if (pMonoImage) {
                AddInternalFunctionsAndScriptClasses(); // mono_add_internal_call ...
            }
        }
    }

然后,我用我的dll做一些事情,如创建对象或调用方法,正如我前面所说的。然后尝试重新加载ForceCS.dll。

代码语言:javascript
运行
复制
    // 1. Load new domain (Works fine).
    char* newDomainName = "ForceCS";
    MonoDomain* newDomain = mono_domain_create_appdomain(newDomainName, NULL);
    if (!mono_domain_set(newDomain, false)) {
         // Cannot set domain!
    }

    // 2. Load image. (Works fine)
    const char* dllFile = "C:\\Force\\Editor\\ForceCS.dll";
    MonoImageOpenStatus status;
    MonoImage* newImage = mono_image_open(dllFile, &status); ** Problem here: Shows the same address as before and that means that Mono use same .dll.** 

    // 3. Load new assembly. (Works fine).
    MonoAssembly* assembly = mono_assembly_load_from(newImage, mono_image_get_name(newImage), &status);
    if (status != MonoImageOpenStatus::MONO_IMAGE_OK) {
        ///Cannot load assembly!
    }

    assembly = mono_assembly_open(dllFile, &status);

    newImage = mono_assembly_get_image(assembly);
    if (newImage) {
        AddInternalFunctionsAndScriptClasses();
    }

    // 4. Inload old domain.
    MonoDomain* domainToUnload = pMonoDomain == nullptr ? mono_domain_get() : pMonoDomain;
    if (domainToUnload && domainToUnload != mono_get_root_domain()) {
        mono_domain_unload(domainToUnload);

        mono_gc_collect(mono_gc_max_generation());
        mono_domain_finalize(domainToUnload, 2000);
        mono_gc_collect(mono_gc_max_generation());

        pMonoDomain = newDomain;
        pMonoImage = newImage;
        pMonoAssembly = assembly;
    }

最后一个问题是映像始终保持不变,只有在加载这个程序集时才能正常工作,比如ForceCS1.dll,但这不是我想要的。请解释一下:

  1. When I需要关闭/释放域、程序集、images.
  2. What -- assembly-image.
  3. How之间的连接重新加载了my .dll assembly.

我会感谢你的每一个回答。

EN

回答 1

Stack Overflow用户

发布于 2021-12-31 11:48:12

最后,经过又一个星期的折磨,我终于成功地解决了它。通常,正如我所理解的,问题是我正在卸载我唯一的主域,但是我没有为mono创建/设置到另一个域,这样他就可以在程序集重新加载时在另一个线程上访问它。

总的来说,我完全重写了代码,我得到了如下内容:

代码语言:javascript
运行
复制
int main() {

    // Init Mono Runtime. (Create Root Domain)
    mono_init();
    if (mono_create_domain_and_open_assembly("Scripting Domain", "MyScript.dll"))
        mono_invoke("Script", "OnCreate", 0, NULL, NULL);

    while (true) {
        bool is_reload_request;
        std::cin >> is_reload_request;

        if (is_reload_request) {
            mono_destroy_domain_with_assisisated_assembly(domain);

            // Update new compiled assembly.
            mono_update_assembly("C:\\MonoProject\\MyScript.dll", "C:\\MonoProjectC#\\Binary\\Debug-windows-x86_64\\MyScript.dll");

            if (mono_create_domain_and_open_assembly("Scripting Domain", "MyScript.dll"))
                mono_invoke("Script", "OnCreate", 0, NULL, NULL);
        }
    }

    mono_jit_cleanup(root_domain);
    return 0;
}

现在我想详细解释一下我做了些什么,因为那些可能也遇到过这样的问题,不知道如何解决和解决这个问题的人。

Init &创建根域。

我做的第一件事是像以前一样初始化mono运行时,从而创建一个根域,以便在重新加载时我们可以切换到它。

代码语言:javascript
运行
复制
void mono_init() {
    mono_set_dirs(".", ".");
    root_domain = mono_jit_init_version("Root Domain", "v4.0.30319");
}

创建脚本域,加载程序集。

然后,我在我们的例子中创建了一个新域,这就是Scripting Domain,在创建它之后,我将它设置为mono (mono_domain_set(domain, 0)),以便从它执行所有后续的操作。然后,和以前一样,我通过域打开程序集MyScript.dll,并获得指向程序集和图像的指针。

代码语言:javascript
运行
复制
bool mono_create_domain_and_open_assembly(char* domain_name, const char* assembly_file) {
    domain = mono_domain_create_appdomain(domain_name, NULL);
    if (domain) {
        mono_domain_set(domain, 0);

        // Open assembly.
        assembly = mono_domain_assembly_open(mono_domain_get(), assembly_file);
        if (assembly) {
            image = mono_assembly_get_image(assembly);
            if (image)
                return true;
        }
    }
    return false;
}

在这个阶段之后,我检查了我的程序集是否正常工作,只是创建了一个Script对象并从它调用了OnCreate方法。我不会写这段代码,所以我希望那些已经和mono一起工作过的人知道怎么做。

再装

现在重新装填。实际上,这里的一切都比看起来简单得多,在调用mono_domain_upload之前,当我们创建新域(新的dll)时,需要调用mono_domain_setScripting Domain切换到Root Domain

代码语言:javascript
运行
复制
bool mono_destory_domain_with_assisisated_assembly(MonoDomain* domain_to_destroy) {
    if (domain_to_destroy != root_domain) {
        mono_domain_set(root_domain, 0);
        mono_domain_unload(domain_to_destroy);
        return true;
    }
    return false;
}

然后编译我们的C#项目并将其移动到我们的项目中(或者您指定了mono的路径)。或者我是如何通过C++做到的:

代码语言:javascript
运行
复制
void mono_update_assembly(char* assembly_path, char* assembly_path_from) {
    if (std::filesystem::exists(assembly_path_from)) {
        
        if (std::filesystem::exists(assembly_path))
            std::filesystem::remove(assembly_path);

        std::filesystem::copy(assembly_path_from, assembly_path);
    }
}

最后一步,在完成所有这些工作之后,我们只创建新域并加载新程序集。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70467578

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档