使用场景
当前项目编辑器中不方便存放或者提交扩展代码 相同的扩展功能需要在多个项目(编辑器)中使用 项目开发中,偶尔临时需要使用一个功能,想随时使用随时卸载
设计思路
使用进程注入,将一个c/c++ dll
注入到当前运行的unity编辑器中 使用c/c++ dll
调用mono
的函数接口,比如mono_get_root_domain
去获取unity的domain
动态去加载想要加载的外部的扩展c# dll
在扩展c# dll
中调用 EditorUtility.RequestScriptReload();
来触发unity编辑器的重新编译,重载编辑器中的domain
实现卸载外部c# dll
的功能 在扩展c# dll
中绑定EditorApplication.update
事件,用来处理主线程的操作,比如AssetDatabase.Refresh();
使用jsonrpc
协议,用来调用c# dll
中的部分封装功能函数,可以实现在unity编辑器直接展示扩展窗口,或者将数据传至其他编辑器进行展示
初步实现
进程注入c/c++ dll
bool DllInject :: nsertDllToProcessByPid ( DWORD Pid, const char * pDllName)
{ DWORD dwIDExplorer = Pid; if ( dwIDExplorer == 0 ) { MEMBOX ( "Get Pro ID Error!\n" ) ; return false ; } HANDLE hProcess = OpenProcess ( 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 ; } HMODULE h = GetModuleHandle ( "MessageHis.dat" ) ; if ( h != NULL ) FreeLibrary ( h) ; PROC AdrMyDllDir = GetProcAddress ( GetModuleHandle ( "Kernel32" ) , "LoadLibraryA" ) ; HANDLE hThread = CreateRemoteThread ( hProcess, 0 , 0 , ( LPTHREAD_START_ROUTINE) AdrMyDllDir, pDllPath, 0 , 0 ) ; if ( ! hThread) { MEMBOX ( "Remote thread faile." ) ; return false ; } WaitForSingleObject ( hThread, INFINITE) ; CloseHandle ( hThread) ; VirtualFreeEx ( hProcess, pDllPath, strlen ( pDllName) + 1 , MEM_RELEASE) ; CloseHandle ( hProcess) ; MEMBOX ( "Remote Inject Dll Success" ) ; return true ;
}
调用mono
接口加载c# dll
bool MonoInjecter :: InjectMonoAssembly ( )
{ 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 ( ) ) ; std:: string assemblyDir; 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) ; method = fnMethodFromName ( klass, PAYLOAD_MAIN, 0 ) ; if ( method == NULL ) return false ; log_trace ( "Hello %s %ld" , "fnMethodFromName" , method) ; fnRuntimeInvoke ( method, NULL , NULL , NULL ) ; log_trace ( "\nHello %s" , "run mono dll!\n\n" ) ; return true ;
}
简单实现一个编辑器工具用来查找当前已经打开的unity编辑器进程,然后进行注入 由注入的c# dll
调用的测试输出, 当前编辑器中是没有任何代码的
测试环境
操作系统系统: windows 11 64位`, (不兼容32位) unity版本: 2021.3.15f1 .NET6.0
(第三方编辑器的实现)