c# - 从线程池访问 WebBrowsers

标签 c# webbrowser-control threadpool invokerequired

我正在尝试从另一个线程访问网络浏览器上的信息。尝试访问 browser.DocumentTitle 时,出现此错误:

名称 DocumentTitle 在当前上下文中不存在

我可以成功导航到 DoWorkProcessWebPage 方法内的网页,但我无法在不崩溃的情况下访问 GetTitle 函数。我已经独自在这部分工作了好几天,根本无法弄清楚。

这是问题代码:

浏览器代码

class BrowserInterface : Form
{
    WebBrowser browser;
    Thread thread;

    State state;

    public State State { get { return state; } }

    public BrowserInterface()
    {
        Initialize();
    }

    void Initialize()
    {
        browser = new WebBrowser();
        state = State.Null;
        state = State.Initializing;
        thread = new Thread(StartThread);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        while (state == State.Initializing) Thread.Sleep(20);
    }

    void StartThread()
    {
        browser = new WebBrowser();
        browser.Dock = DockStyle.Fill;
        browser.Name = "webBrowser";
        browser.ScrollBarsEnabled = false;
        browser.TabIndex = 0;
        browser.DocumentCompleted +=
            new WebBrowserDocumentCompletedEventHandler(this.Web_Completed);
        Form form = new Form();
        form.Controls.Add(browser);
        form.Name = "Browser";
        state = State.Null;
        Application.Run(form);
    }

    public void Navigate(string url)
    {
        state = State.Navigating;
        if (browser.IsDisposed)
            Initialize();
        browser.Navigate(url);
    }

    public string GetTitle()
    {
        if (InvokeRequired)
        {
            BeginInvoke(new MethodInvoker(() => GetTitle()));
        }
        return browser.DocumentTitle;
    }

    private void Web_Completed(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        var br = sender as WebBrowser;
        if (br.Url == e.Url)
            state = State.Completed;

    }
}

enum State
{
    Initializing,
    Null,
    Navigating,
    Completed
}

其他线程

class Controller
{
    public int ThreadsAllowed;

    private ManualResetEvent[] resetEvent;
    private BrowserInterface[] browser;

    static Thread mainThread;

    bool run;
    bool exit;

    public Controller(int threadsAllowed)
    {
        ThreadsAllowed = threadsAllowed;

        resetEvent = new ManualResetEvent[ThreadsAllowed];
        browser = new BrowserInterface[ThreadsAllowed];

        for (int i = 0; i < ThreadsAllowed; i++)
        {
            resetEvent[i] = new ManualResetEvent(true);
            browser[i] = new BrowserInterface();
        }

        ThreadPool.SetMaxThreads(ThreadsAllowed, ThreadsAllowed);

        mainThread = new Thread(RunThread);
        mainThread.Start();

        run = false;
        exit = false;
    }

    public void Run()
    {
        run = true;
    }

    void RunThread()
    {
        while (true)
        {
            while (!run) Thread.Sleep(20);
            while (mode == ScoutMode.Off) Thread.Sleep(100);

            //wait for the last set to complete
            WaitHandle.WaitAll(resetEvent);
            if (exit)
                break;

             for (int i = 0; i < ThreadsAllowed; i++)
             ThreadPool.QueueUserWorkItem(DoWork, i);             
        }
    }

    void DoWork(object o)
    {
        int i = (int)o;
        if(browser[i].state == State.null)
        {
            …
            … navigation code that works …
            …
            return;
        }
        else if(browser[i].state == State.Completed)    
            ProcessWebPage(i);         

    }

    void ProcessWebPage(int i)
    {
        string title;
        try
        {
            title = browser[i].GetTitle();
        }
        catch { return; }
    }
}

最佳答案

让我眼花缭乱的是你的 GetTitle 函数。使用MethodInvoker 时,您处理的是void 类型的方法,也就是说,您无法从函数中获取返回值。这就是为什么您需要一个可以返回值的委托(delegate)。

此外,您必须有 else 语句,因此实际上需要在调用时不尝试返回值。

class BrowserInterface : Form
{
    /* ... */

    private delegate string StringDelegate();

    public string GetTitle()
    {
        /*
        if (InvokeRequired)
        {
            BeginInvoke(new MethodInvoker(() => GetTitle()));
        }
        return browser.DocumentTitle;
        */

        if (InvokeRequired)
        {
            object result = Invoke(new StringDelegate(GetTitle));
            return (string)result;
        }
        else
            return browser.DocumentTitle;
    }

    /* ... */
}

关于c# - 从线程池访问 WebBrowsers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12239604/

相关文章:

c# - 'circular linked list builder' 的最快算法

c# - 系统找不到使用 X509Certificate2 指定的文件来连接 Azure 服务器中的 Google OAuth2.0

c# - 在 SQL 查询中使用 C# 变量

html - 将行添加到 webbrowser 对象中的现有表

c# - 如何使用 WebBrowser 控件打印 css 应用的背景图像

C++ POCO - 如何在不使用 run() 方法的情况下在线程池上启动线程?

Java 执行器服务线程池

c# - 使用表优化中继器的 asp.net C# 代码

c# - 在 WebBrowser 控件中鼠标处于非事件状态一定时间后隐藏鼠标光标

ruby - 为什么 Ruby 没有内置 ThreadPool?