能否在Unity自定义Editor Window中嵌入运行第三方exe或自有桌面应用?
Unity EditorWindow嵌入外部exe桌面应用的可行方案
完全可以实现,主流有两种适配不同场景的实现方案:
方案1:Windows原生窗口嵌入(支持无源码的第三方exe、自有exe)
这个方案是Windows平台下成本最低的实现方式,核心原理是通过Win32 API将外部exe的主窗口设置为Unity EditorWindow的子窗口,直接复用exe本身的渲染和交互逻辑,不需要修改目标exe的源码,用来嵌入cmd这类系统工具非常合适。
核心实现步骤:
- 通过C#的
Process类启动目标exe,等待程序初始化完成后获取它的主窗口句柄 - 调用
EditorWindowUtils.GetWindowHandle获取当前自定义EditorWindow的原生窗口句柄 - 调用Win32 API
SetParent将exe窗口绑定为EditorWindow的子窗口,再去掉exe窗口的标题栏、边框等样式 - 监听EditorWindow的大小变化事件,同步调整内嵌exe窗口的尺寸填满整个EditorWindow客户区
示例核心代码如下:
using UnityEditor; using UnityEngine; using System.Diagnostics; using System.Runtime.InteropServices; public class ExeEmbedWindow : EditorWindow { // 引入需要的Win32 API [DllImport("user32.dll")] private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll")] private static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); [DllImport("user32.dll", SetLastError = true)] private static extern int SetWindowLongPtr(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32.dll")] private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); private Process _targetProcess; private IntPtr _exeWindowHandle = IntPtr.Zero; [MenuItem("Window/Embedded Exe Demo")] public static void OpenWindow() { GetWindow<ExeEmbedWindow>("内嵌exe窗口"); } private void OnEnable() { // 启动目标exe,此处以cmd.exe为例 ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe") { UseShellExecute = true }; _targetProcess = Process.Start(startInfo); // 等待程序初始化完成,部分启动慢的exe可以加个几百毫秒的延迟 _targetProcess.WaitForInputIdle(2000); _exeWindowHandle = _targetProcess.MainWindowHandle; if (_exeWindowHandle == IntPtr.Zero) { UnityEngine.Debug.LogError("获取exe窗口句柄失败"); return; } // 获取当前EditorWindow的原生句柄,Unity 2020及以上版本支持该API IntPtr unityWindowHandle = EditorWindowUtils.GetWindowHandle(this); // 绑定父子窗口关系 SetParent(_exeWindowHandle, unityWindowHandle); // 移除exe窗口的边框、标题栏等样式,仅保留可视属性 SetWindowLongPtr(_exeWindowHandle, -16 /* GWL_STYLE */, 0x10000000 /* WS_VISIBLE */); // 调整初始大小 MoveWindow(_exeWindowHandle, 0, 0, (int)position.width, (int)position.height, true); } private void OnGUI() { // 窗口大小变化时同步调整内嵌exe的尺寸 if (_exeWindowHandle != IntPtr.Zero) { MoveWindow(_exeWindowHandle, 0, 0, (int)position.width, (int)position.height, true); } } private void OnDisable() { // 窗口关闭时主动杀掉进程,避免后台残留 if (_targetProcess != null && !_targetProcess.HasExited) { _targetProcess.Kill(); _targetProcess.Dispose(); } _exeWindowHandle = IntPtr.Zero; } }
方案2:跨平台帧传输方案(仅适用于可修改源码的自有桌面应用)
如果你需要在macOS、Linux等非Windows平台也实现嵌入,或者需要对嵌入的应用做更多自定义控制,可以选择帧传输方案:
- 给你的自有桌面应用新增采集模块,实时捕获应用的渲染帧,通过本地Socket或者共享内存传输给Unity
- Unity的EditorWindow收到帧数据后转成
Texture2D,直接绘制在窗口的GUI区域 - 将EditorWindow内捕获的键盘、鼠标输入事件,反向传输给桌面应用执行对应的交互操作
这个方案的优势是完全跨平台,不受系统原生API限制,也不会出现窗口权限导致的兼容性问题,但需要你有桌面应用的源码可以做二次开发,不适用第三方无源码的exe。
常见注意事项
- 原生窗口嵌入方案仅支持Windows平台,macOS/Linux需要对应替换为Cocoa/X11的原生API实现父子窗口绑定逻辑
- 部分使用DirectX 12、Vulkan渲染的高权限exe可能出现画面闪烁、输入无响应的问题,可尝试关闭exe的硬件加速选项解决
- 内嵌exe的快捷键可能和Unity编辑器的全局快捷键冲突,可根据需求自行实现快捷键拦截过滤逻辑
- 低版本Unity没有
EditorWindowUtils.GetWindowHandleAPI,可以通过Win32的FindWindow接口按窗口标题匹配获取EditorWindow的句柄
内容的提问来源于stack exchange,提问作者geg8285
相关产品推荐
相关产品推荐

