使用WebBrowser打开一个页面,在该页面中有一个iFrame,在WebBrowser完全打开页面后,获取该iFrame的时候总是引发System.UnauthorizedAccessException异常。
Console.WriteLine(this.webMain.Document.Window.Frames[0].Url);
“this.webMain.Document.Window.Frames[0].Url”引发了“System.UnauthorizedAccessException”类型的异常
base {System.SystemException}: {"拒绝访问。 (异常来自 HRESULT:0x80070005 (E_ACCESSDENIED))"}
最近这个跨域的安全问题很困扰,搞了好久,终于在朋友的帮助下找到了一个C++的方法HtmlWindowToHtmlWebBrowser
CComPtr<IWebBrowser2> CTimerSerachDlg::HtmlWindowToHtmlWebBrowser(CComPtr<IHTMLWindow2> spWindow) { ATLASSERT(spWindow != NULL); CComQIPtr<IServiceProvider> spServiceProvider = spWindow; if (spServiceProvider == NULL) { return CComPtr<IWebBrowser2>(); } CComPtr<IWebBrowser2> spWebBrws; HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws); if (hRes != S_OK) { return CComPtr<IWebBrowser2>(); } return spWebBrws; } // Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure. // It takes into account accessing the DOM across frames loaded from different domains. CComPtr<IHTMLDocument2> CTimerSerachDlg::HtmlWindowToHtmlDocument(CComPtr<IHTMLWindow2> spWindow) { ATLASSERT(spWindow != NULL); CComPtr<IHTMLDocument2> spDocument; HRESULT hRes = spWindow->get_document(&spDocument); if ((S_OK == hRes) && (spDocument != NULL)) { // The html document was properly retrieved. return spDocument; } // hRes could be E_ACCESSDENIED that means a security restriction that // prevents scripting across frames that loads documents from different internet domains. CComPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow); if (spBrws == NULL) { return CComPtr<IHTMLDocument2>(); } // Get the document object from the IWebBrowser2 object. CComPtr<IDispatch> spDisp; hRes = spBrws->get_Document(&spDisp); spDocument = spDisp; return spDocument; }
在后来找到了作者的Blog,但是国内屏蔽了blogspot,直接不能够访问。
然后发现了作者的另外一篇文章:http://codecentrix.blogspot.com/2008/02/when-ihtmlwindow2document-throws.html
C# 跨域访问iframe的办法:http://www.codecentrix.com/blog/wnd2doc_csharp/GetDocumentFromWindowCsharp.zip
mshtml.HTMLDocumentClass htmlDoc = this.webMain.Document.DomDocument as mshtml.HTMLDocumentClass; object index = 0; mshtml.IHTMLWindow2 frameWindow = htmlDoc.frames.item(ref index) as mshtml.IHTMLWindow2; Console.WriteLine( CodecentrixSample.CrossFrameIE.GetDocumentFromWindow(frameWindow).activeElement.innerHTML);
接口转换的代码:
using System; using System.Runtime.InteropServices; using mshtml; namespace CodecentrixSample { public class CrossFrameIE { // Returns null in case of failure. public static IHTMLDocument2 GetDocumentFromWindow(IHTMLWindow2 htmlWindow) { if (htmlWindow == null) { return null; } // First try the usual way to get the document. try { IHTMLDocument2 doc = htmlWindow.document; return doc; } catch (COMException comEx) { // I think COMException won't be ever fired but just to be sure ... if (comEx.ErrorCode != E_ACCESSDENIED) { return null; } } catch (System.UnauthorizedAccessException) { } catch { // Any other error. return null; } // At this point the error was E_ACCESSDENIED because the frame contains a document from another domain. // IE tries to prevent a cross frame scripting security issue. try { // Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider. IServiceProvider sp = (IServiceProvider)htmlWindow; // Use IServiceProvider.QueryService to get IWebBrowser2 object. Object brws = null; sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out brws); // Get the document from IWebBrowser2. SHDocVw.IWebBrowser2 browser = (SHDocVw.IWebBrowser2)(brws); return (IHTMLDocument2)browser.Document; } catch { } return null; } private const int E_ACCESSDENIED = unchecked((int)0x80070005L); private static Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); private static Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"); } // This is the COM IServiceProvider interface, not System.IServiceProvider .Net interface! [ComImport(), ComVisible(true), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IServiceProvider { [return: MarshalAs(UnmanagedType.I4)][PreserveSig] int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject); } }