在winform中如何嵌入第三方软件窗体✨
相关win32api的学习
SetParent
[DllImport("user32.dll ", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); //将外部窗体嵌入程序
语法:
HWND SetParent(
[in] HWND hWndChild,
[in, optional] HWND hWndNewParent
);
参数:
参数名 | 类型 | 含义 |
---|---|---|
hWndChild | HWND | 子窗口的句柄 |
hWndNewParent | HWND | 新父窗口的句柄。 如果此参数为 NULL,桌面窗口将成为新的父窗口。 如果此参数 HWND_MESSAGE,则子窗口将成为 仅消息窗口。 |
相关解释:
什么是句柄
在计算机编程和操作系统中,句柄(Handle)是一个用于标识和引用对象或资源的抽象概念。它通常是一个整数值或指针,充当对特定资源的引用或访问标识符。句柄用于管理内存、设备、文件、窗口等各种资源。
句柄在操作系统中被广泛使用,特别是在图形用户界面(GUI)应用程序中。例如,在Windows操作系统中,窗口句柄(Window Handle)用于标识和操作窗口对象。每个窗口都有一个唯一的句柄,通过该句柄可以执行诸如移动、调整大小、关闭等操作。
另一个常见的例子是文件句柄(File Handle),它用于标识和操作打开的文件。通过文件句柄,程序可以读取、写入或关闭文件。
句柄的使用可以提高程序的安全性和效率。它们允许程序通过句柄而不是直接访问资源,从而隐藏底层实现细节并提供一种统一的接口。此外,句柄还可以用于实现资源的共享和保护,通过对句柄的权限管理来控制对资源的访问。
总的来说,句柄是一种重要的编程概念,用于标识和管理各种资源,从而使程序能够有效地操作系统资源,并提供安全和统一的访问接口。
FindWindow
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpszClass, string lpszWindow); //按照窗体类名或窗体标题查找窗体
作用:检索其类名和窗口名称与指定字符串匹配的顶级窗口的句柄。 此函数不搜索子窗口。 此函数不执行区分大小写的搜索。
若要从指定的子窗口开始搜索子窗口,请使用 FindWindowEx 函数。
语法:
HWND FindWindowA(
[in, optional] LPCSTR lpClassName,
[in, optional] LPCSTR lpWindowName
);
参数:
参数名 | 类型 | 含义 |
---|---|---|
lpClassName | LPCTSTR | 如果 lpClassName 指向字符串,则指定窗口类名。 |
lpWindowName | LPCTSTR | 窗口名称 (窗口标题) 。 如果此参数为 NULL,则所有窗口名称都匹配。 |
ShowWindow
作用:设置指定窗口的显示状态。
语法:
HWND FindWindowA(
[in, optional] LPCSTR lpClassName,
[in, optional] LPCSTR lpWindowName
);
参数:
参数名 | 类型 | 含义 |
---|---|---|
hWnd | HWND | 窗口的句柄。 |
nCmdShow | int | 控制窗口的显示方式。 |
nCmdShow不同值与含义:
0 | 隐藏窗口并激活另一个窗口。 |
1 | 激活并显示窗口。 如果窗口最小化、最大化或排列,系统会将其还原到其原始大小和位置。 应用程序应在首次显示窗口时指定此标志。 |
2 | 激活窗口并将其显示为最小化窗口。 |
3 | 激活窗口并显示最大化的窗口。 |
4 | 以最近的大小和位置显示窗口。 |
5 | 激活窗口并以当前大小和位置显示窗口。 |
6 | 最小化指定的窗口,并按 Z 顺序激活下一个顶级窗口。 |
7 | 将窗口显示为最小化窗口。 |
8 | 以当前大小和位置显示窗口。 |
9 | 激活并显示窗口。 如果窗口最小化、最大化或排列,系统会将其还原到其原始大小和位置。 还原最小化窗口时,应用程序应指定此标志。 |
10 | 根据启动应用程序的程序传递给 CreateProcess 函数的 STARTUPINFO 结构中指定的SW_值设置显示状态。 |
11 | 最小化窗口,即使拥有窗口的线程没有响应。 仅当最小化不同线程的窗口时,才应使用此标志。 |
创建一个静态类
为了便于进行相关的操作,创建一个静态类:
public static class WindowManager
{
public static IntPtr intPtr; //第三方应用窗口的句柄
/// <summary>
/// 调整第三方应用窗体大小
/// </summary>
public static void ResizeWindow()
{
ShowWindow(intPtr, 0); //先将窗口隐藏
ShowWindow(intPtr, 3); //再将窗口最大化,可以让第三方窗口自适应容器的大小
}
/// <summary>
/// 循环查找第三方窗体
/// </summary>
/// <returns></returns>
public static bool FindWindow(string formName)
{
for (int i = 0; i < 100; i++)
{
//按照窗口标题查找Python窗口
IntPtr vHandle = FindWindow(null, formName);
if (vHandle == IntPtr.Zero)
{
Thread.Sleep(100); //每100ms查找一次,直到找到,最多查找10s
continue;
}
else //找到返回True
{
intPtr = vHandle;
return true;
}
}
intPtr = IntPtr.Zero;
return false;
}
/// <summary>
/// 将第三方窗体嵌入到容器内
/// </summary>
/// <param name="hWndNewParent">父容器句柄</param>
/// <param name="windowName">窗体名</param>
public static void SetParent(IntPtr hWndNewParent, string windowName)
{
ShowWindow(intPtr, 0); //先将窗体隐藏,防止出现闪烁
SetParent(intPtr, hWndNewParent); //将第三方窗体嵌入父容器
Thread.Sleep(100); //略加延时
ShowWindow(intPtr, 3); //让第三方窗体在容器中最大化显示
RemoveWindowTitle(intPtr); // 去除窗体标题
}
/// <summary>
/// 去除窗体标题
/// </summary>
/// <param name="vHandle">窗口句柄</param>
public static void RemoveWindowTitle(IntPtr vHandle)
{
long style = GetWindowLong(vHandle, -16);
style &= ~0x00C00000;
SetWindowLong(vHandle, -16, style);
}
#region API 需要using System.Runtime.InteropServices;
[DllImport("user32.dll ", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); //将外部窗体嵌入程序
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpszClass, string lpszWindow); //按照窗体类名或窗体标题查找窗体
[DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
private static extern int ShowWindow(IntPtr hwnd, int nCmdShow); //设置窗体属性
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
public static extern long GetWindowLong(IntPtr hWnd, int nIndex);
#endregion
}
首先查看最下方的内容,以
[DllImport("user32.dll ", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
为例进行说明。
这段代码是在C#中使用平台调用(Platform Invoke,或P/Invoke)来调用Windows的user32.dll中的一个函数,名为SetParent。这是一种在.NET中调用本地方法(通常是C或C++编写的)的技术。
[DllImport("user32.dll ", EntryPoint = "SetParent")]
:这是一个属性,它告诉.NET运行时你要调用的DLL的名称(在这里是"user32.dll")和函数的入口点(在这里是"SetParent")。
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent)
:这是函数的声明。它告诉.NET运行时函数的签名。在这个例子中,函数名为SetParent,它接受两个IntPtr类型的参数(hWndChild和hWndNewParent),并返回一个IntPtr类型的值。
在C#中,extern
关键字用于声明一个方法,该方法在外部实现,通常是在一个DLL中。
在该静态类中定义了一个类型为IntPtr
的静态成员intPtr表示第三方应用窗口的句柄。
IntPtr类型介绍
在C#中,IntPtr是一个特殊的数据类型,用于表示指针或句柄。它的大小会根据当前操作系统的位数而变化,32位系统下为4字节,64位系统下为8字节。IntPtr主要用于在托管代码和非托管代码之间传递指针或句柄,以及处理不确定性大小的内存操作。它通常用于与操作系统API进行交互、处理内存分配和操作句柄等场景。
IntPtr类型提供了一种安全的方式来处理指针,因为它是托管代码中的数据类型,受到.NET运行时的管理和保护。通过IntPtr,可以在托管代码中安全地表示非托管资源的地址或句柄,而无需担心内存泄漏或其他不安全的操作。
使用IntPtr类型时,需要谨慎处理,并遵循.NET平台的内存管理规则,以确保代码的稳定性和安全性。通常情况下,IntPtr主要用于与非托管代码进行交互,处理平台特定的资源或操作系统API,同时尽量避免直接使用指针操作,以减少内存管理和安全性方面的问题。
这个静态类还有ResizeWindow
、FindWindow
、SetParent
、RemoveWindowTitle
方法,等后面用到了再做解释。
创建一个winform
winform的设计如下所示:
启动软件按钮点击事件处理程序:
private void button2_Click(object sender, EventArgs e)
{
Process.Start("程序路径");
}
嵌入窗体按钮点击事件处理程序:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
if (WindowManager.FindWindow("Sysplorer [演示版]"))
{
this.Invoke(new Action(() =>
{
WindowManager.SetParent(panel1.Handle, "Sysplorer [演示版]");
}));
}
else
{
MessageBox.Show("未能查找到窗体");
}
});
}
在这里就会遇到一个问题就是如何确定窗体的标题是什么?
可以使用VS中的Spy++工具。
什么是Spy++
Spy++(Spy++)是Microsoft Visual Studio套件中的一个实用工具,用于Windows平台的应用程序开发和调试。它允许开发人员查看和分析正在运行的Windows应用程序的窗口层次结构、消息流和属性。
Spy++的主要功能包括:
- 窗口层次结构:Spy++可以显示当前系统上所有可见和隐藏的窗口,并以层次结构的形式展示它们之间的父子关系。这使得开发人员可以快速了解应用程序的界面组织和窗口之间的相互作用。
- 消息监视:Spy++可以捕获和显示应用程序之间发送和接收的Windows消息。这对于调试和分析应用程序的行为非常有用,特别是在处理用户输入、事件处理和消息传递方面。
- 属性查看:Spy++允许开发人员查看和修改窗口的属性,如标题、类名、位置、大小、样式等。这对于调试和修改窗口属性以及理解窗口如何与应用程序交互非常有帮助。
- 窗口捕获:Spy++可以捕获特定窗口的消息,并将其导出为日志文件,以供进一步分析和调试使用。
总之,Spy++是一个强大的工具,可用于Windows平台的应用程序开发和调试,它提供了丰富的功能来帮助开发人员理解和调试复杂的窗口应用程序。
打开之后,如下所示:
可以通过这样查看窗体名:
得到了关于这个窗体的一些信息,其中红框部分就是窗体标题,如下所示:
找到窗体标题之后,看看WindowManager.FindWindow
方法:
public static bool FindWindow(string formName)
{
for (int i = 0; i < 100; i++)
{
//按照窗体标题查找窗体
IntPtr vHandle = FindWindow(null, formName);
if (vHandle == IntPtr.Zero)
{
Thread.Sleep(100); //每100ms查找一次,直到找到,最多查找10s
continue;
}
else //找到返回True
{
intPtr = vHandle;
return true;
}
}
intPtr = IntPtr.Zero;
return false;
}
再看看 WindowManager.SetParent
方法:
public static void SetParent(IntPtr hWndNewParent, string windowName)
{
ShowWindow(intPtr, 0); //先将窗体隐藏,防止出现闪烁
SetParent(intPtr, hWndNewParent); //将第三方窗体嵌入父容器
Thread.Sleep(100); //略加延时
ShowWindow(intPtr, 3); //让第三方窗体在容器中最大化显示
RemoveWindowTitle(intPtr); // 去除窗体标题
}
现在查看一下效果:
但是我们发现嵌入的效果不是很好,而且无法随着窗体的变化而变化,需要再做下修改:
public Form1()
{
InitializeComponent();
this.Resize += new EventHandler(Form1_Resize);
}
注册窗体的Resize事件。
事件处理程序:
private void Form1_Resize(object sender, EventArgs e)
{
Task.Run(() =>
{
//第三方窗体句柄不为空
if (WindowManager.intPtr != IntPtr.Zero)
{
WindowManager.ResizeWindow();
}
});
}
现在再来看一下效果:
总结
以上就是在winform中嵌入第三方窗体的一次实践,希望对你有所帮助。
参考
1、C#完美将第三方窗体嵌入Panel容器(WPF、Winform)_c#嵌入另一个exe文件到panel控件中,exe打开的子窗口也识别进来-CSDN博客
在winform中如何嵌入第三方软件窗体✨的更多相关文章
- Winform中如何实现父窗体传递数据到子窗体并刷新子窗体
原理:利用委托和事件,本文将以图文并茂的例子讲述,告诉我们So Easy --------------------------------------------------------------- ...
- Winform中如何实现子窗体刷新父窗体
原理:利用委托和事件,本文将以图文并茂的例子讲述,告诉我们So Easy --------------------------------------------------------------- ...
- Winform中利用委托实现窗体之间的传值
点击打开按扭,打开传输值窗体 public partial class Form1 : Form { public Form1() { InitializeComponent(); } public ...
- winform中使用委托进行窗体之间的传值
一.传统的方式 创建一个公共数据资源类,用于存储窗体2的TextBox的值: public class ComValue { public static string Txtvalue { get; ...
- winform中通过事件实现窗体传值思路【待修改】
Form2向Form1传值 private Form1 form1;//定义一个类型为Form1类型的字段,用于存储传递过来的Form对象 public void Se ...
- C#WinForm窗体内Panel容器中嵌入子窗体、程序主窗体设计例子
C#WinForm父级窗体内Panel容器中嵌入子窗体.程序主窗体设计例子 在项目开发中经常遇到父级窗体嵌入子窗体所以写了一个例子程序,顺便大概划分了下界面模块和配色,不足之处还望指点 主窗体窗体采用 ...
- WPF中嵌入WinForm中的webbrowser控件
原文:WPF中嵌入WinForm中的webbrowser控件 使用VS2008创建WPF应用程序,需使用webbrowser.从工具箱中添加WPF组件中的webbrowser发现其中有很多属性事件不能 ...
- 在winform中使用cefsharp.winform嵌入浏览器(含视频教程)
免费视频教程和源码: https://www.bilibili.com/video/av84573813/ 1. 开始使用CefSharp在Winform中嵌入网页 2. 解决重复打开Cefsharp ...
- WinForm中新开一个线程操作 窗体上的控件(跨线程操作控件)
最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录窗体的显示,开了一个线程去加载数据远程的数据,会报一个错误"线程间操作无效: 从不是 ...
- C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部【转载】
这是最近在做的一个项目中提到的需求,把一个现有的窗体应用程序界面嵌入到自己开发的窗体中来,看起来就像自己开发的一样(实际上……跟自己开发的还是有一点点区别的,就是内嵌程序和宿主程序的窗口激活状态问题) ...
随机推荐
- Windows 堆管理机制 [3] Windows XP SP2 – Windows 2003 版本
3. Windows XP SP2 – Windows 2003 3.1 环境准备 环境 环境准备 虚拟机 32位Windows XP SP2 \32位Windows XP SP3 调试器 OllyD ...
- 加速tortoisegit的show log,减少等待时间
KMSID: 81703 是否同步到KM: 是 是否原创: 是 标签: 游戏开发 允许复制: 是 允许评论: 是 允许导出PDF: 是 职业库分类KMS: 游戏-游戏程序 查看权限KMS:网易正式员工 ...
- 快递单中抽取关键信息【一】----基于BiGRU+CR+预训练的词向量优化
相关文章: 1.快递单中抽取关键信息[一]----基于BiGRU+CR+预训练的词向量优化 2.快递单信息抽取[二]基于ERNIE1.0至ErnieGram + CRF预训练模型 3.快递单信息抽取[ ...
- CF813E Army Creation 题解
题目链接:CF 或者 洛谷 并不是很难的题,关于颜色数量类问题,那么很显然,沿用经典的 "HH的项链" 思想去思考问题.由于涉及到了 \(k\) 个数的限制,我们观察到如果一个数在 ...
- 2022 JuiceFS 社区用户调研结果出炉
为了使 JuiceFS 的发展更贴合用户的真实需求,我们在三周前向社区发出了一份调研问卷.此次调研面向已经将 JuiceFS 应用于生产环境的用户,了解其在应用 JuiceFS 前和使用中的体验与评价 ...
- NC19995 [HAOI2015]树上操作
题目链接 题目 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权. 然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...
- comm命令
comm命令 comm命令用于比较两个已排过序的文件,该命令会一列列地比较两个已排序文件的差异,并将其结果显示出来,如果没有指定任何参数,则会把结果分成3列显示:第1列仅是在第1个文件中出现过的列,第 ...
- win10无法保存代理服务器设置
问题说明 通过Internet设置->链接->局域网设置->代理服务设置,填写完地址后点选"确定"无反应,关闭窗口后重新打开'局域网设置',数据全无. 通过 开始 ...
- Java设计模式-迭代器模式Iterator
介绍 根据GoF的定义,迭代器模式提供了一种顺序访问聚合对象的元素而不暴露其底层表示的方法.这是一种行为设计模式. 顾名思义,迭代器有助于以定义的方式遍历对象集合,这对客户端应用程序很有用.在迭代期间 ...
- oracle 使用comment语句添加表注释
使用oracle comment语句可以给表.字段.视图等对象添加备注信息. 大致语法为: comment on TABLE table_name IS '备注内容'; 权限要求: 默认情况下用户只能 ...