使用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 CTimerSerachDlg::HtmlWindowToHtmlWebBrowser(CComPtr spWindow)
{
ATLASSERT(spWindow != NULL);
CComQIPtr spServiceProvider = spWindow;
if (spServiceProvider == NULL)
{
return CComPtr();
}
CComPtr spWebBrws;
HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
if (hRes != S_OK)
{
return CComPtr();
}
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 CTimerSerachDlg::HtmlWindowToHtmlDocument(CComPtr spWindow)
{
ATLASSERT(spWindow != NULL);
CComPtr 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 spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
if (spBrws == NULL)
{
return CComPtr();
}
// Get the document object from the IWebBrowser2 object.
CComPtr 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);
}
}