[代码]Delphi实现窗体内嵌其他应用程序窗体

实现原理是启动一个应用程序,通过ProcessID得到窗体句柄,然后对其设定父窗体句柄为本程序某控件句柄(本例是窗体内一个Panel的句柄),这样就达成了内嵌的效果。

本文实现的是内嵌一个记事本程序,如下图:

在实现细节上需要注意几点:

  1. 为了美化程序的嵌入效果,需要隐藏其标题栏
  2. 在外部窗体大小变化时,需要内嵌的窗体也随之变化大小
  3. 外部程序退出时,内嵌的程序也要退出

下面是例子程序。新建窗体,上面放置一个Panel控件,名为pnlApp,然后按下面代码编写:

unit frmTestEmbedApp;
 
interface
 
uses
  Windows,
Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,
ExtCtrls;
 
type
 
  TForm1
class(TForm)
    pnlApp:
TPanel;
    procedure FormCreate(Sender:
TObject);
    procedure FormClose(Sender:
TObject; 
var Action:
TCloseAction);
    procedure FormResize(Sender:
TObject);
  private
    {
Private declarations }
  public
    {
Public declarations }
  end;
 
var
  Form1:
TForm1;
  hWin:
HWND = 
0;
 
implementation
 
{$R
*.dfm}
 
type
  //
存储窗体信息
  PProcessWindow
= ^TProcessWindow;
  TProcessWindow
record
    ProcessID: Cardinal;
    FoundWindow:
hWnd;
  end;
 
//
窗体枚举函数
 
function EnumWindowsProc(Wnd:
HWND; ProcWndInfo: PProcessWindow): BOOL; stdcall;
var
  WndProcessID: Cardinal;
begin
  GetWindowThreadProcessId(Wnd,
@WndProcessID);
  if WndProcessID
= ProcWndInfo^.ProcessID 
then begin
    ProcWndInfo^.FoundWindow
:= Wnd;
    Result
:= 
False;                                    //
已找到,故停止 EnumWindows
  end
  else
    Result
:= 
True;                                     //
继续查找
end;
 
//
由 ProcessID 查找窗体 Handle
 
function GetProcessWindow(ProcessID: Cardinal):
HWND;
var
  ProcWndInfo:
TProcessWindow;
begin
  ProcWndInfo.ProcessID
:= ProcessID;
  ProcWndInfo.FoundWindow
:= 
0;
  EnumWindows(@EnumWindowsProc, Integer(@ProcWndInfo)); //
查找窗体
  Result
:= ProcWndInfo
.FoundWindow;
end;
 
//
在 Panel 上内嵌运行程序
 
function RunAppInPanel(const AppFileName: string;
ParentHandle: HWND; 
var WinHandle:
HWND): 
Boolean;
var
  si:
STARTUPINFO;
  pi:
TProcessInformation;
begin
  Result
:= 
False;
 
  //
启动进程
  FillChar(si,
SizeOf(si), 
0);
  si.cb
:= SizeOf(si);
  si.wShowWindow
:= SW_SHOW;
  if not CreateProcess(nilPChar(AppFileName), nilniltrue,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nilnil,
si, pi) 
then Exit;
 
  //
等待进程启动
  WaitForInputIdle(pi.hProcess, 10000);
 
  //
取得进程的 Handle
  WinHandle
:= GetProcessWindow(pi
.dwProcessID);
  if WinHandle
0 then begin
    //
设定父窗体
    Windows.SetParent(WinHandle,
ParentHandle);
 
    //
设定窗体位置
    SetWindowPos(WinHandle, 00000,
SWP_NOSIZE 
or SWP_NOZORDER);
 
    //
去掉标题栏
    SetWindowLong(WinHandle,
GWL_STYLE, GetWindowLong(WinHandle, GWL_STYLE)
      and (not WS_CAPTION) and (not WS_BORDER) and (not WS_THICKFRAME));
 
    Result
:= 
True;
  end;
 
  //
释放 Handle
  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);
end;
 
procedure TForm1.FormClose(Sender:
TObject; 
var Action:
TCloseAction);
begin
  //
退出时向内嵌程序发关闭消息
  if hWin
0 then PostMessage(hWin,
WM_CLOSE, 
00);
end;
 
procedure TForm1.FormCreate(Sender:
TObject);
const
  App
'C:\Windows\Notepad.exe';
begin
  pnlApp.Align
:= alClient;
 
  //
启动内嵌程序
  if not RunAppInPanel(App,
pnlApp
.Handle,
hWin) 
then ShowMessage('App
not found'
);
end;
 
procedure TForm1.FormResize(Sender:
TObject);
begin
  //
保持内嵌程序充满 pnlApp
  if hWin
<> 
0 then MoveWindow(hWin, 00,
pnlApp
.ClientWidth,
pnlApp
.ClientHeight, True);
end;
 
end.

这种方式也存在几个问题:

问题1:如果程序有Splash窗体先显示,则实际窗体无法内嵌,因为仅将Splash窗体的父窗体设定为本程序的控件句柄,后续窗体无法设定。

解决方法:可以通过轮询方式查询后续窗体,并设定其父窗体为本程序的控件句柄。

问题2:点击内嵌程序的窗体,则本程序的标题栏失去焦点

解决方法:不详。

问题3:点击内嵌程序的窗体,按下ALT+F4,则内嵌程序退出,仅留下本程序

解决方法:可以通过Hook方式拦截ALT+F4。

Delphi SetParent 嵌入其他应用程序的更多相关文章

  1. WPF中嵌入普通Win32程序的方法

    公司现在在研发基于.Net中WPF技术的产品,由于要兼容旧有产品,比如一些旧有的Win32程序.第三方的Win32程序等等,还要实现自动登录这些外部Win32程序,因此必须能够将这些程序整合到我们的系 ...

  2. 在winform嵌入外部应用程序

    应朋友要求,需要将一个第三方应用程序嵌入到本程序WinForm窗口,以前在VB6时代做过类似的功能,其原理就是利用Windows API中FindWindow函数找到第三方应用程序句柄,再利用SetP ...

  3. WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口)

    原文 WPF 同一窗口内的多线程/多进程 UI(使用 SetParent 嵌入另一个窗口) WPF 的 UI 逻辑只在同一个线程中,这是学习 WPF 开发中大家几乎都会学习到的经验.如果希望做不同线程 ...

  4. 把任意的EXE嵌入到自己程序中

    把任意的EXE嵌入到自己程序中 taoyuan19822008-08-24上传   Delphi把任意的EXE嵌入到自己程序中的程序 资源积分:0分 下载次数:327 资源类型:其他 资源大小:175 ...

  5. Qt界面中嵌入其他exe程序的界面,使用Qt5

    下面用一个小例子来演示如何在Qt的界面中嵌入其他exe程序的界面,最终效果如下图所示.本文参考了 http://blog.csdn.net/jiaoyaziyang/article/details/4 ...

  6. Delphi 在任务栏隐藏程序图标

    Delphi 在任务栏隐藏程序图标 方法一:1.修改工程文件中的“Application.MainFormOnTaskbar := True;”为“Application.MainFormOnTask ...

  7. Delphi Excel导入 的通用程序转载

    Delphi Excel导入 的通用程序 (-- ::)转载▼ 标签: it 分类: Delphi相关 步骤: 连excel(自己知道其格式,最好是没个字段在数据一一对应) 读excel数据,填入到数 ...

  8. Delphi - Indy TIdHTTP方式创建程序外壳 - 实现可执行程序的自动升级

    Delphi 实现可执行程序的自动升级 准备工作: 1:Delphi调用TIdHTTP方式开发程序,生成程序打包外壳 说明:程序工程命名为ERP_Update 界面布局如下: 代码实现如下: unit ...

  9. C# SetParent将其他程序嵌入自己的程序

    模块化的开发,将模块合并到一起的时候,遇到了Mdi不能添加到其它窗口下的问题. 分两种情况: 将mdi窗口A设成普通窗口B的子控件,需要将A的TopLevel设置成false,但是Mdi窗口的TopL ...

随机推荐

  1. phpstorm 2017激活码(方法)

    JetBrains激活 JetBrains 授权服务器(License Server URL):http://idea.imsxm.com 使用方法:激活时选择License server 填入htt ...

  2. 存储-实例-ibm v3700

    raid5总容量计算(n-1)*最小盘容量 RAID0:N块盘组成,逻辑容量为N块盘容量之和:RAID1:两块盘组成,逻辑容量为一块盘容量:RAID3:N+1块盘组成,逻辑容量为N块盘容量之和:RAI ...

  3. 论 大并发 下的 乐观锁定 Redis锁定 和 新时代事务

    在 <企业应用架构模式> 中 提到了 乐观锁定, 用 时间戳 来 判定 交易 是否有效, 避免 传统事务 的 表锁定 造成 的 瓶颈 . 在 现在的 大并发 的 大环境下, 传统事务 及其 ...

  4. 1、Nginx集群tomcat

    一.准备工作 下载nginx,http://nginx.org/,本文采用nginx-1.8.0,下载之后直接解压,免安装 下载tomcat,以配置3台tomcat服务器做负载均衡为例 二.修改tom ...

  5. CF 666E Forensic Examination——广义后缀自动机+线段树合并

    题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...

  6. python调用hanlp分词包手记

    python调用hanlp分词包手记   Hanlp作为一款重要的分词工具,本月初的时候看到大快搜索发布了hanlp的1.7版本,新增了文本聚类.流水线分词等功能.关于hanlp1.7版本的新功能,后 ...

  7. cat命令详解

    命令cat cat 命令用于连接文件并打印到标准输出设备上 语法格式: cat [-AbeEnstTuv] [--help] [--version] fileName 参数说明: -n 或 --num ...

  8. ML: 聚类算法R包-网格聚类

    网格聚类算法 optpart::clique optpart::clique CLIQUE(Clustering In QUEst)是一种简单的基于网格的聚类方法,用于发现子空间中基于密度的簇.CLI ...

  9. ML: 聚类算法-概论

    聚类分析是一种重要的人类行为,早在孩提时代,一个人就通过不断改进下意识中的聚类模式来学会如何区分猫狗.动物植物.目前在许多领域都得到了广泛的研究和成功的应用,如用于模式识别.数据分析.图像处理.市场研 ...

  10. 坑人的 Javascript 模块化编程 sea.js

    坑人的 Javascript 模块化编程 sea.js 忧伤 加 蛋疼的 开始了 看文档 Sea.js 进行配置 seajs.config({ // 设置路径,方便跨目录调用 paths: { 'ar ...