记录使用注入的方式为Unity编辑器实现扩展能力

news/2025/2/11 4:16:40/

使用场景

  1. 当前项目编辑器中不方便存放或者提交扩展代码
  2. 相同的扩展功能需要在多个项目(编辑器)中使用
  3. 项目开发中,偶尔临时需要使用一个功能,想随时使用随时卸载

设计思路

  1. 使用进程注入,将一个c/c++ dll注入到当前运行的unity编辑器中
  2. 使用c/c++ dll调用mono的函数接口,比如mono_get_root_domain去获取unity的domain动态去加载想要加载的外部的扩展c# dll
  3. 在扩展c# dll中调用 EditorUtility.RequestScriptReload();来触发unity编辑器的重新编译,重载编辑器中的domain实现卸载外部c# dll的功能
  4. 在扩展c# dll中绑定EditorApplication.update事件,用来处理主线程的操作,比如AssetDatabase.Refresh();
  5. 使用jsonrpc协议,用来调用c# dll中的部分封装功能函数,可以实现在unity编辑器直接展示扩展窗口,或者将数据传至其他编辑器进行展示

初步实现

  1. 进程注入c/c++ dll
//远程进程插入dll bypid
bool DllInject::nsertDllToProcessByPid(DWORD Pid, const char* pDllName)
{// 提升权限//ImproveProcessPri();// 获取要插入的进程IDDWORD dwIDExplorer = Pid;if (dwIDExplorer == 0){MEMBOX("Get Pro ID Error!\n");return false;}// 打开进程HANDLE hProcess = OpenProcess(/*PROCESS_ALL_ACCESS*/0x1F0FFF, FALSE, dwIDExplorer);if (hProcess == NULL){MEMBOX("Open Process Error!\n");return false;}// 分配空间void* pDllPath = VirtualAllocEx(hProcess, 0, strlen(pDllName) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (!pDllPath){MEMBOX("pRemoteThread = NULL!\n");return false;}if (!WriteProcessMemory(hProcess, pDllPath, pDllName, strlen(pDllName) + 1, 0)){MEMBOX("WriteProcessMemory Fail!\n");return false;}//看有Game.exe 是否打开   .   打开了  GetProcAddress 是不准的  /*1. 检测静态 变量是否存储2.*///卸载保护HMODULE h = GetModuleHandle("MessageHis.dat");if (h != NULL)FreeLibrary(h);PROC AdrMyDllDir = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");//(PROC)::GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")),"LoadLibraryA");// 创建远程线程HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)AdrMyDllDir, pDllPath, 0, 0);if (!hThread){MEMBOX("Remote thread faile.");//AfxMessageBox("远程线程创建失败");return false;}WaitForSingleObject(hThread, INFINITE); //等待运行完成CloseHandle(hThread);//这个CloseHandle是不会关掉远程线程的,TerminateThread能关掉VirtualFreeEx(hProcess, pDllPath, strlen(pDllName) + 1, MEM_RELEASE);CloseHandle(hProcess);MEMBOX("Remote Inject Dll Success");//AfxMessageBox("注入成功");return true;
}
  1. 调用mono接口加载c# dll
bool MonoInjecter::InjectMonoAssembly()
{//GetProcAddress/* grab the root domain */// domain = fnGetRootDomain();// fnThreadAttach(domain);log_trace("Hello %s %s", "fnGeDomainFriendlyName", fnGeDomainFriendlyName(domain));log_trace("Hello %s %s", "fnGetRootDir", fnGetRootDir());//----------------------------domain = fnGetDomainById(1);log_trace("Hello %s %ld", "fnGetRootDomain", domain);fnThreadAttach(domain);log_trace("Hello %s %s", "fnGeDomainFriendlyName", fnGeDomainFriendlyName(domain));log_trace("Hello %s %s", "fnGetRootDir", fnGetRootDir());//----------------------------/* open payload assembly */std::string assemblyDir;/* Grab our root directory*/assemblyDir.append(fnGetRootDir());assemblyDir.append(ASSEMBLY_PATH);assembly = fnAssemblyOpen(assemblyDir.c_str(), NULL);if (assembly == NULL) return false;log_trace("Hello %s %ld", "fnAssemblyOpen", assembly);image = fnAssemblyGetImage(assembly);if (image == NULL) return false;log_trace("Hello %s %ld", "fnAssemblyGetImage", image);klass = fnClassFromName(image, PAYLOAD_NAMESPACE, PAYLOAD_CLASS);if (klass == NULL) return false;log_trace("Hello %s %ld", "fnClassFromName", klass);/* grab the hack entrypoint */method = fnMethodFromName(klass, PAYLOAD_MAIN, 0);if (method == NULL) return false;log_trace("Hello %s %ld", "fnMethodFromName", method);/* call our entrypoint */fnRuntimeInvoke(method, NULL, NULL, NULL);log_trace("\nHello %s", "run mono dll!\n\n");return true;
}
  1. 简单实现一个编辑器工具用来查找当前已经打开的unity编辑器进程,然后进行注入
    在这里插入图片描述
  2. 由注入的c# dll调用的测试输出, 当前编辑器中是没有任何代码的
    在这里插入图片描述

测试环境

  1. 操作系统系统: windows 11 64位`, (不兼容32位)
  2. unity版本: 2021.3.15f1
  3. .NET6.0(第三方编辑器的实现)

http://www.ppmy.cn/news/862654.html

相关文章

什么时构造方法,构造方法特点,复制与重载

构造方法是一种特殊的方法,用于创建和初始化对象。它与类同名,没有返回类型,可以有参数,也可以没有参数。 构造方法的特点包括: 构造方法在创建对象时自动调用,无需手动调用。 构造方法可以用来初始化对象…

SSD固态硬盘怎么选?选对接口是关键

随着固态硬盘的价格不断走低,其优异的性能也更为引人关注,越来越多的用户开始考虑自家的电脑怎样进行硬盘更新。那么问题来了,固态硬盘应该怎么选呢? 在选购SSD时,除了性能上的需求需要满足以外,接口的选择…

固态硬盘跟机械硬盘的区别

固态硬盘(Solid State Disk或Solid State Drive),也称作电子硬盘或者固态电子盘,是由控制单元和固态存储单元(DRAM或FLASH芯片)组成的硬盘。固态硬盘的接口规范和定义、功能及使用方法上与普通硬盘的相同&a…

SSD时代,你的固态硬盘选哪款?

随着存储产品的迭代更新,雄踞存储市场多年的机械硬盘也迎来了实力“挑战者”。在京东输入固态硬盘,点击搜索,就有1.5万个搜索结果,不仅三星、东芝等知名品牌位列其中,专业的存储品牌朗科、金士顿等亦名列前茅。 在速度…

固态硬盘 Solid State Disk

态硬盘(Solid State Disk或Solid State Drive),也称作 电子硬盘或者固态电子盘,是由控制单元和固态存储单元(DRAM或FLASH芯片)组成的硬盘。固态硬盘的接口规范和定义、功能及使用方法上与普通硬盘的相同&a…

服务器ssd硬盘格式化,格式化没你想象的简单 格式选错了对SSD有损

清空盘里边的文件最快的方法就是格式化,我们经常会对电脑硬盘、闪存盘、移动硬盘进行格式化,而在格式化硬盘的时候会弹出文件系统的选项,分别有FAT32、NTFS、exFAT三种格式,那么FAT32、NTFS、exFAT有什么区别?硬盘格式…

解决win11中快捷键不能使用的问题(shift+F6)

1.背景 windows11在某次开机之后,idea的shiftF6快捷键不生效了,很不方便。本来想着凑合着用吧,但是越凑合越不爽!直到今天,一定得搞定这个问题。在网上找了好几种检测热键冲突的软件,在windows11上&#x…

webpack相关

在 webpack < 4 的版本中&#xff0c;通常将 vendor 作为一个单独的入口起点添加到 entry 选项中&#xff0c;以将其编译为一个单独的文件&#xff08;与 CommonsChunkPlugin 结合使用&#xff09;。而在 webpack 4 中不鼓励这样做。而是使用 optimization.splitChunks 选项…