WPF嵌入外部exe应用程序-实现基本的嵌入

WPF嵌入外部exe应用程序

使用场景

在WPF桌面应用程序开发过程中,有时候需要将其他程序结合到一起,让他看起来是一个程序,就需要把其他程序的窗口,作为子窗体,嵌入到程序中去。如果都是自己程序,可以将其他程序的项目直接导入引用。

在以下几种情况,可能无法直接修改和调用源程序。

  • 其他人员开发,无法获取源代码,无权对源码进行修改
  • exe并非使用C#相关框架(WPF/Winform)开发,比如用unity开发的程序

这种时候就只能通过直接将打包的exe程序嵌入到当前程序中去。

功能实现

嵌入基本功能实现

1.导入windows API

需要调用Windows API的SetParentMoveWindow,通过DllImport将API加载进来

SetParent通过句柄将一个窗体设为另一个窗体的父窗体。

MoveWindow改变指定窗口的位置和大小.对基窗口来说,位置和大小取决于屏幕的左上角;对子窗口来说,位置和大小取决于父窗口客户区的左上角.对于Owned窗口,位置和大小取决于屏幕左上角.

csharp 复制代码
[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运行外部程序(以画图程序为示例),需要将外部程序设置为正常的窗体样式,最大化状态的窗体无法嵌入。

csharp 复制代码
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的窗体句柄

csharp 复制代码
      while (p.MainWindowHandle.ToInt32() == 0)
            {
                System.Threading.Thread.Sleep(100);
            }
            IntPtr appWin = p.MainWindowHandle;
            IntPtr hwnd = new WindowInteropHelper(this).Handle;

4. 嵌入窗体

使用 SetParent将外部程序窗体嵌入当前窗体

csharp 复制代码
 SetParent(appWin, hwnd);

效果如下:

窗体已经嵌入称为当前窗体的子窗体,可以跟随移动,子窗体也无法移出父窗体位置。

5.设置子窗体位置

嵌入窗体之后,子窗体位置可以随机放置,我们可以通过MoveWindow来设置子窗体的位置和大小

csharp 复制代码
  MoveWindow(appWin, 0, 0, 500, 400, true);

效果:

整个代码

完整实现的代码:

csharp 复制代码
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);

嵌入存在的问题:

  1. WPF控件无法获取句柄,只能直接在窗体下操作嵌入
  2. 嵌入窗体大小无法实时跟随变化
  3. 嵌入窗体保留了窗体样式和边框,无法现成一个整体
  4. 嵌入窗体永远附在最顶层,会遮挡其他控件

后续更新:

  1. 通过WindowsFormsHost在WPF中调用winform的控件来解决控件没有句柄问题,进行封装控件,解决问题1和2;
  2. 问题3需要使用其他Windows API来解决;
  3. 问题4暂时没有解决办法,只能避免。
相关推荐
九鼎科技-Leo8 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
麻花201317 小时前
C#之WPF的C1FlexGrid空间的行加载事件和列事件变更处理动态加载的枚举值
开发语言·c#·wpf
lcintj18 小时前
【WPF】Prism学习(九)
学习·wpf·prism
界面开发小八哥18 小时前
界面控件DevExpress WPF中文教程:网格视图数据布局的列和卡片字段
wpf·界面控件·devexpress·ui开发·用户界面
△曉風殘月〆18 小时前
如何在WPF中嵌入其它程序
wpf
Crazy Struggle18 小时前
功能齐全的 WPF 自定义控件资源库(收藏版)
.net·wpf·ui控件库
shepherd枸杞泡茶1 天前
WPF动画
c#·.net·wpf
lcintj1 天前
【WPF】Prism学习(十)
学习·wpf·prism
wyh要好好学习1 天前
WPF数据加载时添加进度条
ui·wpf
code_shenbing1 天前
跨平台WPF框架Avalonia教程 三
前端·microsoft·ui·c#·wpf·跨平台·界面设计