使用场景
当前项目编辑器中不方便存放或者提交扩展代码 相同的扩展功能需要在多个项目(编辑器)中使用 项目开发中,偶尔临时需要使用一个功能,想随时使用随时卸载
设计思路
使用进程注入,将一个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
(第三方编辑器的实现)