首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET Compact Framework 透過托管 Win32 取得 HtmlDocument:嵌入 HTML 控制項實現 DOM 操作

     編輯:關於.NET

痛點:CF WebBrowser 控制項無 Document 屬性

.NET CF 2.0 的 WebBrowser 僅提供 DocumentText (唯寫),**無法取得 DOM 模型**,導致無法:

  • 讀取使用者輸入的表單值。
  • 動態操作 HTML 元素 (隱藏/顯示/修改樣式)。
  • 注入 JavaScript 與宿主互動。

完整框架 (FF) 有 WebBrowser.Document 回傳 HtmlDocument (封裝 IHTMLDocument2),CF 卻被阉割。

突破口:Windows Mobile 原生 HTML Control (DISPLAYCLASS)

WM SDK 提供原生視窗類別 DISPLAYCLASS (定義於 htmlctrl.h),支援訊息 DTM_DOCUMENTDISPATCH 取得 IDispatch → QueryInterface(IID_IPIEHTMLDocument) 獲得完整 DOM 介面。

步驟一:P/Invoke 建立原生視窗


[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);

步驟二:自訂 Control 封裝 HTML Control


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);
}

步驟三:取得 IPIEHTMLDocument (DOM 根節點)


[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;
    }
}

步驟四:產生 COM Interop 組件 (關鍵技巧)

  1. SDK 中 webvw.idl 定義 IPIEHTMLDocument 等介面。
  2. VS 命令提示字元執行:
    midl /D UNDER_CE webvw.idl   → 產生 webvw.tlb
    tlbimp webvw.tlb /out:HtmlViewExport.dll
  3. 專案參考 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 專案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved