实现原理是启动一个应用程序,通过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(nil, PChar(AppFileName), nil, nil, true,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil, si, pi) then Exit;
 
// 等待进程启动
WaitForInputIdle(pi.hProcess, 10000);
 
// 取得进程的 Handle
WinHandle := GetProcessWindow(pi.dwProcessID);
if WinHandle > 0 then begin
// 设定父窗体
Windows.SetParent(WinHandle, ParentHandle);
 
// 设定窗体位置
SetWindowPos(WinHandle, 0, 0, 0, 0, 0, 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, 0, 0);
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, 0, 0, pnlApp.ClientWidth, pnlApp.ClientHeight, True);
end;
 
end.

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

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

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

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

解决方法:不详。

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

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

Delphi实现窗体内嵌其他应用程序窗体的更多相关文章

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

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

  2. WebAPI服务端内嵌在CS程序里面

    有时候我们不需要将WebAPI发布到iis上运行,需要将webapi内嵌到cs程序内部,随程序一起启动,其实比较简单,需要一个类,如下 public class Startup { public st ...

  3. C# 窗体常用API函数 应用程序窗体查找

    常用的处理窗体的API函数如下(注意:API函数必须放在窗体中...): 使用C#语言,要引用DllImport,必须要添加using System.Runtime.InteropServices命名 ...

  4. h5内嵌微信小程序,调用微信支付功能

    在小程序中不能使用之前在浏览器中配置的支付功能,只能调用小程序专属的api进行支付. 因为需要在现在实现的基础上,再添加在小程序中调用微信支付功能,所以我的思路是这样的 1.在点击支付按钮时,判断是不 ...

  5. C#窗体内嵌外部程序(cmd.exe)的显示【转载】

    [DllImport("User32.dll ", EntryPoint = "SetParent")] private static extern IntPt ...

  6. C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部【转载】

    这是最近在做的一个项目中提到的需求,把一个现有的窗体应用程序界面嵌入到自己开发的窗体中来,看起来就像自己开发的一样(实际上……跟自己开发的还是有一点点区别的,就是内嵌程序和宿主程序的窗口激活状态问题) ...

  7. 【转】C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部

    PS:文末的附件已更新,这次我放到博客园里面了,不会弹出广告,放心下载,O(∩_∩)O谢谢! 这是最近在做的一个项目中提到的需求,把一个现有的窗体应用程序界面嵌入到自己开发的窗体中来,看起来就像自己开 ...

  8. .NET Core的文件系统[4]:由EmbeddedFileProvider构建的内嵌(资源)文件系统

    一个物理文件可以直接作为资源内嵌到编译生成的程序集中.借助于EmbeddedFileProvider,我们可以统一的编程方式来读取内嵌于某个程序集中的资源文件,不过在这之前我们必须知道如何将一个项目文 ...

  9. 由EmbeddedFileProvider构建的内嵌(资源)文件系统

    由EmbeddedFileProvider构建的内嵌(资源)文件系统 一个物理文件可以直接作为资源内嵌到编译生成的程序集中.借助于EmbeddedFileProvider,我们可以统一的编程方式来读取 ...

随机推荐

  1. JQM页面跳转,多种效果

    <div data-role="page" id="pageone"> <div data-role="header"&g ...

  2. live555库中的testRTSPClient实例

    1.testRTSPClient简介 testRTSPClient是个简单的客户端实例,这个实例对rtsp数据交互作了详细的描述,其中涉及到rtsp会话的两个概念Source和Sink. Source ...

  3. 使用 trash-cli 逃出 rm 命令误删除重要文件的阴影

    今天是一个难忘的日子,当时本来想清除我们公司网站cms目录下面一些cdn推送网页后残留的垃圾文件,结果在执行rm -rf conten* 时打成了rm -rf conten *结果就悲剧了.cms目录 ...

  4. APP抓链接工具(Fiddler版)

    1.下载Fiddler源代码: http://pan.baidu.com/s/1hqEUK0O 2.修改如下源代码: 3.运行截图:

  5. (转载)MySQL数据类型:TINYINT, SMALLINT, MEDIUMINT, INT, INTEGER等字段类型区别

    (转自http://3y.uu456.com/bp-f088f739376baf1ffc4fad40-1.html) MySQL支持大量的列类型,它可以被分为3类:数字类型.日期和时间类型.字符串(字 ...

  6. eclipse-4.4.2安装Groovy插件(其他版本eclipse可参考)

    步骤 : 1.启动eclipse,点击help -> Install New Software... 在弹出的窗口中点击:Add... Groovy插件的地址:http://dist.sprin ...

  7. asp.net core csrf

    如果用tag 比如 <form asp-action="Login" asp-controller="Account" method="post ...

  8. 获取C#代码执行的时间(精确到毫秒)

    using System.Diagnostics;//引用相关的命名空间Stopwatch st=new Stopwatch ();//实例化类st. Start();//开始计时 //需要统计时间的 ...

  9. 降维PCA技术

    降维技术使得数据变得更易使用,并且它们往往能够去除数据中的噪声,使得机器学习任务往往更加精确. 降维往往作为预处理步骤,在数据应用到其它算法之前清洗数据.有很多技术可以用于数据降维,在这些技术中,独立 ...

  10. 大熊君大话NodeJS之------Global Objects全局对象

    一,开篇分析 在上个章节中我们学习了NodeJS的基础理论知识,对于这些理论知识来说理解是至关重要的,在后续的章节中,我们会对照着官方文档逐步学习里面的各部分模块,好了该是本文主角登台亮相的时候了,G ...