.NET CF 2.0 的 WebBrowser 僅提供 DocumentText (唯寫),**無法取得 DOM 模型**,導致無法:
完整框架 (FF) 有 WebBrowser.Document 回傳 HtmlDocument (封裝 IHTMLDocument2),CF 卻被阉割。
WM SDK 提供原生視窗類別 DISPLAYCLASS (定義於 htmlctrl.h),支援訊息 DTM_DOCUMENTDISPATCH 取得 IDispatch → QueryInterface(IID_IPIEHTMLDocument) 獲得完整 DOM 介面。
[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr CreateWindowExW(
uint dwExStyle, string lpClassName, string lpWindowName, uint dwStyle,
int x, int y, int nWidth, int nHeight,
IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);
[DllImport("coredll.dll", EntryPoint = "GetModuleHandleW", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("coredll.dll", EntryPoint = "LoadLibraryW", SetLastError = true)]
public static extern IntPtr LoadLibraryCE(string lpLibFileName);
[DllImport("htmlview.dll", EntryPoint = "InitHTMLControl", SetLastError = true)]
public static extern int InitHTMLControl(IntPtr hInst);
public class WebBrowserEx : Control
{
private IntPtr _hwndHtml;
public WebBrowserEx()
{
// 1. 載入 htmlview.dll 並初始化
IntPtr hInst = GetModuleHandle(null);
LoadLibraryCE("htmlview.dll");
InitHTMLControl(hInst);
// 2. 建立原生視窗為子視窗
const uint WS_CHILD = 0x40000000, WS_VISIBLE = 0x10000000;
_hwndHtml = CreateWindowExW(0, "DISPLAYCLASS", null, WS_CHILD | WS_VISIBLE,
0, 0, 0, 0, this.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
// 同步大小
MoveWindow(_hwndHtml, 0, 0, Width, Height, true);
}
[DllImport("coredll.dll")]
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int w, int h, bool repaint);
}
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, ref IntPtr lParam);
private const int DTM_DOCUMENTDISPATCH = 0x0400 + 123; // WM_USER + 123
public HtmlViewExport.IPIEHTMLDocument3 HtmlDocument
{
get
{
IntPtr pDispatch = IntPtr.Zero;
SendMessage(_hwndHtml, DTM_DOCUMENTDISPATCH, 0, ref pDispatch);
if (pDispatch == IntPtr.Zero) return null;
return Marshal.GetObjectForIUnknown(pDispatch) as HtmlViewExport.IPIEHTMLDocument3;
}
}
webvw.idl 定義 IPIEHTMLDocument 等介面。midl /D UNDER_CE webvw.idl → 產生 webvw.tlb
tlbimp webvw.tlb /out:HtmlViewExport.dll
HtmlViewExport.dll 即獲得強型別 IPIEHTMLDocument3、IHTMLElement 等介面。
// 載入 HTML
webBrowserEx.HtmlDocument.write("");
webBrowserEx.HtmlDocument.close();
// 取得輸入值
var doc = webBrowserEx.HtmlDocument;
var input = doc.getElementById("txtName") as HtmlViewExport.IHTMLInputElement;
string name = input.value; // "Andy"
MessageBox.Show(name);
doc.write(html) + doc.close() (等同 FF DocumentText)。doc.parentWindow.execScript("alert('hi')", "JavaScript")。IHTMLElement.attachEvent 接收 click/change 等事件。| 問題 | 解決 |
|---|---|
| htmlview.dll 版本差異 (WM5/6/6.5) | IDL 可能不同,需對應 SDK 版本重新產生 TLB |
| 視窗銷毀順序 | 覆寫 Dispose 先 DestroyWindow(_hwndHtml) 再 base.Dispose() |
| 記憶體洩漏 | COM 介面用完 Marshal.ReleaseComObject;長期持有 HtmlDocument 參考會阻礙 GC |
| 輸入法/鍵盤彈出遮擋 | 處理 GotFocus/LostFocus 調整版面 |
透過 **P/Invoke 建立原生 DISPLAYCLASS 視窗 + DTM_DOCUMENTDISPATCH 取得 IDispatch + tlbimp 產生 Interop 組件**,成功在 .NET CF 上實現**完整 HTML DOM 讀寫、腳本執行、事件處理**,彌補框架缺失。此技術同樣適用於需嵌入原生 ActiveX/COM 控制項的 CF 專案。