文章目录[x]
- 1:1、引用System.Windows.Forms.dll和WindowsFormsIntegration.dll
- 2:2、将Unity3d嵌入到WinForm控件中
- 3:3、效果演示
- 4:4、.NetCore
- 5:5、补充win32api
1、引用System.Windows.Forms.dll和WindowsFormsIntegration.dll
unity3D界面不能够直接嵌入到WPF控件中,但是可以嵌入到WinForm控件中,所以我们需要在WPF中使用WinForm控件作为载体。需要引用上述dll,如下图所示:
然后在Xaml文件中引入命名空间即可使用WinForm控件,例如:
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" <WindowsFormsHost Grid.Row="1"> <wf:Panel/> </WindowsFormsHost>
2、将Unity3d嵌入到WinForm控件中
public class AppContainer { private System.Windows.Forms.Panel _hostPanel; private readonly ManualResetEvent _eventDone = new ManualResetEvent(false); private Process _process = null; internal IntPtr _embededWindowHandle; public AppContainer(System.Windows.Forms.Panel panel) { this._hostPanel = panel; this._hostPanel.Resize += _hostPanel_Resize; } private void _hostPanel_Resize(object sender, EventArgs e) { SetBounds(); } public void ActivateWindow() { if (_process == null) return; if (_process.MainWindowHandle == IntPtr.Zero) return; Win32Api.SendMessage(_process.MainWindowHandle, Win32Api.WM_ACTIVATE, Win32Api.WA_ACTIVE, IntPtr.Zero); } public void SetBounds() { SetBounds(_hostPanel.Width, _hostPanel.Height); } public void SetBounds(int width, int height) { if (_process == null) return; if (_process.MainWindowHandle == IntPtr.Zero) return; if (width <= 0 || height <= 0) return; Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, width, height, true); ActivateWindow();//激活 } public bool StartAndEmbedProcess(string processPath) { if (null != _process) return true; var isStartAndEmbedSuccess = false; _eventDone.Reset(); // Start the process ProcessStartInfo info = new ProcessStartInfo(processPath); info.WindowStyle = ProcessWindowStyle.Maximized;//默认最大化,不弹出界面。 info.Arguments = $"-popupwindow";//Unity的命令行参数 _process = Process.Start(info); if (_process == null) { return false; } // Wait for process to be created and enter idle condition _process.WaitForInputIdle(); // Get the main handle var thread = new Thread(() => { while (true) { if (_process.MainWindowHandle != (IntPtr)0) { _eventDone.Set(); break; } Thread.Sleep(10); } }); thread.Start(); //嵌入进程 if (_eventDone.WaitOne(10000)) { isStartAndEmbedSuccess = EmbedApp(_process); if (!isStartAndEmbedSuccess) { CloseApp(_process); } } return isStartAndEmbedSuccess; } public bool EmbedExistProcess(Process process) { _process = process; return EmbedApp(process); } /// <summary> /// 将外进程嵌入到当前程序 /// </summary> /// <param name="process"></param> private bool EmbedApp(Process process) { //是否嵌入成功标志,用作返回值 var isEmbedSuccess = false; //外进程句柄 var processHwnd = process.MainWindowHandle; //容器句柄 var panelHwnd = _hostPanel.Handle; if (processHwnd != (IntPtr)0 && panelHwnd != (IntPtr)0) { //把本窗口句柄与目标窗口句柄关联起来 var setTime = 0; while (!isEmbedSuccess && setTime < 50) { // Put it into this form isEmbedSuccess = Win32Api.SetParent(processHwnd, panelHwnd) != 0; Thread.Sleep(10); setTime++; } // Remove border and whatnot //Win32Api.SetWindowLong(processHwnd, Win32Api.GWL_STYLE, Win32Api.WS_CHILDWINDOW | Win32Api.WS_CLIPSIBLINGS | Win32Api.WS_CLIPCHILDREN | Win32Api.WS_VISIBLE); SetBounds(); Move the window to overlay it on this window //Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true); } if (isEmbedSuccess) { _embededWindowHandle = _process.MainWindowHandle; } return isEmbedSuccess; } /// <summary> /// 关闭进程 /// </summary> /// <param name="process"></param> private void CloseApp(Process process) { if (process != null && !process.HasExited) { process.Kill(); } } public void CloseProcess() { CloseApp(_process); _process = null; } }
这是一个嵌入的实例类,构造函数会把WinForm控件以参数的形式传入,然后此控件需要订阅Resize事件,该事件处理器进行了unity3D窗体的重新激活。
在进程启动的时候使用了命令行参数info.Arguments = $"-popupwindow";//Unity的命令行参数。
依然使用的Win32Api设置为父窗体, Win32Api.SetParent(processHwnd, panelHwnd)。
<Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition /> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Button Margin="3" Content="Embed" Click="Button_Click" HorizontalAlignment="Center"/> </Grid> <WindowsFormsHost Grid.Row="1"> <wf:Panel x:Name="host"/> </WindowsFormsHost> </Grid>
private void Button_Click(object sender, RoutedEventArgs e) { AppContainer container = new AppContainer(this.host); container.StartAndEmbedProcess(@"Child.exe"); }
3、效果演示
鼠标和键盘均可正常响应。
4、.NetCore
目前很多项目使用的.NetCore,任何发现用不了上述说的dll。那么因为需要在项目文件中添加如下信息:
<PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <UseWPF>true</UseWPF> <UseWindowsForms>true</UseWindowsForms> <AssemblyName>SIFP.Core</AssemblyName> </PropertyGroup>
https://blog.csdn.net/zhudaokuan/article/details/109007012
5、补充win32api
public class Win32Api { [DllImport("User32.dll")] public static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw); internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam); [DllImport("user32.dll")] internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", SetLastError = true)] public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent); internal Process process; internal IntPtr unityHWND = IntPtr.Zero; internal const int WM_ACTIVATE = 0x0006; internal static readonly IntPtr WA_ACTIVE = new IntPtr(1); internal static readonly IntPtr WA_INACTIVE = new IntPtr(0); }