WebBrowser 控件:“指定的强制转换无效。”

问题描述 投票:0回答:3

我使用 WebBrowser 控件导航到 WordPress 博客的登录页面。 页面加载正常,但每当我尝试从线程访问 WebBrowser 时。我得到一个特定的强制转换无效的异常。此外,在调试时,一切都会冻结大约 5 秒。调试时,我尝试访问控件。我的所有成员变量都出现超时错误。

//in constructor of main form
Thread.CurrentThread.ApartmentState = ApartmentState.STA;
this.CheckForIllegalCrossThreadCalls = false;

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

private void mainThread()
{
    wbMain.Navigate("http://example.com/");

    //navigating is set to false in the document complete event.
    navigating = true;

    while (navigating == true)
        Thread.Sleep(5000);

    try
    {
        //Where I get the issues
        MessageBox.Show(wbMain.DocumentText);
    }
    catch (Exception e)
    {

    }

    Thread.Sleep(1000);
}
c# .net winforms webbrowser-control
3个回答
9
投票

WebBrowser 是一个底层的 COM 组件。 作为一种单元线程的 COM,COM 负责以线程安全的方式调用其方法。 您的 Navigate() 调用正是出于这个原因,它实际上是在 UI 线程上执行的。 不起作用的是 DocumentText 属性,它是在 .NET 包装器中实现的,并且他们在某种程度上弄乱了代码。 当 CLR 中的 COM 互操作支持注意到 MTA 中的线程尝试访问 STA 上的组件的属性时,它就会崩溃。

您对 SetApartmentState() 的调用不正确。 它是在错误的线程上创建的,UI 线程已经是 STA。 这也是它不会崩溃的原因,在线程启动后您无法更改线程的单元状态。 您必须在您创建的 Thread 对象上调用它。 还是没解决你的问题,两个STA线程不兼容。

解决问题的两种基本方法。 第一个是在单独的 STA 线程上创建 WebBrowser 对象本身。 这个答案中的代码向您展示了如何做到这一点。

您以这种方式创建的浏览器也无法在您的表单上可见。 这是第二种方法,使用 Control.Invoke() 自行编组调用。 然而,这样做是毫无意义的,无论如何,所有代码都在 UI 线程上执行,你不会获得并发性。 这里没有免费的午餐。 在线程上运行它只会让你头疼。 如果您需要时间处理文档文本,请在单独的线程上运行 that 代码。


1
投票

为什么在使用 WebBrowser 控件时不使用事件模型?

正在导航已导航进度已更改文档已完成


0
投票

您有一个“标准跨线程异常”,当从非“UI 线程”访问时,这是所有 UI 控件所共有的。 诀窍在于,与按钮、文本框等其他控件不同,微软不会为 WebBrowser 控件发送标准跨线程异常...

您只需通过标准委托将代码委托给主 UI 线程,就像您应该对另一个线程的所有控件执行的操作一样:

    private readonly WebBrowser _browser;


        Action action = () => 
        {
            while (_browser.IsBusy)
            {
                Thread.Sleep(1000);
            }
            while (_browser.ReadyState != WebBrowserReadyState.Complete)
            {
                Thread.Sleep(1000);
            }
            foreach (HtmlElement element in _browser.Document.All)
            {
                string currentTag = element.TagName.ToUpper();
                _log.Debug("element: " + currentTag);
                if (currentTag== "INPUT" || 
                    currentTag == "BUTTON" ||
                    currentTag == "SELECT")
                {
.....

                }
            }
        };
        _browser.Invoke(action);
© www.soinside.com 2019 - 2024. All rights reserved.