代码之家  ›  专栏  ›  技术社区  ›  Art W

新线程中的WebBrowser控件

  •  80
  • Art W  · 技术社区  · 15 年前

    我有一个列表URI,我想“点击”来实现这个目标,我正在尝试为每个URI创建一个新的Web浏览器控件。我为每个URI创建一个新线程。我遇到的问题是在文档完全加载之前线程结束,所以我永远无法利用DocumentComplete事件。我怎样才能克服这个问题?

    var item = new ParameterizedThreadStart(ClicIt.Click); 
    var thread = new Thread(item) {Name = "ClickThread"}; 
    thread.Start(uriItem);
    
    public static void Click(object o)
    {
        var url = ((UriItem)o);
        Console.WriteLine(@"Clicking: " + url.Link);
        var clicker = new WebBrowser { ScriptErrorsSuppressed = true };
        clicker.DocumentCompleted += BrowseComplete;
        if (String.IsNullOrEmpty(url.Link)) return;
        if (url.Link.Equals("about:blank")) return;
        if (!url.Link.StartsWith("http://") && !url.Link.StartsWith("https://"))
            url.Link = "http://" + url.Link;
        clicker.Navigate(url.Link);
    }
    
    4 回复  |  直到 9 年前
        1
  •  140
  •   Hans Passant    15 年前

    您必须创建一个sta线程,该线程会产生一个消息循环。对于像WebBrowser这样的ActiveX组件来说,这是唯一友好的环境。否则将无法获取DocumentCompleted事件。一些示例代码:

    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
        }
    }
    
        2
  •  24
  •   Community Mohan Dere    9 年前

    以下是如何在非UI线程上组织消息循环,以运行异步任务,如 WebBrowser 自动化。它使用 async/await 提供方便的线性代码流并在循环中加载一组网页。该代码是一个随时可以运行的控制台应用程序,部分基于 this excellent post .

    相关答案:

    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();
                }
            }
        }
    }
    
        3
  •  3
  •   Community Mohan Dere    9 年前

    根据我过去的经验,WebBrowser不喜欢在主应用程序线程之外操作。

    尝试使用httpwebrequests,您可以将其设置为异步的,并创建响应的处理程序,以了解响应何时成功。完整:

    how-to-use-httpwebrequest-net-asynchronously

        4
  •  0
  •   Ramil Shavaleev    9 年前

    一种简单的解决方案,其中多个WebBrowser同时运行。

    1. 创建新的Windows窗体应用程序
    2. 放置名为的按钮 钮扣1
    3. 放置名为text box 1的文本框
    4. 设置文本属性 字段:多行真和滚动条
    5. 写下以下内容 按钮1单击处理程序:

      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);