从Threadpool访问WebBrowsers

时间:2012-09-02 20:43:17

标签: c# webbrowser-control threadpool invokerequired

我正在尝试从其他线程访问Web浏览器上的信息。在尝试访问browser.DocumentTitle时,我收到此错误:

The name DocumentTitle does not exist in the current context

我可以成功导航到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; }
    }
}

2 个答案:

答案 0 :(得分:1)

我的眼睛是你的GetTitle功能。使用MethodInvoker时,您正在处理void类型的方法,也就是说,您无法从函数中获取返回值。这就是为什么你需要一个会给你回报价值的代表。

此外,您必须拥有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;
    }

    /* ... */
}

答案 1 :(得分:0)

首先,使用浏览器调用而不是表单。并且在调用之后你将返回代码并尝试访问browser.DocumentTitle作为后台线程的主要问题。为避免这种情况,请添加其他构造。

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