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

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

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. C#窗体内嵌外部程序(cmd.exe)的显示【转载】

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

  5. Markdown 代码块中再内嵌一个行内代码

    在 jQuery 1.9 之前(不含1.9):如果传入一个空字符串. null 或 jQuery.parseJSON( jsonString ) ,该函数将返回,而不是抛出一个错误,即使它不是有效的  ...

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

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

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

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

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

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

  9. 『Asp.Net 组件』Asp.Net 服务器组件 内嵌图片:自己的图片控件

    代码: using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace ...

随机推荐

  1. PySpider HTTP 599: SSL certificate problem错误的解决方法

    在用 PySpider 爬取 https 开头的网站的时候遇到了 HTTP 599: SSL certificate problem: self signed certificate in certi ...

  2. 百度云受邀成为 LF Edge 创始成员

    导读 1月24日,Linux Foundation(Linux基金会,以下简称LF)宣布正式成立旗下子机构边缘计算基金会LF Edge,旨在建立边缘计算统一开源框架.百度云受邀成为创始成员. 中国公司 ...

  3. AI 朴素贝叶斯分类

    1.条件概率 P(A|B)表示在事件B已经发生的条件下,事件A发生的概率.计算公式:P(A|B)=P(AB)/P(B). 2.相互独立事件 对于相互独立事件A和B,它们发生的概率互不影响,P(AB)= ...

  4. 网易云音乐 歌词制作软件 BesLyric (最新版本下载)

    导读 BesLyric , 一款专门制作 网易云音乐 LRC 滚动歌词的软件! 搜索.下载.制作 歌词更方便! 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦!Beslyric 为你排忧解难! 本文 ...

  5. STM32 printf()函数和scanf()函数重定向到串口

    STM32 printf()函数和scanf()函数重定向到串口 printf()函数和scanf()函数重定向 在学习STM32的时候,常常需要用串口来测试代码的正确与否,这时候就要要用到print ...

  6. SQL Server中使用convert进行日期转换(转载)

    一般存入数据库中的时间格式为yyyy-mm-dd hh:mm:ss 如果要转换为yyyy-mm-dd  短日期格式.可以使用convert函数.下面是sqlserver帮助中关于convert函数的声 ...

  7. ASP.NET Core读取AppSettings (转载)

    今天在把之前一个ASP.NET MVC5的Demo项目重写成ASP.NET Core,发现原先我们一直用的ConfigurationManager.AppSettings[]读取Web.config中 ...

  8. Mapreduce打印调试输出

    Mapreduce打印调试内容: 一.启动JobHistoryServer mr-jobhistory-daemon.sh start historyserver [hadoop@node11 sbi ...

  9. 介绍一个axios调试好用的工具:axios-mock-adapter

    上一篇文章中写到用promise时应注意的问题,这一篇文章继续介绍一个可以和axios库配合的好工具: axios-mock-adapter.axios-mock-adapter可以用来拦截http请 ...

  10. ASP.NET RAZOR自动生成的js Timer

    <input type="hidden" value="@(Model.TimeLength)" id="examTimeLength" ...