WebBrowser Control in a new thread

前端 未结 4 1441
执笔经年
执笔经年 2020-11-21 06:24

I have a list Uri\'s that I want \"clicked\" To achieve this I\"m trying to create a new web-browser control per Uri. I create a new thread per Uri. The problem I\'m having

相关标签:
4条回答
  • 2020-11-21 06:50

    From my experience in the past the webbrowser does not like operating outside of the main application thread.

    Try using httpwebrequests instead, you can set them as asynchronous and create a handler for the response to know when it is succesfull:

    how-to-use-httpwebrequest-net-asynchronously

    0 讨论(0)
  • 2020-11-21 06:51

    A simple solution at which the simultaneous operation of several WebBrowsers occurs

    1. Create a new Windows Forms application
    2. Place the button named button1
    3. Place the text box named textBox1
    4. Set properties of text field: Multiline true and ScrollBars Both
    5. Write the following button1 click handler:

      textBox1.Clear();
      textBox1.AppendText(DateTime.Now.ToString() + Environment.NewLine);
      int completed_count = 0;
      int count = 10;
      for (int i = 0; i < count; i++)
      {
          int tmp = i;
          this.BeginInvoke(new Action(() =>
          {
              var wb = new WebBrowser();
              wb.ScriptErrorsSuppressed = true;
              wb.DocumentCompleted += (cur_sender, cur_e) =>
              {
                  var cur_wb = cur_sender as WebBrowser;
                  if (cur_wb.Url == cur_e.Url)
                  {
                      textBox1.AppendText("Task " + tmp + ", navigated to " + cur_e.Url + Environment.NewLine);
                      completed_count++;
                  }
              };
              wb.Navigate("https://stackoverflow.com/questions/4269800/webbrowser-control-in-a-new-thread");
          }
          ));
      }
      
      while (completed_count != count)
      {
          Application.DoEvents();
          Thread.Sleep(10);
      }
      textBox1.AppendText("All completed" + Environment.NewLine);
      
    0 讨论(0)
  • 2020-11-21 06:54

    You have to create an STA thread that pumps a message loop. That's the only hospitable environment for an ActiveX component like WebBrowser. You won't get the DocumentCompleted event otherwise. Some sample code:

    private void runBrowserThread(Uri url) {
        var th = new Thread(() => {
            var br = new WebBrowser();
            br.DocumentCompleted += browser_DocumentCompleted;
            br.Navigate(url);
            Application.Run();
        });
        th.SetApartmentState(ApartmentState.STA);
        th.Start();
    }
    
    void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
        var br = sender as WebBrowser;
        if (br.Url == e.Url) {
            Console.WriteLine("Natigated to {0}", e.Url);
            Application.ExitThread();   // Stops the thread
        }
    }
    
    0 讨论(0)
  • 2020-11-21 06:57

    Here is how to organize a message loop on a non-UI thread, to run asynchronous tasks like WebBrowser automation. It uses async/await to provide the convenient linear code flow and loads a set of web pages in a loop. The code is a ready-to-run console app which is partially based on this excellent post.

    Related answers:

    • https://stackoverflow.com/a/22262976/1768303
    • https://stackoverflow.com/a/21775343/1768303

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    
    namespace ConsoleApplicationWebBrowser
    {
        // by Noseratio - https://stackoverflow.com/users/1768303/noseratio
        class Program
        {
            // Entry Point of the console app
            static void Main(string[] args)
            {
                try
                {
                    // download each page and dump the content
                    var task = MessageLoopWorker.Run(DoWorkAsync,
                        "http://www.example.com", "http://www.example.net", "http://www.example.org");
                    task.Wait();
                    Console.WriteLine("DoWorkAsync completed.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("DoWorkAsync failed: " + ex.Message);
                }
    
                Console.WriteLine("Press Enter to exit.");
                Console.ReadLine();
            }
    
            // navigate WebBrowser to the list of urls in a loop
            static async Task<object> DoWorkAsync(object[] args)
            {
                Console.WriteLine("Start working.");
    
                using (var wb = new WebBrowser())
                {
                    wb.ScriptErrorsSuppressed = true;
    
                    TaskCompletionSource<bool> tcs = null;
                    WebBrowserDocumentCompletedEventHandler documentCompletedHandler = (s, e) =>
                        tcs.TrySetResult(true);
    
                    // navigate to each URL in the list
                    foreach (var url in args)
                    {
                        tcs = new TaskCompletionSource<bool>();
                        wb.DocumentCompleted += documentCompletedHandler;
                        try
                        {
                            wb.Navigate(url.ToString());
                            // await for DocumentCompleted
                            await tcs.Task;
                        }
                        finally
                        {
                            wb.DocumentCompleted -= documentCompletedHandler;
                        }
                        // the DOM is ready
                        Console.WriteLine(url.ToString());
                        Console.WriteLine(wb.Document.Body.OuterHtml);
                    }
                }
    
                Console.WriteLine("End working.");
                return null;
            }
    
        }
    
        // a helper class to start the message loop and execute an asynchronous task
        public static class MessageLoopWorker
        {
            public static async Task<object> Run(Func<object[], Task<object>> worker, params object[] args)
            {
                var tcs = new TaskCompletionSource<object>();
    
                var thread = new Thread(() =>
                {
                    EventHandler idleHandler = null;
    
                    idleHandler = async (s, e) =>
                    {
                        // handle Application.Idle just once
                        Application.Idle -= idleHandler;
    
                        // return to the message loop
                        await Task.Yield();
    
                        // and continue asynchronously
                        // propogate the result or exception
                        try
                        {
                            var result = await worker(args);
                            tcs.SetResult(result);
                        }
                        catch (Exception ex)
                        {
                            tcs.SetException(ex);
                        }
    
                        // signal to exit the message loop
                        // Application.Run will exit at this point
                        Application.ExitThread();
                    };
    
                    // handle Application.Idle just once
                    // to make sure we're inside the message loop
                    // and SynchronizationContext has been correctly installed
                    Application.Idle += idleHandler;
                    Application.Run();
                });
    
                // set STA model for the new thread
                thread.SetApartmentState(ApartmentState.STA);
    
                // start the thread and await for the task
                thread.Start();
                try
                {
                    return await tcs.Task;
                }
                finally
                {
                    thread.Join();
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题