Доступ к веб-браузерам из Threadpool

Я пытаюсь получить доступ к информации в веб-браузере из другого потока. При попытке получить доступ к browser.DocumentTitleЯ получаю эту ошибку:

The name DocumentTitle does not exist in the current context

Я могу успешно перейти на веб-страницы внутри DoWork или же ProcessWebPage методы, но я не могу получить доступ к 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 ответа

Решение

Что болит мой глаз, так это твой 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;
    }

    /* ... */
}

Во-первых, используйте браузер вызова вместо форм один. И главная проблема в том, что после вызова вы вернетесь к коду и попытаетесь получить доступ к browser.DocumentTitle в качестве фонового потока. Чтобы избежать этого, добавьте еще конструкцию.

public string GetTitle()
{
    if (this.browser.InvokeRequired)
    {
        this.browser.Invoke(new MethodInvoker(() => GetTitle()));
    }
    else
    {
        return browser.DocumentTitle;
    }
}
Другие вопросы по тегам