We often use WebBrowser to perform automated tasks during web testing. In some web pages, IFrame is used to nest other pages. These pages may not be under the same domain name. In this case, cross-domain issues may occur and the IFrame elements cannot be obtained directly in WebBrowser. Next, let's do a test. Write a page to nest a Baidu homepage, enter the words to be queried on our own page, and then automatically complete the search on Baidu.
Copy codeThe Code is as follows:
<! DOCTYPE html>
<Html lang = "en" xmlns = "http://www.w3.org/1999/xhtml">
<Head>
<Meta charset = "UTF-8"/>
<Title> </title>
</Head>
<Body>
<Iframe id = "baidu" style = "float: left;" width = "500" height = "500" src = "http://www.baidu.com"> </iframe>
<Div>
Test Value: <input id = "search" type = "text"/>
</Div>
</Body>
</Html>
Next, we will create a simple WinForm project for testing. The interface is as follows:
The following is the WebBrowser test code:
Copy codeThe Code is as follows:
Using System;
Using System. Windows. Forms;
Namespace WebBrowserTest
{
Public partial class Form1: Form
{
Public Form1 ()
{
InitializeComponent ();
}
Private void button#click (object sender, EventArgs e)
{
This. webBrowser1.Navigate (this. textBox1.Text );
}
Private void button2_Click (object sender, EventArgs e)
{
Var doc = this. webBrowser1.Document;
Var frames = doc. Window. Frames;
String testValue = doc. GetElementById ("search"). GetAttribute ("value ");
Frames [0]. Document. GetElementById ("kw"). SetAttribute ("value", testValue );
Frames [0]. Document. GetElementById ("su"). InvokeMember ("click ");
}
}
}
After running our test program, load the previously written page, enter the words we want to query on our own page, and click the test button, the program reports the UnauthorizedAccessException error:
The following is a Helper class to solve this problem. The main principle is to use the IWebBrowser2 interface to obtain the Dom in Ifream. The document in IWebBrowser2 can be converted to IHtmlDocument1, IHtmlDocument2, and IHtmlDocument3.
Copy codeThe Code is as follows:
Using System;
Using System. Runtime. InteropServices;
Using System. Windows. Forms;
Using mshtml;
Namespace WebBrowserTest
{
// 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: financialas (UnmanagedType. I4)]
[PreserveSig]
Int QueryService (ref Guid guidService, ref Guid riid, [financialas (UnmanagedType. Interface)] out object ppvObject );
}
Public enum OLECMDF
{
OLECMDF_DEFHIDEONCTXTMENU = 0x20,
OLECMDF_ENABLED = 2,
OLECMDF_INVISIBLE = 0x10,
OLECMDF_LATCHED = 4,
OLECMDF_NINCHED = 8,
OLECMDF_SUPPORTED = 1
}
Public enum olepolicid
{
OLECMDID_PAGESETUP = 8,
OLECMDID_PRINT = 6,
OLECMDID_PRINTPREVIEW = 7,
OLECMDID_PROPERTIES = 10,
OLECMDID_SAVEAS = 4
}
Public enum OLECMDEXECOPT
{
OLECMDEXECOPT_DODEFAULT,
OLECMDEXECOPT_PROMPTUSER,
OLECMDEXECOPT_DONTPROMPTUSER,
OLECMDEXECOPT_SHOWHELP
}
[ComImport, Guid ("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), TypeLibType (TypeLibTypeFlags. FOleAutomation | TypeLibTypeFlags. FDual | TypeLibTypeFlags. FHidden)]
Public interface IWebBrowser2
{
[DispId (100)]
Void GoBack ();
[DispId (0x65)]
Void GoForward ();
[DispId (0x66)]
Void GoHome ();
[DispId (0x67)]
Void GoSearch ();
[DispId (0x68)]
Void Navigate ([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers );
[DispId (-550)]
Void Refresh ();
[DispId (0x69)]
Void Refresh2 ([In] ref object level );
[DispId (0x6a)]
Void Stop ();
[DispId (200)]
Object Application {[return: financialas (UnmanagedType. IDispatch)] get ;}
[DispId (0xc9)]
Object Parent {[return: financialas (UnmanagedType. IDispatch)] get ;}
[DispId (0xca)]
Object Container {[return: financialas (UnmanagedType. IDispatch)] get ;}
[DispId (0xcb)]
Object Document {[return: financialas (UnmanagedType. IDispatch)] get ;}
[DispId (0xcc)]
Bool TopLevelContainer {get ;}
[DispId (0xcd)]
String Type {get ;}
[DispId (0xce)]
Int Left {get; set ;}
[DispId (0xcf)]
Int Top {get; set ;}
[DispId (0xd0)]
Int Width {get; set ;}
[DispId (0xd1)]
Int Height {get; set ;}
[DispId (210)]
String LocationName {get ;}
[DispId (0xd3)]
String LocationURL {get ;}
[DispId (0xd4)]
Bool Busy {get ;}
[DispId (300)]
Void Quit ();
[DispId (0x12d)]
Void ClientToWindow (out int pcx, out int pcy );
[DispId (0x12e)]
Void PutProperty ([In] string property, [In] object vtValue );
[DispId (0x12f)]
Object GetProperty ([In] string property );
[DispId (0)]
String Name {get ;}
[DispId (-515)]
Int HWND {get ;}
[DispId (400)]
String FullName {get ;}
[DispId (0x191)]
String Path {get ;}
[DispId (0x192)]
Bool Visible {get; set ;}
[DispId (0x193)]
Bool StatusBar {get; set ;}
[DispId (0x194)]
String StatusText {get; set ;}
[DispId (0x195)]
Int ToolBar {get; set ;}
[DispId (0x196)]
Bool MenuBar {get; set ;}
[DispId (0x197)]
Bool FullScreen {get; set ;}
[DispId (500)]
Void Navigate2 ([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers );
[DispId (0x1f5)]
OLECMDF QueryStatusWB ([In] olestmid statement ID );
[DispId (0x1f6)]
Void ExecWB ([In] oleadeid upload ID, [In] OLECMDEXECOPT cmdexecopt, ref object pvaIn, IntPtr pvaOut );
[DispId (0x1f7)]
Void ShowBrowserBar ([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize );
[DispId (-525)]
WebBrowserReadyState ReadyState {get ;}
[DispId (550)]
Bool Offline {get; set ;}
[DispId (0x227)]
Bool Silent {get; set ;}
[DispId (0x228)]
Bool RegisterAsBrowser {get; set ;}
[DispId (0x229)]
Bool RegisterAsDropTarget {get; set ;}
[DispId (0x22a)]
Bool TheaterMode {get; set ;}
[DispId (0x22b)]
Bool AddressBar {get; set ;}
[DispId (0x22c)]
Bool Resizable {get; set ;}
}
Class CorssDomainHelper
{
Private static Guid IID_IWebBrowserApp = new Guid ("0002DF05-0000-0000-C000-000000000046 ");
Private static Guid IID_IWebBrowser2 = new Guid ("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E ");
// Utility for IE cross domain access
// Returns null in case of failure.
Public static IHTMLDocument3 GetDocumentFromWindow (IHTMLWindow2 htmlWindow)
{
If (htmlWindow = null)
{
Return null;
}
// First try the usual way to get the document.
Try
{
IHTMLDocument2 doc = html##doc ument;
Return (IHTMLDocument3) doc;
}
Catch (COMException comEx)
{
// I think COMException won't be ever fired but just to be sure...
}
Catch (UnauthorizedAccessException)
{
}
Catch (Exception ex)
{
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.
IWebBrowser2 browser = (IWebBrowser2) (brws );
Return (IHTMLDocument3) browser. Document;
}
Catch (Exception ex)
{
Console. WriteLine (ex );
}
Return null;
}
}
}
Finally, we changed the Running code to the following form to call the GetDocumentFromWindow method in the Helper class:
Copy codeThe Code is as follows:
Using System;
Using System. Windows. Forms;
Using mshtml;
Namespace WebBrowserTest
{
Public partial class Form1: Form
{
Public Form1 ()
{
InitializeComponent ();
}
Private void button#click (object sender, EventArgs e)
{
This. webBrowser1.Navigate (this. textBox1.Text );
}
Private void button2_Click (object sender, EventArgs e)
{
Var doc = this. webBrowser1.Document;
Var frames = doc. Window. Frames;
String testValue = doc. GetElementById ("search"). GetAttribute ("value ");
IHTMLDocument3 baiduDoc = CorssDomainHelper. GetDocumentFromWindow (frames [0]. DomWindow as IHTMLWindow2 );
BaiduDoc. getElementById ("kw"). setAttribute ("value", testValue );
BaiduDoc. getElementById ("su"). click ();
}
}
}
Finally, run the program and we can see that we can get the elements on Baidu.
Let's add some questions about passing by autumn.:
In fact, I have not studied these interfaces in depth, but I can find a lot of relevant information on the Internet to introduce the differences between these interfaces. Here is a link:
Http://hi.baidu.com/christole/item/1c8dfd1a791a53643f87ced8
Then, why should I use IHMLDocument3 in the above Code, instead of the other two interfaces, because the IHMLDocument3 interface defines the getElementById method I need.
By viewing MSDN, you can find the desired attributes or methods and directly convert them into the types you need in the Code. They can be converted to each other. For example, if I have used the getElementById method, and I need to view the webpage title, I can forcibly convert the baiduDoc variable above to IHMLDocument2, and then I can directly use its title attribute.
Reference:
Http://msdn.microsoft.com/en-us/library/aa752052 (v = vs.85). aspx
Http://codecentrix.blogspot.com/2007/10/when-ihtmlwindow2getdocument-returns.html
Http://msdn.microsoft.com/en-us/library/aa752641 (v = VS.85). aspx