我有一个问题,我有一个星期不能处理,你不会相信,我已经检查了一切可能,但它仍然不想正确工作。现在我正在为我的游戏引擎编写一个脚本系统,我需要动态地重新编译/重新加载包含我的库和脚本的.the dll文件。要做到这一点,我要:
但在某种程度上,我需要重新编译/重新加载这个程序集,因为我更改了脚本代码,这是可以理解的。正如我从大量阅读文档和各种来源中了解到的,在域、程序集和映像处于活动状态时,不必描述它,为此,您首先需要:
的资源)
但是,为了不尝试,或者在mono代码中发生错误,或者现在我已经到了没有错误的阶段,但是这里有一个指向从新加载的程序集(同名)获得的新的映像的指针,结果是mono继续使用相同的图像,并且没有更新的脚本值。
这是我的Mono init代码:
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。
// 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,但这不是我想要的。请解释一下:
我会感谢你的每一个回答。
发布于 2021-12-31 11:48:12
最后,经过又一个星期的折磨,我终于成功地解决了它。通常,正如我所理解的,问题是我正在卸载我唯一的主域,但是我没有为mono创建/设置到另一个域,这样他就可以在程序集重新加载时在另一个线程上访问它。
总的来说,我完全重写了代码,我得到了如下内容:
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运行时,从而创建一个根域,以便在重新加载时我们可以切换到它。
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
,并获得指向程序集和图像的指针。
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_set
将Scripting Domain
切换到Root Domain
。
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++做到的:
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);
}
}
最后一步,在完成所有这些工作之后,我们只创建新域并加载新程序集。
https://stackoverflow.com/questions/70467578
复制相似问题