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


    实现原理是启动一个应用程序,通过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:WindowsNotepad.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。

     
     
  • 相关阅读:
    Perl文件处理示例——批量添加Copyright版权信息
    关于Perl文件操作——批量修改文件名
    保持创造力的29种方法
    用perl实现宋词词频统计——东风何处是人间
    Verilog UDP(User Defined Primitives)
    [转]一些经典的计算机书籍
    [转]30分钟,让你成为一个更好的程序员
    Python 使用数据库(SQLite)
    scrapy配置mysql
    创建自己的网站博客Hexo
  • 原文地址:https://www.cnblogs.com/jijm123/p/6503401.html
Copyright © 2020-2023  润新知