起因:程序原来是定时5分钟监测一个网页A,并进行相应处理C,后来要加入对另一个网页B的监测,而新网页B是flash,flash定时10秒刷新数据,原来的处理流程C没问题,但发现过一段时间后,网页B停止了更新数据相应,经过分析发现是处理流程C阻塞了网页B的响应,于是便考虑处理流程C放到子线程里,而处理流程C需要访问网页A,从而抛出了错误“在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级”、“指定的转换无效。”
网上搜索,比较有用及简单明了的有:
解决多线程操作控件时可能出现的异常:“在某个线程上创建的控件不能成为在另一个线程上创建的控件的父级”
c#_在多线程下访问WebBrowser对象报异常,"指定的转换无效。" 这个帖子还有大伽18楼微软MVP蒋晟 说:“ Windows Forms控件不支持跨线程调用,IE的很多接口也不支持。 ” 23楼绿色夹克衫 :其他线程调用WebBrowser的话应该还是需要Invoke给创建的线程,另外创建线程也需要是STA的。从微软自己的实现来看,用多进程似乎更好一些,需要自己写一下进程通讯。
后来发现是可以跨线程访问的,既没有某些帖子说的不能用Threading.Timer定时器,也不会说不能访问“注:这里的WebBrowser要从代码中new,如果是拉控件的方式创建的,将会报错:"指定的转换无效"”的问题,现将前人经验整理,方便自己查阅。
Imports System.Threading
Public Class Form1
'//子线程里调用UI线程执行InvokeFun()
'访问WebBrowser控件抛出错误:
'System.InvalidCastException:“指定的转换无效。”
'HResult = 0x80004002
'访问TextBox控件抛出错误:
'System.InvalidOperationException:“线程间操作无效: 从不是创建控件“TextBox1”的线程访问它。”
'HResult = 0x80131509
'//1、设置子线程函数
Private Sub ThreadFun()
'MethodInvoker不带参数
Dim test As MethodInvoker = New MethodInvoker(AddressOf InvokeFun) '通过委托执行
Me.Invoke(test) '同步
'Me.BeginInvoke(test) '也可以用异步执行指定的委托
End Sub
'//2、设置UI线程里要执行的函数
Private Sub InvokeFun()
TextBox1.Text = Now
MsgBox(WebBrowser1.Document.All.Count)
End Sub
'//3、调用子线程
'可以使用线程定时器启动子线程
Dim ThreadingTimer1 As Threading.Timer = New Threading.Timer(New TimerCallback(AddressOf ThreadFun), Nothing, 6000, 6000)
'可以通过按钮启动子线程
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim thdProcess As Thread = New Thread(New ThreadStart(AddressOf InvokeDeThreadFun))
thdProcess.Start()
End Sub
'用Delegate可以带参数
Private Delegate Sub DeThreadFun()
Private Sub InvokeDeThreadFun()
Dim useDeThreadFun As DeThreadFun = New DeThreadFun(AddressOf InvokeFun)
Me.Invoke(useDeThreadFun)
End Sub
'带参数
Private Sub ThreadFun2()
Dim Tasks As New TasksClass()
Tasks.wb = WebBrowser1
Dim test As MethodInvoker = New MethodInvoker(AddressOf Tasks.SomeTask)
Me.Invoke(test)
End Sub
'需要传递参数,则封装到Class里
Class TasksClass
Friend wb As WebBrowser
Sub SomeTask()
MsgBox(wb.Document.All.Count)
End Sub
End Class
End Class