WinForms WebBrowser 載入頁面包含跨域 <iframe> (如嵌入百度)。嘗試 frames[0].Document.GetElementById(...) 會拋出 UnauthorizedAccessException,瀏覽器同源策略阻擋跨域 DOM 存取。
WebBrowser 底層為 ActiveX IWebBrowser2。透過 IServiceProvider.QueryService 從 IHTMLWindow2 (iframe window) 取得對應的 IWebBrowser2,再取其 Document 即可繞過同源限制 (需同進程、信任區域)。
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using mshtml; // 參考 Microsoft.mshtml
namespace WebBrowserTest
{
// COM IServiceProvider 介面 (非 System.IServiceProvider)
[ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IServiceProvider
{
[PreserveSig]
int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
}
// IWebBrowser2 關鍵成員
[ComImport, Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")]
[TypeLibType(TypeLibTypeFlags.FOleAutomation | TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)]
public interface IWebBrowser2
{
object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
// 其他成員略...
}
public static class CrossDomainHelper
{
private static readonly Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
private static readonly Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
///
/// 從跨域 iframe window 取得可操作的 IHTMLDocument3
///
public static IHTMLDocument3 GetDocumentFromWindow(IHTMLWindow2 htmlWindow)
{
if (htmlWindow == null) return null;
// 1. 嘗試直接存取 (同域有效)
try
{
return (IHTMLDocument3)htmlWindow.document;
}
catch (UnauthorizedAccessException) { /* 跨域,繼續 */ }
catch (COMException) { /* 其他 COM 錯誤 */ }
// 2. 跨域處理:透過 IServiceProvider 取得 IWebBrowser2
try
{
var sp = (IServiceProvider)htmlWindow;
object browserObj = null;
int hr = sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out browserObj);
if (hr != 0 || browserObj == null) return null;
var browser = (IWebBrowser2)browserObj;
return (IHTMLDocument3)browser.Document;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"CrossDomainHelper error: {ex}");
return null;
}
}
}
}
private void btnTest_Click(object sender, EventArgs e)
{
var doc = webBrowser1.Document;
var frames = doc.Window.Frames;
// 取得主頁面輸入值
string searchText = doc.GetElementById("search").GetAttribute("value");
// 關鍵:使用輔助類取得跨域 iframe Document
var baiduDoc = CrossDomainHelper.GetDocumentFromWindow((IHTMLWindow2)frames[0].DomWindow);
if (baiduDoc != null)
{
baiduDoc.getElementById("kw").setAttribute("value", searchText);
baiduDoc.getElementById("su").click(); // 執行搜尋
}
else
{
MessageBox.Show("無法存取跨域 iframe");
}
}
<!DOCTYPE html>
<html>
<body>
<iframe id="baidu" width="500" height="500" src="https://www.baidu.com"></iframe>
<div>測試值:<input id="search" type="text" /></div>
</body>
</html>
| 關鍵 | 說明 |
|---|---|
| mshtml 參考 | 專案右鍵「加入參考」-> COM -> 「Microsoft HTML Object Library」 |
| IServiceProvider | COM 介面,非 .NET 同名介面,需正確 GUID 與 PreserveSig |
| QueryService | 從 IHTMLWindow2 取得對應 IWebBrowser2,核心跳板 |
| IHTMLDocument3 | 包含 getElementById 等 DOM 方法,轉型後可直接操作 |
| 信任區域 | 本地檔案區域 / 信任網站區域較易成功;Internet 區域可能受保護模式阻擋 |
傳統 WebBrowser (IE 內核) 跨域 iframe 存取需透過 COM IServiceProvider.QueryService 取得 IWebBrowser2.Document 繞過同源策略。代碼複雜且受 IE 保護模式影響大。新專案強烈建議改用 WebView2 (Edge Chromium) 或 CefSharp,獲得現代渲染引擎與標準化自動化 API。