[代碼]Delphi實(shí)現(xiàn)窗體內(nèi)嵌其他應(yīng)用程序窗體
實(shí)現(xiàn)原理是啟動一個(gè)應(yīng)用程序,通過ProcessID得到窗體句柄,然后對其設(shè)定父窗體句柄為本程序某控件句柄(本例是窗體內(nèi)一個(gè)Panel的句柄),這樣就達(dá)成了內(nèi)嵌的效果。
本文實(shí)現(xiàn)的是內(nèi)嵌一個(gè)記事本程序,如下圖:
在實(shí)現(xiàn)細(xì)節(jié)上需要注意幾點(diǎn):
- 為了美化程序的嵌入效果,需要隱藏其標(biāo)題欄
- 在外部窗體大小變化時(shí),需要內(nèi)嵌的窗體也隨之變化大小
- 外部程序退出時(shí),內(nèi)嵌的程序也要退出
下面是例子程序。新建窗體,上面放置一個(gè)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 ; // 窗體枚舉函數(shù) 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 ; // 繼續(xù)查找 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 上內(nèi)嵌運(yùn)行程序 function RunAppInPanel( const AppFileName: string ; ParentHandle: HWND; var WinHandle: HWND): Boolean ; var si: STARTUPINFO; pi: TProcessInformation; begin Result := False ; // 啟動進(jìn)程 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; // 等待進(jìn)程啟動 WaitForInputIdle(pi . hProcess, 10000 ); // 取得進(jìn)程的 Handle WinHandle := GetProcessWindow(pi . dwProcessID); if WinHandle > 0 then begin // 設(shè)定父窗體 Windows . SetParent(WinHandle, ParentHandle); // 設(shè)定窗體位置 SetWindowPos(WinHandle, 0 , 0 , 0 , 0 , 0 , SWP_NOSIZE or SWP_NOZORDER); // 去掉標(biāo)題欄 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 // 退出時(shí)向內(nèi)嵌程序發(fā)關(guān)閉消息 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; // 啟動內(nèi)嵌程序 if not RunAppInPanel(App, pnlApp . Handle, hWin) then ShowMessage( 'App not found' ); end ; procedure TForm1 . FormResize(Sender: TObject); begin // 保持內(nèi)嵌程序充滿 pnlApp if hWin <> 0 then MoveWindow(hWin, 0 , 0 , pnlApp . ClientWidth, pnlApp . ClientHeight, True ); end ; end . |
這種方式也存在幾個(gè)問題:
問題1:如果程序有Splash窗體先顯示,則實(shí)際窗體無法內(nèi)嵌,因?yàn)閮H將Splash窗體的父窗體設(shè)定為本程序的控件句柄,后續(xù)窗體無法設(shè)定。
解決方法:可以通過輪詢方式查詢后續(xù)窗體,并設(shè)定其父窗體為本程序的控件句柄。
問題2:點(diǎn)擊內(nèi)嵌程序的窗體,則本程序的標(biāo)題欄失去焦點(diǎn)
解決方法:不詳。
問題3:點(diǎn)擊內(nèi)嵌程序的窗體,按下ALT+F4,則內(nèi)嵌程序退出,僅留下本程序
解決方法:可以通過Hook方式攔截ALT+F4。
愛生活,愛拉風(fēng)