在winform中如何嵌入第三方软件窗体✨

news2025/1/19 11:23:55

相关win32api的学习✨

SetParent

[DllImport("user32.dll ", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);   //将外部窗体嵌入程序

image-20240305202650242

语法:

HWND SetParent(
  [in]           HWND hWndChild,
  [in, optional] HWND hWndNewParent
);

参数:

参数名类型含义
hWndChildHWND子窗口的句柄
hWndNewParentHWND新父窗口的句柄。 如果此参数为 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
);

参数:

参数名类型含义
lpClassNameLPCTSTR如果 lpClassName 指向字符串,则指定窗口类名。
lpWindowNameLPCTSTR窗口名称 (窗口标题) 。 如果此参数为 NULL,则所有窗口名称都匹配。

ShowWindow

image-20240306122438035

作用:设置指定窗口的显示状态。

语法:

HWND FindWindowA(
  [in, optional] LPCSTR lpClassName,
  [in, optional] LPCSTR lpWindowName
);

参数:

参数名类型含义
hWndHWND窗口的句柄。
nCmdShowint控制窗口的显示方式。

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,同时尽量避免直接使用指针操作,以减少内存管理和安全性方面的问题。

这个静态类还有ResizeWindowFindWindowSetParentRemoveWindowTitle方法,等后面用到了再做解释。

创建一个winform✨

winform的设计如下所示:

image-20240306150128808

启动软件按钮点击事件处理程序:

 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++工具。

image-20240306151051044

什么是Spy++

Spy++(Spy++)是Microsoft Visual Studio套件中的一个实用工具,用于Windows平台的应用程序开发和调试。它允许开发人员查看和分析正在运行的Windows应用程序的窗口层次结构、消息流和属性。

Spy++的主要功能包括:

  1. 窗口层次结构:Spy++可以显示当前系统上所有可见和隐藏的窗口,并以层次结构的形式展示它们之间的父子关系。这使得开发人员可以快速了解应用程序的界面组织和窗口之间的相互作用。
  2. 消息监视:Spy++可以捕获和显示应用程序之间发送和接收的Windows消息。这对于调试和分析应用程序的行为非常有用,特别是在处理用户输入、事件处理和消息传递方面。
  3. 属性查看:Spy++允许开发人员查看和修改窗口的属性,如标题、类名、位置、大小、样式等。这对于调试和修改窗口属性以及理解窗口如何与应用程序交互非常有帮助。
  4. 窗口捕获:Spy++可以捕获特定窗口的消息,并将其导出为日志文件,以供进一步分析和调试使用。

总之,Spy++是一个强大的工具,可用于Windows平台的应用程序开发和调试,它提供了丰富的功能来帮助开发人员理解和调试复杂的窗口应用程序。

打开之后,如下所示:

image-20240306151615839

可以通过这样查看窗体名:

查找窗体名

得到了关于这个窗体的一些信息,其中红框部分就是窗体标题,如下所示:

image-20240306152034487

找到窗体标题之后,看看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);             // 去除窗体标题
  }

现在查看一下效果:

查看效果1

但是我们发现嵌入的效果不是很好,而且无法随着窗体的变化而变化,需要再做下修改:

 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();
          }
      });
    
  }

现在再来看一下效果:

查看效果2

总结✨

以上就是在winform中嵌入第三方窗体的一次实践,希望对你有所帮助。

参考✨

1、C#完美将第三方窗体嵌入Panel容器(WPF、Winform)_c#嵌入另一个exe文件到panel控件中,exe打开的子窗口也识别进来-CSDN博客

2、技术文档 | Microsoft Learn

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1496259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows关闭copilot预览版

如果用户不想在windows系统当中启用Copilot&#xff0c;可以通过以下三种方式禁用。 第一种&#xff1a;隐藏Copilot 按钮 右键点击任务栏&#xff0c;取消勾选“显示 Copilot&#xff08;预览版&#xff09;按钮”&#xff0c;任务栏则不再显示&#xff0c;用户可以通过快捷键…

2024 年 AI 辅助研发趋势:从研发数字化到 AI + 开发工具 2.0,不止于 Copilot

在上一年里&#xff0c;已经有不少的企业在工具链上落地了生成式 AI&#xff0c;结合我们对于这些企业的分析&#xff0c;以及最近在国内的一些 “新技术” 趋势&#xff0c;诸如于鸿蒙原生应用的初步兴起。从这些案例与趋势中&#xff0c;我们也看到了一些新的可能方向。 结合…

【C++】蓝桥杯必备 算法竞赛常用STL万字总结

传送门⏬⏬⏬[方便查表] &#x1f31f;一、什么是STL&#xff1f;&#x1f31f;二、为什么STL重要&#xff1f;✨1、原因✨2、STL的作用 &#x1f31f;三、STL知识点总结✨0.使用说明书✨1、vector 【可变数组】✨2、pair [ x,y ]✨3、string【字符串】✨4、queue【队列】 和pr…

2024年【道路运输企业安全生产管理人员】复审考试及道路运输企业安全生产管理人员模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年道路运输企业安全生产管理人员复审考试为正在备考道路运输企业安全生产管理人员操作证的学员准备的理论考试专题&#xff0c;每个月更新的道路运输企业安全生产管理人员模拟考试题祝您顺利通过道路运输企业安全…

第三讲 汇编初步 课程随手记

一、寄存器 32位CPU通用寄存器如下图所示&#xff1a; 因为教材依照的是32位CPU寄存器&#xff0c;而我安装的是64位寄存器&#xff0c;所以找了一下64位的寄存器的资料 PS&#xff1a;一般来说&#xff0c;Intel处理器字节存储顺序为小端法存储&#xff0c;是指数据的高字节保…

JavaScript极速入门(1)

初识JavaScript JavaScript是什么 JavaScript(简称JS),是一个脚本语言,解释型或者即时编译型语言.虽然它是作为开发Web页面的脚本语言而著名,但是也应用到了很多非浏览器的环境中. 看似这门语言叫JavaScript,其实在最初发明之初,这门语言的名字其实是在蹭Java的热度,实际上和…

Vue2+ElementUI下拉、Select组件的封装

Vue2ElementUI下拉、Select组件的封装&#xff1a;引言 在 Vue2 项目中&#xff0c;ElementUI 的 el-select 组件是常用的下拉选择框组件。它提供了丰富的功能和样式&#xff0c;可以满足各种需求。但是&#xff0c;在实际开发中&#xff0c;我们经常会遇到一些重复性的需求&a…

男人的玩具系统wordpress外贸网站主题模板

垂钓用品wordpress外贸模板 鱼饵、鱼竿、支架、钓箱、渔线轮、鱼竿等垂钓用品wordpress外贸模板。 https://www.jianzhanpress.com/?p3973 身体清洁wordpress外贸网站模板 浴盐、防蚊液、足部护理、沐浴液、洗手液、泡澡用品wordpress外贸网站模板。 https://www.jianzhan…

【CSP试题回顾】201612-1-中间数

CSP-201612-1-中间数 解题思路 输入和初始化&#xff1a;首先&#xff0c;程序读入一个整数n&#xff0c;表示序列中数的个数。接着&#xff0c;读入n个正整数并存储在numList向量中&#xff0c;这些数依次表示a1, a2, …, an。 排序&#xff1a;使用sort函数对numList进行升…

前端运算符比较与计算中的类型转换,运算规则

题目&#xff1a; 下面表达式的值分别都是什么&#xff08;类型转换&#xff09; 0 0 0 2 true 2 false false false false 0 false undefined false null null undefined\t\r\n 0JS中的原始类型有哪些 原始值类型就是 存储的都是值&#xff0c;没有函数可以调用的。…

【Python时序预测系列】基于LSTM+Attention实现单变量时间序列预测(源码)

这是我的第232篇原创文章。 一、引言 长短期记忆网络&#xff08;LSTM&#xff09;结合注意力机制是一种常用的深度学习模型结构&#xff0c;用于处理序列数据。LSTM是一种循环神经网络&#xff08;RNN&#xff09;的变体&#xff0c;专门设计用来解决长序列数据的梯度消失和…

不知道去哪里找拍抖音的短视频素材?分享几个抖音短视频素材资源网站

嘿嘿&#xff0c;小伙伴们&#xff0c;是不是在抖音创作的路上遇到了素材荒&#xff1f;别担心&#xff0c;我这里有几个超给力的短视频素材网站推荐给大家&#xff0c;保证让你的创作不再为素材发愁 1&#xff0c;蛙学府资源 这个网站简直是短视频素材的大宝库&#xff0c;无…

CSS盒子模型笔记

尚硅谷学习视频链接&#xff1a;117_CSS_盒子模型的组成部分_哔哩哔哩_bilibili 1、盒子组成 盒子组成 content内容 padding border &#xff08;margin不包含在盒子内&#xff09; 2、div样式width、height 当css3属性box-sizingcontent-box&#xff08;默认&#xff0…

基于Java的超市自助付款系统(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 商品类型模块2.2 商品模块2.3 超市账单模块 三、界面展示3.1 登录注册模块3.2 超市商品类型模块3.3 超市商品模块3.4 商品购买模块3.5 超市账单模块 四、部分源码展示4.1 实体类定义4.2 控制器接口 五、配套文档展示六、…

乐优商城(八)商品详情

1. 搭建商品详情微服务 当用户搜索到商品后&#xff0c;如果想要了解商品的更多信息&#xff0c;就需要进入商品详情页。 由于商品详情浏览量比较大&#xff0c;所以我们会创建一个微服务&#xff0c;用来展示商品详情。我们的商品详情页会采用 Thymeleaf 模板引擎渲染后&…

Linux高级编程:网络

回顾&#xff1a; 进程间的通信&#xff1a; 同一主机内通信&#xff1a; 传统的进程间通信方式&#xff08;管道、信号&#xff09;&#xff1b; IPC对象&#xff08;共享内存&#xff0c;消息队列&#xff0c;信号量集&#xff09;&#xff1b; 不同主机间进程的通信&#…

Linux之线程概念

目录 一、细粒度划分 1、堆区细粒度划分 2、物理内存和可执行程序细粒度划分 3、虚拟地址到物理地址的转化 二、线程的概念 1、基本概念 2、线程的优点 3、线程的缺点 4、线程异常 5、线程用途 三、Linux下的进程和线程 一、细粒度划分 1、堆区细粒度划分 在语言…

【MySQL】索引优化与关联查询优化

数据库调优的几个维度&#xff1a; 索引失效&#xff0c;没有充分用到索引——索引建立关联查询太多JOIN——SQL优化服务器调优以及各个参数设置——调整my.cnf数据过多——分库分表 SQL查询优化的几种方式&#xff1a; 物理查询优化&#xff1a;通过索引以及表连接方式进行…

Day30-Linux基础阶段总复习

Day30-Linux基础阶段总复习 1. 运维人员的三个核心职责&#xff08;了解&#xff09;2. 企业网站和应用的可用性的衡量标准&#xff08;重点&#xff09;2.1 高并发企业业务写入流程图2.2 中小型企业案例 3. Linux系统诞生发展过程中的关键代表人物4. 企业场景如何针对不同的业…

Springboot配置MySQL数据库

Springboot配置MySQL数据库 一、创建springboot项目&#xff0c;并添加如下依赖 <dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope> </dependency>二、在applica…