Introduction to C's WebBrowser operation and precautions

  • 2020-05-09 19:13:29
  • OfStack

1. To use WebBrowser in Winform, add 1 to Form1.cs:
      1.1 above "public partial class Form1: Form", add:


[PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
[System.Runtime.InteropServices.ComVisibleAttribute(true)]

    1.2 in the Shown event of Form1, add:


this.UI_webBrowser.ObjectForScripting = this;

2. Since WebBrowser is placed in the Winform interface, which is managed by the interface thread (the main thread), and the rendering execution is also the main thread, the business logic should not be placed in the main thread, another thread should be opened to execute the business logic. And interact with WebBrowser via Invoke.

  examples:


private void Form1_Shown(object sender, EventArgs e)
 {
     this._thread_mainLogic = new Thread(this.ThreadFunction_MainLogic);
     this._thread_mainLogic.Start();
 }

 private void ThreadFunction_MainLogic()
 {
     Debugger.Log(0, "", "\r\n Start executing the business logic \r\n");
     this.Invoke( new Action( () => { this.webBrowser.Navigate("http://www.baidu.com");} ) );// through Invoke with webBrowser interaction 
     .....
 }

3. Browse to specify URL. Note that this method is asynchronous and requires manual synchronization.


// The following methods are not thread-safe 
 private AutoResetEvent _threadControlEvent_Tool_webBrowser_Navigate = null;

 private void Tool_webBrowser_Navigate(string arg_URL)
 {
     this._threadControlEvent_Tool_webBrowser_Navigate = new AutoResetEvent(false);
     this.Invoke(new Action(() =>
     {
         this.webBrowser.DocumentCompleted += webBrowser_DocumentCompleted_Tool_webBrowser_Navigate;
         this.webBrowser.Navigate(arg_URL);
     }));
     this._threadControlEvent_Tool_webBrowser_Navigate.WaitOne();
     this._threadControlEvent_Tool_webBrowser_Navigate.Close();
     this._threadControlEvent_Tool_webBrowser_Navigate.Dispose();
 }

 void webBrowser_DocumentCompleted_Tool_webBrowser_Navigate(object sender, WebBrowserDocumentCompletedEventArgs e)
 {
     this.webBrowser.DocumentCompleted -= webBrowser_DocumentCompleted_Tool_webBrowser_Navigate;
     this._threadControlEvent_Tool_webBrowser_Navigate.Set();
 }

4. Get the ID button and click on it :(also works on the URL link in the web page)


// So let's say that the button in the web page, ID for "btn"
HtmlElement element_btn = null;
this.Invoke(new Action(() => { element_btn = this.UI_webBrowser.Document.All["btn"]; }));// To obtain 
element_btn.InvokeMember("Click");// Click, this method is a synchronous method, can be used safely 

5. Get the input box according to ID and enter the content


// So let's say that the input box in the web page, ID for "input"
HtmlElement input = null;
this.Invoke( new Action( () => { input = this.UI_webBrowser.Document.All["input"]; } ) );// To obtain 
input.InnerText = "123";// The input "123" . This method is not a synchronous method and needs to use the following Wait_SafeMode Methods. 
Tool_Wait_SafeMode();// Implemented below 

6. Get form according to ID and submit (submit)


// Let's say it's in a web page form . ID for "form2"
HtmlElement form2 = null;
this.Invoke( new Action( () => { form2 = this.UI_webBrowser.Document.Forms["form2"]; } ) );// To obtain 
form_submit.InvokeMember("submit");// submit form2 The contents of. This method is a synchronous method and can be used safely. 

7. Get CheckBox according to ID and set it to selected (Checked)


// Let's say it's in a web page CheckBox . ID for "checkbox5"
HtmlElement checkBox5 = null;
this.Invoke( new Action( () => { checkBox5 = this.UI_webBrowser.Document.All["checkbox5"]; } ) );// To obtain 
checkBox5.SetAttribute("Checked", "true");// Set to selected. This method is a synchronous method and can be used safely. 


8. Find an element based on its known attributes


// Suppose there is, and only, such a thing on a web page 1 Elements: it has 1 called "value" , the value of which is "12345"
 bool isFind = false;
 HtmlElementCollection htmlElementCollection = null;
 this.Invoke( new Action( () => { htmlElementCollection = this.webBrowser.Document.All; } ) );// For collection 
 HtmlElement resultElement = null;

 foreach (HtmlElement currentElement in htmlElementCollection)// Iterate through all the elements in the collection to find them 
 {
     if (currentElement.GetAttribute("value") == "12345")
     {
         isFind = true;
         resultElement = currentElement;
         break;
     }
 }

 if( ! isFind )
 {
      Deal with the missing ;
 }


9. Set ComboBox on the page. Note: please do not use the following code as there is a problem. Since SetAttribute is an unresponsive API, it is recommended to use js for setup. In the following, having WebBrowser execute the js code will allow for callbacks.


// Let's say it's in the web page 1 a ComboBox . ID for "comboBox123" , the drop-down menu has two items: 
 // The first 1 item ID for 1 . value for " apple "
 // The first 2 item ID for 2 . value for " watermelon "
 HtmlElement element_comboBox = null;
 this.Invoke( new Action( () => { element_comboBox = this.webBrowser.Document.All["comboBox123"]; } ) );// To obtain 
 Tool_Wait_SafeMode();
 this.Invoke( new Action( () => { element_comboBox.SetAttribute("value", "2"); } ) );// Set to " watermelon " , i.e., value = 2
 Tool_Wait_SafeMode();

10.Tool_Wait_SafeMode


private void Tool_Wait_SafeMode()
 {
     bool isError = false;
     bool isBusy = false;
     do
     {
         this.Invoke(new Action(() => 
         {
             try
             {
                 isBusy = this.webBrowser.IsBusy;
             }
             catch (System.Exception ex)
             {
                 isError = true;
             }
         }));
         if (isError)
         {
             Thread.Sleep(errorWaitTime);// Suggestions for 2 Seconds or more. This time should be set according to the machine performance, must be set long 1 Some. 
         }
         else
         {
             if (isBusy)
             {
                 Thread.Sleep(arg_waitTime);// Suggestions for 0.1 Seconds or more. This time should be set according to the machine performance, can be set short 1 Some. 
             }
         }
     }
     while (isError | isBusy);
 }

11. Execute the js code on the web page

      this functionality is somewhat complicated because having WebBrowser perform js is an asynchronous process and requires a callback. This is encapsulated as a synchronization process for ease of use:


#region private void Tool_webBrowser_ExecUserJSScript(string arg_jsCodes)
         private AutoResetEvent _threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init = null;
         private AutoResetEvent _threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec = null;
         private object _returnObj_Tool_webBrowser_ExecUserJSScript = null;

         /// <summary>
         ///  with WebBrowser perform JS Custom statements. 
         /// 1 Definition: 1 a js Method, the method name should be as special as possible, so as not to html Existing in js Method rename. At the end of this method, 1 Need to use window.external.NotifyCSharpComplete( msg ); To achieve js Upon completion of the execution, the notification CSharp . Pass the method to the parameter arg_jsFunctionDefineCodes . 
         /// 2 : passes the method name of the method to the parameter arg_jsFunctionName . 
         /// 3:  Pass this method, the parameters that you want to pass, to arg_functionArgs . If no parameters are passed in, the field may not be assigned, or may be assigned to null , or is assigned to new object[]{} . 
         /// 4:  if js In the callback C# When you do not need to return the parameter, please js In the method window.external.NotifyCSharpComplete( null ); If there is a return parameter, you can modify it to window.external.NotifyCSharpComplete(  Parameters of the variable  );
         ///  Example: js Methods: function jsFunctionTest( arg1, arg2 ) { var arg3 = arg1 + arg2; window.external.NotifyCSharpComplete( " Calculation results: " + arg3 ); }
         ///  the  arg_jsFunctionDefineCodes = "function jsFunctionTest( arg1, arg2 ) { var arg3 = arg1 + arg2; window.external.NotifyCSharpComplete( \" Calculation results: \" + arg3 ); }";
         ///    arg_jsFunctionName = jsFunctionTest
         ///     If the parameter to be passed is 123 , 456 , arg_functionArgs = new object[] { 123, 456 }
         ///  Return the value through object Make a return. if object is 1 Other types, please convert yourself. Such as: stirng result = (string)Tool_webBrowser_ExecUserJSScript(...);
         /// </summary>
         /// <param name="arg_jsFunctionDefineCodes">js Method, note that the total length cannot exceed 1991 The total length must not exceed 2048 , the program will add to the string 1 Something.) </param>
         /// <param name="arg_jsFunctionName">js The method name of the method </param>
         /// <param name="arg_functionArgs">js The parameter list for the method. If no parameters are passed in, the field may not be assigned, or may be assigned to null , or is assigned to new object[]{}</param>
         /// <returns> Returns the result of the execution. Note that the default is to return the parameter. If it does not, modify it js The method, NotifyCSharpComplete( msg ) Instead of NotifyCSharpComplete( null )</returns>
         private object Tool_webBrowser_ExecUserJSScript(string arg_jsFunctionDefineCodes, string arg_jsFunctionName, object[] arg_functionArgs = null)
         {
             this._returnObj_Tool_webBrowser_ExecUserJSScript = null;
             if (arg_jsFunctionDefineCodes.Length > 1991)
             {
                 throw new Exception(" Error: js The length of the method definition exceeds 1991 . ");
             }
             //1. write js Methods. 
             arg_jsFunctionDefineCodes = "javascript:" + arg_jsFunctionDefineCodes + ";window.external.NotifyCSharpCompleteInit();";
             if (arg_jsFunctionDefineCodes.Length >= 2048)
             {
                 throw new Exception(" Error: js The total length of the method definition exceeds 2048 (original method  +  Add content). ");
             }
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init = new AutoResetEvent(false);
             this.Invoke(new Action(() =>
             {
                 this.webBrowser.Navigate(arg_jsFunctionDefineCodes);
             }));
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init.WaitOne();
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init.Close();
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init.Dispose();
             //2. perform js methods 
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec = new AutoResetEvent(false);
             this.Invoke(new Action(() =>
             {
                 this.webBrowser.Document.InvokeScript(arg_jsFunctionName, arg_functionArgs);
             }));
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec.WaitOne();
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec.Close();
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec.Dispose();
             //3. Returns the parameter 
             return this._returnObj_Tool_webBrowser_ExecUserJSScript;
         }

         public void NotifyCSharpCompleteInit()
         {
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Init.Set();
         }

         public void NotifyCSharpComplete(object arg_obj)
         {
             this._returnObj_Tool_webBrowser_ExecUserJSScript = arg_obj;
             this._threadControlEvent_Tool_webBrowser_ExecUserJSScript_Exec.Set();
         }
         #endregion

Usage example 1:


string jsCmdTest = "function testFunction( msg ) { setTimeout(\"window.external.NotifyCSharpComplete(\\\" Returns the content \\\");\", 5000);};";
object returnObj = this.Tool_webBrowser_ExecUserJSScript(jsCmdTest, "testFunction", new object[] {" Incoming parameters "});
string returnStr = returnObj as string;

Usage example 2:


string jsCmdTest = "function testFunction( ) { var a = 122; var b = 244; var c = a + b; window.external.NotifyCSharpComplete(c);};";
object returnObj = this.Tool_webBrowser_ExecUserJSScript(jsCmdTest, "testFunction", null);
int returnInt = (int)returnObj;

Example 3:


string jsCmdTest = "function testFunction( ) { window.external.NotifyCSharpComplete(null);};";
object returnObj = this.Tool_webBrowser_ExecUserJSScript(jsCmdTest, "testFunction", null);
string result = "js completed ";

Summary: two big problems with WebBrowser:

1.WebBrowser is to invoke IE on the machine, so the version and rendering program also depend on the version and renderer program of IE.

2. The execution of WebBrowser, js and many other operations are asynchronous and have no event response, so you can only estimate one execution time by yourself and wait. Besides, the waiting time 1 must be greater than the actual execution time of js, otherwise problems will occur in the subsequent code.

3. Currently, js can only be executed through the browser's address bar. The address bar is limited in length.


Related articles: