none
c# webbrowser 在线程里调用的问题? RRS feed

  • 问题

  • 我今天我写一个程序,创建了2个线程,需要在线程里多次循环调用webbrowser,但是出现异常,好像是不让调用?不知道有没有这方便的高手,在多线程里怎么调用webbrowser????

    //////////////////////////////////////
    未处理 System.InvalidCastException
    Message="指定的转换无效。"
    Source="System.Windows.Forms"
    StackTrace:
    在 System.Windows.Forms.UnsafeNativeMethods.IHTMLDocument2.GetLocation()
    在 System.Windows.Forms.WebBrowser.get_Document()
    在 Xiaonei_Helper.Form1.filll() 位置 E:\Documents\Visual Studio 2008\Projects\Xiaonei Helper\Xiaonei Helper\Form1.cs:行号 76
    在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    在 System.Threading.ThreadHelper.ThreadStart()
    InnerException:
    /////////////////////////////////////
    2010年1月8日 13:30

答案

  •   this.webBrowser1.Refresh();   应该改成 Invoke 方式的。后台线程不能直接跨线程访问界面控件的方法操纵界面控件,否则会产生不可预期的异常。 请参考:
    http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx 的方法,用Invoke的方式调用Refresh 方法。

    另外,定时刷新网页的动作,一般不在客户端的WebBrowser 控件里面做,典型的做法是在Web页面的<meta> 标记里面声明刷新间隔,这样WebBrower 就会自动定时刷新网页了。请参考:
    http://www.cnblogs.com/jerryshi/archive/2008/10/14/1310611.html
    2010年1月9日 9:13
  • 你再检查你的方法里面是不是用到了字符串或者数字类型转换,
    可能是转换地方出了错误,
    努力+方法=成功
    2010年1月9日 10:52

全部回复

  • 你这个在 Xiaonei_Helper.Form1.filll() 位置 E:\Documents\Visual Studio 2008\Projects\Xiaonei Helper\Xiaonei Helper\Form1.cs:行号 76 附近的代码是怎样的?fill() 方法做了什么?

    WebBrowser 是界面控件,线程之间调用时有没有用Invoke 方式?
    • 已建议为答案 zbjuke 2012年7月5日 6:31
    • 取消建议作为答案 zbjuke 2012年7月5日 6:31
    2010年1月8日 14:11
  •         private void DoRefresh()
            {
                try
                {
                    while (!Stopped)
                    {
                        this.webBrowser1.Url = new Uri(SUrl2 + refmobile);
                        while (webBrowser1.ReadyState == WebBrowserReadyState.Loading)
                        {
                            Application.DoEvents();
                        }
                        this.webBrowser1.Refresh();
                        Thread.Sleep(readInterval);
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    //Stopped = true;
                    //记录异常日志
                    string errinfo = System.DateTime.Now.ToLongDateString() + " " + System.DateTime.Now.ToLongTimeString() + "Dorefresh刷新页面,系统异常原因:" + ex;
                    Conrecord(errinfo);
                }
            }

    //创建一个刷新的线程
                Thread tRefresh = new Thread(new ThreadStart(DoRefresh));
                tRefresh.IsBackground = true;
                tRefresh.Start();
    我的代码是这样的,谢谢大哥,你再帮我看看,谢谢
    2010年1月9日 8:44
  •   this.webBrowser1.Refresh();   应该改成 Invoke 方式的。后台线程不能直接跨线程访问界面控件的方法操纵界面控件,否则会产生不可预期的异常。 请参考:
    http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx 的方法,用Invoke的方式调用Refresh 方法。

    另外,定时刷新网页的动作,一般不在客户端的WebBrowser 控件里面做,典型的做法是在Web页面的<meta> 标记里面声明刷新间隔,这样WebBrower 就会自动定时刷新网页了。请参考:
    http://www.cnblogs.com/jerryshi/archive/2008/10/14/1310611.html
    2010年1月9日 9:13
  • 你再检查你的方法里面是不是用到了字符串或者数字类型转换,
    可能是转换地方出了错误,
    努力+方法=成功
    2010年1月9日 10:52
  • 谢谢以上兄弟,问题解决了,是委托写的有问题
    2010年1月19日 2:23
  • private void button3_Click(object sender, EventArgs e)
            {
                t1 = new Thread(new ThreadStart(A1));
                t1.Start();//线程1启动
            }
    private void A1()
    {
        webBrowser1.Navigate(address);
        Thread.Sleep(1000);
         HtmlElement GG= webBrowser1.Document.GetElementById("id1");   //执行到这行时,提示"指定的转换无效"的错误!
       }
    }



    请问这是什么原因?
    要怎样处理这样情况?

    ***********
    2010年1月21日 8:03
  • 估计是webbrower还没有加载完网页的问题
    你把Thread.Sleep(1000);
    改成
    Application.DoEvents();


    确保你的网页里面有 id1这个DOM对象
    努力+方法=成功
    2010年1月21日 8:07
  • 不能在线程操作


    http://feiyun0112.cnblogs.com/
    2010年1月21日 8:28
    版主
  • 还要注意非主线程内操作主线程 UI 对象时请记住需要使用 Invoke 方法。
    Mark Zhou
    2010年1月21日 9:17
  • 你好!

    首先确定 Id 为 id1 的控件是存在。然后将下面的代码改到 webBrowser 的 DocumentCompleted 事件中。

    HtmlElement GG= webBrowser1.Document.GetElementById("id1"); 

    知识改变命运,奋斗成就人生!
    2010年1月22日 1:39
    版主
  • 你好,

    在其它线程中调用主线程的控件要用到Invoke。可以参考下面的代码试试。

     private void button4_Click(object sender, EventArgs e)
            {
                Thread t = new Thread(GetElement);
                t.Start();
    
            }
            protected void GetElement()
            {
                Action<string> test = s => GetElement1(s);
                this.Invoke(test,"test");
               
               
            }
    
            protected void GetElement1(string s)
            {
                HtmlElement he = this.webBrowser1.Document.GetElementById("divSelect");
                MessageBox.Show(he.InnerText);
            }

    Microsoft Online Community Support
    2010年1月22日 6:07
  • 谢谢各位的回复!

    我再试下看看。。。


    ***********
    2010年1月22日 6:10
  • private void button3_Click(object sender, EventArgs e)
            {
                t1 = new Thread(new ThreadStart(A1));
                t1.Start();//线程1启动
            }
    private void A1()
    {
        webBrowser1.Navigate(address);
        Thread.Sleep(1000);
         HtmlElement GG= webBrowser1.Document.GetElementById("id1");   //执行到这行时,提示"指定的转换无效"的错误!
       }
    }



    请问这是什么原因?
    要怎样处理这样情况?

    ***********
    你跨线程访问UI控件的方法了,当然报告异常了。 应该用Invoke 的方式调用Navigate 方法。别说WebBrowser了,就是一个TextBox的Text属性,你都不能在后台线程里面直接TextBox1.Text = "abc"; 这么调用。

    2010年1月22日 6:34
  • 你好!
         实际上我一直很奇怪,跨线程访问控件应该出现的异常是:“从不是创建控件WebBrowser1的线程访问它。”,不应该是这个异常啊!

    周雪峰
    2010年1月22日 8:13
    版主
  • 我觉得这里可能是原因是页面未加载完成或页面上没有是找的控件,但不管怎么样线程中调用窗体控件都应该使用 Invoke,以避免不必要的问题。
    知识改变命运,奋斗成就人生!
    2010年1月22日 8:18
    版主
  • 参考下面的代码。

    using System;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace Demos.Winforms.ControlDemo.WebBrowerDemo
    {
        public partial class Form001 : Form
        {
            public Form001()
            {
                InitializeComponent();
                this.webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
                this.InitDemo();
            }
    
            public void InitDemo()
            {
                Thread t1 = new Thread(new ThreadStart(A1));
                t1.Start();
            }
    
            public delegate void ClientDelegate();
    
            private void A1()
            {
                // 使用 BeginInvoke 或 Invoke 的方式
                this.BeginInvoke(new ClientDelegate(delegate { webBrowser1.Navigate("http://localhost:3474/Tmp/WebForm001.aspx"); }));
                Thread.Sleep(10001);
            }
    
            void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
            {
                // 在页面加载完成时再获取
                HtmlElement GG = webBrowser1.Document.GetElementById("div1");
                GG.InnerText = String.Format("hello {0}", DateTime.Now);
            }
        }
    }


    知识改变命运,奋斗成就人生!
    2010年1月22日 8:19
    版主
  • 我觉得这里可能是原因是页面未加载完成或页面上没有是找的控件,但不管怎么样线程中调用窗体控件都应该使用 Invoke,以避免不必要的问题。
    知识改变命运,奋斗成就人生!

    我也赞成你的说法。要使用线程就用Invoke就可以了。。
    ***********
    2010年1月22日 8:31
  • 参考下面的代码。

    using System;
    
    using System.Threading;
    
    using System.Windows.Forms;
    
    
    
    namespace Demos.Winforms.ControlDemo.WebBrowerDemo
    
    {
    
        public partial class Form001 : Form
    
        {
    
            public Form001()
    
            {
    
                InitializeComponent();
    
                this.webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
    
                this.InitDemo();
    
            }
    
    
    
            public void InitDemo()
    
            {
    
                Thread t1 = new Thread(new ThreadStart(A1));
    
                t1.Start();
    
            }
    
    
    
            public delegate void ClientDelegate();
    
    
    
            private void A1()
    
            {
    
                // 使用 BeginInvoke 或 Invoke 的方式
    
                this.BeginInvoke(new ClientDelegate(delegate { webBrowser1.Navigate("http://localhost:3474/Tmp/WebForm001.aspx"); }));
    
                Thread.Sleep(10001);
    
            }
    
    
    
            void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    
            {
    
                // 在页面加载完成时再获取
    
                HtmlElement GG = webBrowser1.Document.GetElementById("div1");
    
                GG.InnerText = String.Format("hello {0}", DateTime.Now);
    
            }
    
        }
    
    }
    
    


    知识改变命运,奋斗成就人生!



    该结贴了。。。
    ***********
    • 已标记为答案 mick122 2010年1月22日 8:32
    • 取消答案标记 KeFang Chen 2010年1月22日 8:41
    2010年1月22日 8:32
  • 这个错误信息的描述是有点问题的。实际上COM返回的错误应该是E_NOINTERFACE,因为对应的接口没有注册用于跨线程列集的代理存根。CLR给E_NOINTERFACE的封装是一个cast exception。

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2010年1月23日 22:23
    版主