在WPF中嵌入其它程序,这里提供两种方案
一、使用WindowsFormHost
使用步骤如下
1、添加WindowsFormsIntegration和System.Windows.Forms引用
2、在界面上放置WindowsFormHost和System.Windows.Forms.Panel
1 <Grid> 2 <WindowsFormsHost> 3 <winform:Panel x:Name="panel"></winform:Panel> 4 </WindowsFormsHost> 5 </Grid>
3、运行被嵌入的程序,获取主窗口句柄,然后调用WinAPI SetParent函数设置被嵌入的程序的父级为panel
Winform控件是有句柄的,直接调用SetParent函数即可。
1 var process = System.Diagnostics.Process.Start("xxx.exe"); 2 3 SetParent(process.MainWindowHandle, this.panel.Handle);
这种方案理论可行,但我没有具体尝试。
二、手动控制被嵌入程序的位置和状态
这里我们以WPF嵌入WPF来进行演示,其它程序也可以嵌入,但是要注意:被嵌入的窗口必须是无边框且置顶的。
像一般的窗口程序都可以设置窗口类型,如果是嵌入Unity这种无法控制窗口类型的,可以调用SetWindowsLong函数去除边框,参考代码如下:
1 SetWindowLong(m_hWnd, GWL_EXSTYLE, GetWindowLong(m_hWnd, GWL_EXSTYLE) & ~(WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME));
ChildWindow.xaml
1 <Window x:Class="ChildWindow.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:ChildWindow" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800" WindowStyle="None" AllowsTransparency="True" Topmost="True"> 9 <Grid Background="LightGray"> 10 <Label Content="Child Window" HorizontalAlignment="Center" VerticalAlignment="Center"></Label> 11 </Grid> 12 </Window>
HostWindow.xaml
1 <Window x:Class="WPFHostDemoShell.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WPFHostDemoShell" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded"> 9 <Grid x:Name="Host"> 10 11 </Grid> 12 </Window>
在窗口的Loaded事件中创建其它程序进程,并嵌入 。
在此之前我们需要要入一些WinAPI函数签名。
User32.cs
1 public class User32 2 { 3 public const uint SWP_SHOWWINDOW = 0x0040; 4 public const uint WM_USER = 0x0400; 5 public const uint WM_Normal = WM_USER + 1; //正常显示消息 6 public const uint WM_Minimal = WM_USER + 2; //最小化消息 7 public const uint WM_Exit = WM_USER + 3; //退出消息 8 9 [DllImport("User32.dll")] 10 public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); 11 12 [DllImport("User32.dll")] 13 public static extern uint SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 14 }
窗口的Loaded事件中处理如下:
1 IntPtr childWindowHandle = IntPtr.Zero; 2 3 private async void Window_Loaded(object sender, RoutedEventArgs e) 4 { 5 var childExePath = Environment.CurrentDirectory + "\\ChildWindow.exe"; 6 if (System.IO.File.Exists(childExePath) == false) 7 return; 8 9 var process = System.Diagnostics.Process.Start(childExePath); 10 process.WaitForInputIdle(); 11 12 await Task.Delay(200); 13 14 var point = this.Host.PointToScreen(new Point(0, 0)); 15 16 User32.SetWindowPos(process.MainWindowHandle, IntPtr.Zero, (int)point.X, (int)point.Y, (int)this.Host.ActualWidth, (int)this.Host.ActualHeight, User32.SWP_SHOWWINDOW); 17 childWindowHandle = process.MainWindowHandle; 18 }
此时我们运行后,就可以看到窗口已经被嵌入
此时我们还需要处理一些窗口的事件,比如最大化,最小化,移动和大小改变等。
这里我们可以借助WinAPI SendMessage函数来对进程进行简单通信。
我们在ChildWindow增加Win32消息的处理
1 protected override void OnSourceInitialized(EventArgs e) 2 { 3 base.OnSourceInitialized(e); 4 5 HwndSource.FromHwnd(new WindowInteropHelper(this).Handle).AddHook(HwndSourceHook); 6 } 7 8 protected IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 9 { 10 switch(msg) 11 { 12 case User32.WM_Minimal: 13 this.WindowState = WindowState.Minimized; 14 break; 15 case User32.WM_Normal: 16 this.WindowState = WindowState.Normal; 17 break; 18 case User32.WM_Exit: 19 this.Close(); 20 break; 21 } 22 23 return IntPtr.Zero; 24 }
在父窗口中,窗口关闭时,发送消息到子窗口即可
1 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) 2 { 3 User32.SendMessage(childWindowHandle, User32.WM_Exit, IntPtr.Zero, IntPtr.Zero); 4 }
其它的操作可以参考示例代码,这里不做详细介绍。
最终运行效果如下:
示例代码