IHTMLWindow2?許多 Delphi 開發者在 FormCreate 或 Navigate 後立即嘗試訪問 WebBrowser1.Document,卻發現返回 nil 或拋出「介面不支援」異常。根本原因在於:MSHTML DOM 樹尚未構建完成。正確的時機是 DocumentComplete 事件,且必須過濾主框架。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, OleCtrls, SHDocVw, StdCtrls, MSHTML;
type
TForm1 = class(TForm)
WebBrowser1: TWebBrowser;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure WebBrowser1DocumentComplete(ASender: TObject; const pDisp: IDispatch; var URL: OleVariant);
procedure Button1Click(Sender: TObject);
private
FWindow: IHTMLWindow2;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
WebBrowser1.Align := alClient;
WebBrowser1.Navigate('https://example.com');
end;
// 關鍵:只在主框架完成時獲取 Window
procedure TForm1.WebBrowser1DocumentComplete(ASender: TObject; const pDisp: IDispatch; var URL: OleVariant);
var
WB: TWebBrowser;
Doc: IHTMLDocument2;
begin
WB := ASender as TWebBrowser;
// pDisp = WB.Application 表示主框架文檔加載完成
if pDisp = WB.Application then
begin
Doc := WB.Document as IHTMLDocument2;
if Assigned(Doc) then
FWindow := Doc.parentWindow; // 緩存供後續使用
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FWindow) then
begin
// 示例:修改頁面內容並彈出 JS 警告
FWindow.document.body.innerHTML := 'Delphi 動態注入成功
';
FWindow.alert('從 Delphi 呼叫 JavaScript alert()');
end
else
ShowMessage('Window 尚未就緒,請等待頁面加載完成');
end;
end.
pDisp = WB.Application?DocumentComplete 會為每個框架(<iframe>、<frame>)分別觸發。pDisp 參數指向觸發該事件的文檔的 IDispatch 指針。只有主框架的 pDisp 等於 WebBrowser.Application(即 IWebBrowser2 接口)。若不加過濾,iframe 載入時也會覆蓋 FWindow,導致主文檔引用丟失。
parentWindow vs ScriptparentWindow:返回 IHTMLWindow2,標準 DOM Window 對象,支援完整 DOM API。Script:返回 IDispatch,指向腳本引擎的全局對象(JScript/VBScript),適合調用全局 JS 函數或訪問全局變量。二者在現代 IE 引擎中本質相同,但 parentWindow 更符合 DOM 標準。示例中 FWindow 定義為窗體私有字段(private),而非單元級全局變量。優點:
_Release)。try
Doc := WB.Document as IHTMLDocument2;
if Assigned(Doc) then
FWindow := Doc.parentWindow;
except
on E: EOleException do
// 處理文檔未就緒、跨域拒絕訪問等 COM 錯誤
LogError('獲取 Window 失敗: ' + E.Message);
end;
若在運行時動態創建 TWebBrowser,需手動連接事件:
procedure TForm1.CreateDynamicBrowser;
var
WB: TWebBrowser;
begin
WB := TWebBrowser.Create(Self);
WB.Parent := Panel1;
WB.Align := alClient;
WB.OnDocumentComplete := WebBrowser1DocumentComplete; // 共用事件處理器
WB.Navigate('https://another-site.com');
end;
| 錯誤做法 | 後果 | 正確做法 |
|---|---|---|
| 在 FormCreate 中直接訪問 Document | Document 為 nil,AV 異常 | 等待 DocumentComplete |
| 不判斷 pDisp,直接賦值 | iframe 載入覆蓋主窗口引用 | if pDisp = WB.Application then |
| 用局部變量接收 Window | 事件結束變量銷毀,引用失效 | 用字段/屬性持久化引用 |
| 忽略跨域訪問異常 | 程式崩潰或靜默失敗 | try..except 捕獲 EOleException |
掌握 DocumentComplete + pDisp 過濾 + 字段緩存 這套「黃金三角」,是 Delphi 控制 TWebBrowser 進行 DOM 操作的基石。在此基礎上,你可以安全地注入腳本、提取數據、模擬用戶交互,構建穩健的網頁自動化應用。