天天看点

C#中webBrowser加载页面中访问不同域的iFrame引发System.UnauthorizedAccessException异常的解决办法

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