WPF嵌入外部exe应用程序
- 使用场景
- 功能实现
- 嵌入基本功能实现
- 1.导入windows API
- 2.运行外部程序
- 3. 获取窗体句柄
- 4. 嵌入窗体
- 5.设置子窗体位置
- 整个代码
- 嵌入存在的问题:
使用场景
在WPF桌面应用程序开发过程中,有时候需要将其他程序结合到一起,让他看起来是一个程序,就需要把其他程序的窗口,作为子窗体,嵌入到程序中去。如果都是自己程序,可以将其他程序的项目直接导入引用。
在以下几种情况,可能无法直接修改和调用源程序。
- 其他人员开发,无法获取源代码,无权对源码进行修改
- exe并非使用C#相关框架(WPF/Winform)开发,比如用unity开发的程序
这种时候就只能通过直接将打包的exe程序嵌入到当前程序中去。
功能实现
嵌入基本功能实现
1.导入windows API
需要调用Windows API的SetParent
和MoveWindow
,通过DllImport将API加载进来
SetParent
通过句柄将一个窗体设为另一个窗体的父窗体。
MoveWindow
改变指定窗口的位置和大小.对基窗口来说,位置和大小取决于屏幕的左上角;对子窗口来说,位置和大小取决于父窗口客户区的左上角.对于Owned窗口,位置和大小取决于屏幕左上角.
[DllImport("user32.dll", SetLastError = true)]
public static extern long SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
2.运行外部程序
使用Process
运行外部程序(以画图程序为示例),需要将外部程序设置为正常的窗体样式,最大化状态的窗体无法嵌入。
var exeName = "C:\\WINDOWS\\system32\\mspaint";
//使用Process运行程序
Process p = new Process();
p.StartInfo.FileName = exeName;
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p.Start();
3. 获取窗体句柄
循环判断运行的外部程序窗体句柄,如果不为零就说明程序已经运行,获取他的句柄。
WPF中控件无法获取句柄,只能获取窗体的句柄,使用WindowInteropHelper
获取WPF的窗体句柄
while (p.MainWindowHandle.ToInt32() == 0)
{
System.Threading.Thread.Sleep(100);
}
IntPtr appWin = p.MainWindowHandle;
IntPtr hwnd = new WindowInteropHelper(this).Handle;
4. 嵌入窗体
使用 SetParent
将外部程序窗体嵌入当前窗体
SetParent(appWin, hwnd);
效果如下:
窗体已经嵌入称为当前窗体的子窗体,可以跟随移动,子窗体也无法移出父窗体位置。
5.设置子窗体位置
嵌入窗体之后,子窗体位置可以随机放置,我们可以通过MoveWindow
来设置子窗体的位置和大小
MoveWindow(appWin, 0, 0, 500, 400, true);
效果:
整个代码
完整实现的代码:
var exeName = "C:\\WINDOWS\\system32\\mspaint";//嵌入程序路径,可以改成其他程序
//使用Process运行程序
Process p = new Process();
p.StartInfo.FileName = exeName;
p.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
p.Start();
//获取窗体句柄
while (p.MainWindowHandle.ToInt32() == 0)
{
System.Threading.Thread.Sleep(100);
}
IntPtr appWin = p.MainWindowHandle;//子窗体(外部程序)句柄
IntPtr hwnd = new WindowInteropHelper(this).Handle;//当前窗体(主程序)句柄
//设置父窗体(实现窗体嵌入)
SetParent(appWin, hwnd);
//设置窗体位置和大小
MoveWindow(appWin, 0, 0, 500, 400, true);
嵌入存在的问题:
- WPF控件无法获取句柄,只能直接在窗体下操作嵌入
- 嵌入窗体大小无法实时跟随变化
- 嵌入窗体保留了窗体样式和边框,无法现成一个整体
- 嵌入窗体永远附在最顶层,会遮挡其他控件
后续更新:
- 通过
WindowsFormsHost
在WPF中调用winform的控件来解决控件没有句柄问题,进行封装控件,解决问题1和2; - 问题3需要使用其他Windows API来解决;
- 问题4暂时没有解决办法,只能避免。