3

问题范围:

我正在编写一个应用程序来保存从BingGoogle搜索中检索到的 HTML。我知道有一些类可以使用诸如这个示例的流来执行 Web 请求,但是由于 Google 和 Bing 都使用 Javascript 和 Ajax 将结果呈现到 HTML 中,所以我无法简单地读取流并使用获取结果我需要。

对此的解决方案是使用 WebBrowser 类并导航到我想要的 url,以便浏览器本身将处理所有 Javascript 和 Ajax 脚本执行。

多线程:

为了提高效率,我有相同的表单应用程序为每个服务触发一个线程(一个用于 Bing,一个用于 Google)。

问题:

由于我需要 WebBrowser,因此我为每个线程(目前为 2 个)实例化了一个。根据微软的说法,有一个known bug that prevents the DocumentCompleted event from firing if the WebBrowser is not visible and is not added to a visible form aswell(有关更多信息,请点击此链接)。

真正的问题:

主要问题是,DocumentCompleted浏览器的事件永远不会触发。绝不。

DocumentCompleted我已经为永远不会得到回调的事件编写了一个适当的处理程序。为了处理浏览器事件触发所需的等待,我实现了一个具有高超时(5 分钟)的AutoResetEvent,如果它在 5 分钟后没有触发我需要的事件,它将处理 webbrowser 线程。

目前,我已经创建了浏览器并将其添加到 WindowsForm 中,两者都是可见的,并且事件仍然没有触发。

一些代码:

        // Creating Browser Instance
        browser = new WebBrowser ();

        // Setting up Custom Handler to "Document Completed" Event
        browser.DocumentCompleted += DocumentCompletedEvent;

        // Setting Up Random Form
        genericForm = new Form();
        genericForm.Width = 200;
        genericForm.Height = 200;
        genericForm.Controls.Add (browser);
        browser.Visible = true;  

至于导航我有以下(浏览器的方法):

    public void NavigateTo (string url)
    {
        CompletedNavigation = false;

        if (browser.ReadyState == WebBrowserReadyState.Loading) return;

        genericForm.Show (); // Shows the form so that it is visible at the time the browser navigates
        browser.Navigate (url);
    }

而且,对于导航的调用,我有这个:

            // Loading URL
            browser.NavigateTo(URL);

            // Waiting for Our Event To Fire
            if (_event.WaitOne (_timeout))
            {
               // Success
            }
            { // Error / Timeout From the AutoResetEvent } 

TL:博士:

我的 WebBrowser 被实例化为另一个 STAThread,添加到表单中,当浏览器导航触发时,两者都可见并显示,但来自浏览器的 DocumentCompleted 事件永远不会触发,因此 AutoResetEvent 总是超时,我没有来自浏览器的响应.

提前致谢,很抱歉发了这么长的帖子

4

1 回答 1

2

虽然这看起来很奇怪,但这是我的尝试。

var tasks = new Task<string>[]
{
    new MyDownloader().Download("http://www.stackoverflow.com"),
    new MyDownloader().Download("http://www.google.com")
};

Task.WaitAll(tasks);
Console.WriteLine(tasks[0].Result);
Console.WriteLine(tasks[1].Result);

public class MyDownloader
{
    WebBrowser _wb;
    TaskCompletionSource<string> _tcs;
    ApplicationContext _ctx;
    public Task<string> Download(string url)
    {
        _tcs = new TaskCompletionSource<string>();

        var t = new Thread(()=>
        {
            _wb = new WebBrowser();
            _wb.ScriptErrorsSuppressed = true;
            _wb.DocumentCompleted += _wb_DocumentCompleted;
            _wb.Navigate(url);
            _ctx = new ApplicationContext();
            Application.Run(_ctx);
        });

        t.SetApartmentState(ApartmentState.STA);
        t.Start();

        return _tcs.Task;
    }

    void _wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        //_tcs.TrySetResult(_wb.DocumentText);
        _tcs.TrySetResult(_wb.DocumentTitle);
        _ctx.ExitThread();
    }
}
于 2013-05-23T14:11:30.127 回答