I am trying to completely disable illegal crossthreads checking with
CheckForIllegalCrossThreadCalls = False
I ended up looking for this after i realized that i wasn't able to have multiple tabs in a TabControl with multiple WebBrowser controls and changing the GUI -the WebBrowser control actually- in all tabs at the same time.
The problem is that i need CheckForIllegalCrossThreadCalls to be completely disabled but it seems that i get a cross-thread error even if i put it in my code. Is there anything extra, like a setting or something that i should tweak?
Disabling CheckForIllegalCrossThreadCalls is really not a good idea. Why don't you just make your code thread-safe instead?
It's actually easier than a lot think. What you have to do is call a method which checks the InvokeRequired property of the form. If it returns True, the function will invoke itself and then execute the specified task.
Here's how you'd do it in .NET 4.0 or higher:
Public Sub InvokeIfRequired(ByVal Method As Action)
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(Sub() InvokeIfRequired(Method)) 'Invoke this method to make it thread-safe.
Else
Method.Invoke() 'Execute the specified method.
End If
End Sub
And here's how you'd do it in .NET 3.5 or lower:
Delegate Sub InvocationDelegate(ByVal Method As Action)
Public Sub InvokeIfRequired(ByVal Method As Action)
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(New InvocationDelegate(AddressOf InvokeIfRequired), Method) 'Invoke this method to make it thread-safe.
Else
Method.Invoke() 'Execute the specified method.
End If
End Sub
Example usage:
.NET 4.0 or higher:
'Thread-safely sets Label1's text.
InvokeIfRequired(Sub() Label1.Text = "Hello World!")
.NET 3.5 or lower:
'Thread-safely sets Label1's text.
InvokeIfRequired(AddressOf SetNewText)
...further down in code...
Private Sub SetNewText()
Label1.Text = "Hello World!"
End Sub
Related
I made a file search program in visual studio on windows 10 using .net lang,
My problem starts from form1 with a "dim frm2 as form2 = new form2" call,
after the new form being shown i start a while loop on form1 that feeds data into a listbox in form 2:
1)form1 call form2 and show it.
2)form1 start a while loop.
3)inside the while loop data being fed to listbox1 in frm2
Now everything works on windows 10, the while loop can run as much as it needs without any trouble, the window can loose focus and regain focus without showing any "Not Responding.." msgs or white\black screens..
But, when i take the software to my friend computer which is running windows 7, install all required frameworks and visual studio itself, run it from the .sln in debug mode, and do the same search on the same folder the results are:
1) the while loop runs smoothly as long as form 2 dont loose focus
(something that doesnt happen on windows 10)
2) when i click anywhere on the screen the software loose focus what
causes 1) to happen (black screen\white screen\not responding etc..)
3) if i wait the time needed for the loop and dont click anywhere else
it keeps running smoohtly, updating a label like it should with the
amount of files found.. and even finish the loop with 100% success
(again unless i click somewhere)
Code Example:
Sub ScanButtonInForm1()
Dim frm2 As Form2 = New Form2
frm2.Show()
Dim AlreadyScanned As HashSet(Of String) = New HashSet(Of String)
Dim stack As New Stack(Of String)
stack.Push("...Directoy To Start The Search From...")
Do While (stack.Count > 0)
frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
frm2.Label4.Refresh()
Dim ScanDir As String = stack.Pop
If AlreadyScanned.Add(ScanDir) Then
Try
Try
Try
Dim directoryName As String
For Each directoryName In System.IO.Directory.GetDirectories(ScanDir)
stack.Push(directoryName)
frm2.Label4.Text = "-- Mapping Files... -- Folders Left:" + stack.Count.ToString + " -- Files Found:" + frm2.ListBox1.Items.Count.ToString + " --"
frm2.Label4.Refresh()
Next
frm2.ListBox1.Items.AddRange(System.IO.Directory.GetFiles(ScanDir, "*.*", System.IO.SearchOption.AllDirectories))
Catch ex5 As UnauthorizedAccessException
End Try
Catch ex2 As System.IO.PathTooLongException
End Try
Catch ex4 As System.IO.DirectoryNotFoundException
End Try
End If
Loop
End Sub
My conclusions was simple!
1) windows 7 dont support live ui (label) update from a while loop
called from a button...
2) windows 7 could possibly support a new
thread running the same loop
i think mabye if i run all the code in a thread mabye the ui will remain responsive
(by the way the UI is not responsive in windows 10 but i still see
the label refresh and nothing crashes when form loose focus..)
so i know how to do that but i also know that if i do that a thread will not be able to update a listbox or a label in a form and refresh it..
so the thread will need to update an external file with the data and the form2 will need to read that data live from the file but will it make the same problems? i have no idea what to do.. can use some help and tips. THANK YOU!
I must menttion the fact that the loop is working on windows 10 without a responsive UI means i cant click on any button but i can
still see the label refresh BUT on windows 7 everything works the same
UNLESS i click somewhere, no matter where i click on windows the loop
crashes
im using framework 4.6.2 developer
While I'm glad you found a solution, I advise against using Application.DoEvents() because it is bad practice.
Please see this blog post: Keeping your UI Responsive and the Dangers of Application.DoEvents.
Simply put, Application.DoEvents() is a dirty workaround that makes your UI seem responsive because it forces the UI thread to handle all currently available window messages. WM_PAINT is one of those messages which is why your window redraws.
However this has some backsides to it... For instance:
If you were to close the form during this "background" process it would most likely throw an error.
Another backside is that if the ScanButtonInForm1() method is called by the click of a button you'd be able to click that button again (unless you set Enabled = False) and starting the process once more, which brings us to yet another backside:
The more Application.DoEvents()-loops you start the more you occupy the UI thread, which will cause your CPU usage to rise rather quickly. Since every loop is run in the same thread your processor cannot schedule the work over different cores nor threads, so your code will always run on one core, eating as much CPU as possible.
The replacement is, of course, proper multithreading (or the Task Parallel Library, whichever you prefer). Regular multithreading actually isn't that hard to implement.
The basics
In order to create a new thread you only need to declare an instance of the Thread class and pass a delegate to the method you want the thread to run:
Dim myThread As New Thread(AddressOf <your method here>)
...then you should set its IsBackground property to True if you want it to close automatically when the program closes (otherwise it keeps the program open until the thread finishes).
Then you just call Start() and you have a running background thread!
Dim myThread As New Thread(AddressOf myThreadMethod)
myThread.IsBackground = True
myThread.Start()
Accessing the UI thread
The tricky part about multithreading is to marshal calls to the UI thread. A background thread generally cannot access elements (controls) on the UI thread because that might cause concurrency issues (two threads accessing the same control at the same time). Therefore you must marshal your calls to the UI by scheduling them for execution on the UI thread itself. That way you will no longer have the risk of concurrency because all UI related code is run on the UI thread.
To marhsal calls to the UI thread you use either of the Control.Invoke() or Control.BeginInvoke() methods. BeginInvoke() is the asynchronous version, which means it doesn't wait for the UI call to complete before it lets the background thread continue with its work.
One should also make sure to check the Control.InvokeRequired property, which tells you if you already are on the UI thread (in which case invoking is extremely unnecessary) or not.
The basic InvokeRequired/Invoke pattern looks like this (mostly for reference, keep reading below for shorter ways):
'This delegate will be used to tell Control.Invoke() which method we want to invoke on the UI thread.
Private Delegate Sub UpdateTextBoxDelegate(ByVal TargetTextBox As TextBox, ByVal Text As String)
Private Sub myThreadMethod() 'The method that our thread runs.
'Do some background stuff...
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), TextBox1, "Status update!") 'We are in a background thread, therefore we must invoke.
Else
UpdateTextBox(TextBox1, "Status update!") 'We are on the UI thread, no invoking required.
End If
'Do some more background stuff...
End Sub
'This is the method that Control.Invoke() will execute.
Private Sub UpdateTextBox(ByVal TargetTextBox As TextBox, ByVal Text As String)
TargetTextBox.Text = Text
End Sub
New UpdateTextBoxDelegate(AddressOf UpdateTextBox) creates a new instance of the UpdateTextBoxDelegate that points to our UpdateTextBox method (the method to invoke on the UI).
However as of Visual Basic 2010 (10.0) and above you can use Lambda expressions which makes invoking much easier:
Private Sub myThreadMethod()
'Do some background stuff...
If Me.InvokeRequired = True Then '"Me" being the current form.
Me.Invoke(Sub() TextBox1.Text = "Status update!") 'We are in a background thread, therefore we must invoke.
Else
TextBox1.Text = "Status update!" 'We are on the UI thread, no invoking required.
End If
'Do some more background stuff...
End Sub
Now all you have to do is type Sub() and then continue typing code like if you were in a regular method:
If Me.InvokeRequired = True Then
Me.Invoke(Sub()
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
ProgressBar1.Value += 1
End Sub)
Else
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
ProgressBar1.Value += 1
End If
And that's how you marshal calls to the UI thread!
Making it simpler
To make it even more simple to invoke to the UI you can create an Extension method that does the invoking and InvokeRequired check for you.
Place this in a separate code file:
Imports System.Runtime.CompilerServices
Public Module Extensions
''' <summary>
''' Invokes the specified method on the calling control's thread (if necessary, otherwise on the current thread).
''' </summary>
''' <param name="Control">The control which's thread to invoke the method at.</param>
''' <param name="Method">The method to invoke.</param>
''' <param name="Parameters">The parameters to pass to the method (optional).</param>
''' <remarks></remarks>
<Extension()> _
Public Function InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) As Object
If Parameters IsNot Nothing AndAlso _
Parameters.Length = 0 Then Parameters = Nothing
If Control.InvokeRequired = True Then
Return Control.Invoke(Method, Parameters)
Else
Return Method.DynamicInvoke(Parameters)
End If
End Function
End Module
Now you only need to call this single method when you want to access the UI, no additional If-Then-Else required:
Private Sub myThreadMethod()
'Do some background stuff...
Me.InvokeIfRequired(Sub()
TextBox1.Text = "Status update!"
Me.Text = "Hello world!"
Label1.Location = New Point(128, 32)
End Sub)
'Do some more background stuff...
End Sub
Returning objects/data from the UI with InvokeIfRequired()
With my InvokeIfRequired() extension method you can also return objects or data from the UI thread in a simple manner. For instance if you want the width of a label:
Dim LabelWidth As Integer = Me.InvokeIfRequired(Function() Label1.Width)
Example
The following code will increment a counter that tells you for how long the thread has run:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim CounterThread As New Thread(AddressOf CounterThreadMethod)
CounterThread.IsBackground = True
CounterThread.Start()
Button1.Enabled = False 'Make the button unclickable (so that we cannot start yet another thread).
End Sub
Private Sub CounterThreadMethod()
Dim Time As Integer = 0
While True
Thread.Sleep(1000) 'Wait for approximately 1000 ms (1 second).
Time += 1
Me.InvokeIfRequired(Sub() Label1.Text = "Thread has been running for: " & Time & " seconds.")
End While
End Sub
Hope this helps!
The reason your application is freezing is that you are doing all the work on the UI thread. Check out Async and Await. It uses threading in the background but makes it way easier to manage. An example here:
https://stephenhaunts.com/2014/10/14/using-async-and-await-to-update-the-ui-thread/
I am using vb.net, and in my program I get this 'crossthread operation not valid' error when I run my backgroundworker that will make this textbox enabled true. My main sub will first turn the enabled to false, and when the backgroundworker runs it will turn it back true then exit. Why does it give me an error? FYI: There is more code to this but I don't want to make it any more confusing...
Here is the stack trace:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.OnEnabledChanged(EventArgs e)
at System.Windows.Forms.Control.set_Enabled(Boolean value)
at Helium.Form1.BackgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Kevin\documents\visual studio 2010\Projects\Helium\Helium\Form1.vb:line 167
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
and here is the exact error message:
{"Cross-thread operation not valid: Control 'mainText' accessed from a thread other than the thread it was created on."}
Can someone please help me out!
Thanks,
KEvin
The purpose of the BackgroundWorker class is to perform work on a non-GUI thread while the GUI remains responsive. Unless you set Control.CheckForIllegalCrossThreadCalls to false (which you shouldn't do), or use Invoke as suggested in the other answers (which I also wouldn't recommend), you're going to get an illegal cross-thread operation exception.
If you want GUI-related "stuff" to happen while your BackgroundWorker is running, I'd generally recommend using the BackgroundWorker.ReportProgress method and attaching an appropriate handler to the BackgroundWorker.ProgressChanged event. If you want something on the GUI to happen once the BackgroundWorker is finished, then simply attach your handler to update the GUI to the BackgroundWorker.RunWorkerCompleted event.
Type the following code in the Form1_Load (or whatever your form is) sub:
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
It fixes all problems with blocked cross-thread operations.
Better way to this in VB.NET is to use a Extension it makes very nice looking code for cross-threading GUI Control Calls.
Just add this line of code to any Module you have.
<System.Runtime.CompilerServices.Extension()> _
Public Sub Invoke(ByVal control As Control, ByVal action As Action)
If control.InvokeRequired Then
control.Invoke(New MethodInvoker(Sub() action()), Nothing)
Else
action.Invoke()
End If
End Sub
Now you can write Cross-Thread Control code that's only 1 line long for any control call.
Like this, lets say you want to clear a ComboBox and it's called from threads or without threads you can just use do this now
cboServerList.Invoke(Sub() cboServerList.Items.Clear())
Want to add something after you clear it?
cboServerList.Invoke(Sub() cboServerList.Items.Add("Hello World"))
Where exactly do you set the Enabled property? If you do it within the DoWork event handler, this code is running on a different thread than the button was created on, which should give the exception that you experience. To get around this, you should use BeginInvoke. For convenience it could be wrapped into a method, like so:
Private Sub SetControlEnabled(ByVal ctl As Control, ByVal enabled As Boolean)
If ctl.InvokeRequired Then
ctl.BeginInvoke(New Action(Of Control, Boolean)(AddressOf SetControlEnabled), ctl, enabled)
Else
ctl.Enabled = enabled
End If
End Sub
Now you can safely call that method to enable or disable any control from any thread:
SetControlEnabled(someButton, False)
You cannot directly set a control's property that is on the UI thread from another thread. It can be done though, here is an example from msdn.
Private Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the'
' calling thread to the thread ID of the creating thread.'
' If these threads are different, it returns true.'
If Me.textBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.textBox1.Text = [text]
End If
End Sub
Your Form_Load () pls write below code part. All your problems will be solved.
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
Suggest to use AutomationPeer.
For example below code calls Execute button when I press F5 key. Similarly if you want a thread or Background worker to call an event or function you can use Automation Peer. In your case instead of button (Which I used here) you can use text box with its appropriate property to invoke.
'Button name=btnExecute
'Imports System.Windows.Automation.Peers
'Imports System.Windows.Automation.Provider
If e.Key = Key.F5 Then
Dim peer As New ButtonAutomationPeer(btnExecute)
Dim invokeProv As IInvokeProvider = TryCast(peer.GetPattern(PatternInterface.Invoke), IInvokeProvider)
invokeProv.Invoke()
End If
Regards
RV
CheckForIllegalCrossThreadCalls = False
I have this text loader class that I'm trying to write tests for. And one of it's methods does a RaiseEvent with a CancelEventArgs parsed in as an argument, so something like this:
Private Sub FireThisEvent()
cancelEvent created here
RaiseEvent HelloWorld(cancelEvent)
If cancelEvent.Cancel Then
'do smthg
End If
End Sub
The handler for HelloWorld event is my UI class that makes a pop-up for the user to
decide Yes or No, which then sets cancelEvent.Cancel to either True or False. And then the above method checks on cancelEvent and does an action accordingly.
My question is, since I'm only testing the loader class (and not the UI), how do I manipulate the cancelEvent after the event is raised so that I can test for when cancelEvent.Cancel is True and then , when it is False. Thank you.
Would I have mock the UI class?
My solution to this was to add an event handler in the test method, so that when the event is raised, the test method will create a CancelEventAgrs and set its Cancel to True/False.
Public Sub TestingMethod()
Dim txt As TextLoader = Nothing
AddHandler TextLoader.LoadingDoneEvent,
(Sub(e As ComponentModel.CancelEventArgs)
e.Cancel = True
End Sub)
txt = New TextLoader()
txt.FireThisEvent()
End Sub
For some reason a background thread in my app can't change any labels, textbox values, etc on my main form. There is no compile errors, when the thread executes nothing happens.
Here is some example code:
Imports System.Threading
Public Class Class1
Dim tmpThread As System.Threading.Thread
Private Sub bgFindThread()
Form1.lblStatus.Text = "test"
End Sub
Public Sub ThreadAction(ByVal Action As String)
If Action = "Start" Then
tmpThread = New System.Threading.Thread(New System.Threading.ThreadStart(AddressOf bgFindThread))
tmpThread.Start()
ElseIf Action = "Abort" Then
If tmpThread.IsAlive = True Then tmpThread.Abort()
End If
End Sub
End Class
Can someone let me know what I'm doing wrong?
AFAIK code above will throw an exception IllegalCrossThreadException, it is because the background thread is not the same as UI thread and background try to set value on other thread. So windows form check every thread that work properly.
You can set Control.CheckForIllegalCrossThreadCalls to false to make it works.
Code below is when setting property is not run
Add into your code
------------------------------
Delegate Sub MyDelegate()
Private Sub RunMyControl()
lblStatus.Text = "test"
End Sub
Change your code
------------------------------
Private Sub bgFindThread
lblStatus.BeginInvoke (New MyDelegate(AddressOf RunMyControl))
End Sub
The method asyncronsly run code from background thread to UI thread.
You can only access UI controls from the UI thread.
I suggest reading this first: http://www.albahari.com/threading/
As others have mentioned, it is forbidden (for good reasons) to update UI elements from a non-UI thread.
The canonical solution is as follows:
Test whether you are outside the UI thread
If so, request for an operation to be performed inside the UI thread
[Inside the UI thread] Update the control.
In your case:
Private Sub bgFindThread()
If lblStatus.InvokeRequired Then
lblStatus.Invoke(New Action(AddressOf bgFindThread))
Return
End If
lblStatus.Text = "test"
End Sub
The only thing that changed is the guard clause at the beginning of the method which test whether we’re inside the UI thread and, if not, requests an execution in the UI thread and returns.
You can use a delegate to update UI controls in a background thread.
Example
Private Delegate Sub bkgChangeControl(ByVal bSucceed As Boolean)
Private dlgChangeControl As bkgChangeControl = AddressOf ChangeControl
Private Sub threadWorker_ChangeControl(ByVal bSucceed As Boolean)
Me.Invoke(dlgChangeControl, New Object() {bSucceed})
End Sub
Private Sub ChangeControl()
Me.lable="Changed"
End Sub
'In your background thread, call threadWorker_ChangeControl.
I am using vb.net, and in my program I get this 'crossthread operation not valid' error when I run my backgroundworker that will make this textbox enabled true. My main sub will first turn the enabled to false, and when the backgroundworker runs it will turn it back true then exit. Why does it give me an error? FYI: There is more code to this but I don't want to make it any more confusing...
Here is the stack trace:
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.OnEnabledChanged(EventArgs e)
at System.Windows.Forms.Control.set_Enabled(Boolean value)
at Helium.Form1.BackgroundWorker1_DoWork(Object sender, DoWorkEventArgs e) in C:\Users\Kevin\documents\visual studio 2010\Projects\Helium\Helium\Form1.vb:line 167
at System.ComponentModel.BackgroundWorker.OnDoWork(DoWorkEventArgs e)
at System.ComponentModel.BackgroundWorker.WorkerThreadStart(Object argument)
and here is the exact error message:
{"Cross-thread operation not valid: Control 'mainText' accessed from a thread other than the thread it was created on."}
Can someone please help me out!
Thanks,
KEvin
The purpose of the BackgroundWorker class is to perform work on a non-GUI thread while the GUI remains responsive. Unless you set Control.CheckForIllegalCrossThreadCalls to false (which you shouldn't do), or use Invoke as suggested in the other answers (which I also wouldn't recommend), you're going to get an illegal cross-thread operation exception.
If you want GUI-related "stuff" to happen while your BackgroundWorker is running, I'd generally recommend using the BackgroundWorker.ReportProgress method and attaching an appropriate handler to the BackgroundWorker.ProgressChanged event. If you want something on the GUI to happen once the BackgroundWorker is finished, then simply attach your handler to update the GUI to the BackgroundWorker.RunWorkerCompleted event.
Type the following code in the Form1_Load (or whatever your form is) sub:
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
It fixes all problems with blocked cross-thread operations.
Better way to this in VB.NET is to use a Extension it makes very nice looking code for cross-threading GUI Control Calls.
Just add this line of code to any Module you have.
<System.Runtime.CompilerServices.Extension()> _
Public Sub Invoke(ByVal control As Control, ByVal action As Action)
If control.InvokeRequired Then
control.Invoke(New MethodInvoker(Sub() action()), Nothing)
Else
action.Invoke()
End If
End Sub
Now you can write Cross-Thread Control code that's only 1 line long for any control call.
Like this, lets say you want to clear a ComboBox and it's called from threads or without threads you can just use do this now
cboServerList.Invoke(Sub() cboServerList.Items.Clear())
Want to add something after you clear it?
cboServerList.Invoke(Sub() cboServerList.Items.Add("Hello World"))
Where exactly do you set the Enabled property? If you do it within the DoWork event handler, this code is running on a different thread than the button was created on, which should give the exception that you experience. To get around this, you should use BeginInvoke. For convenience it could be wrapped into a method, like so:
Private Sub SetControlEnabled(ByVal ctl As Control, ByVal enabled As Boolean)
If ctl.InvokeRequired Then
ctl.BeginInvoke(New Action(Of Control, Boolean)(AddressOf SetControlEnabled), ctl, enabled)
Else
ctl.Enabled = enabled
End If
End Sub
Now you can safely call that method to enable or disable any control from any thread:
SetControlEnabled(someButton, False)
You cannot directly set a control's property that is on the UI thread from another thread. It can be done though, here is an example from msdn.
Private Sub SetText(ByVal [text] As String)
' InvokeRequired required compares the thread ID of the'
' calling thread to the thread ID of the creating thread.'
' If these threads are different, it returns true.'
If Me.textBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf SetText)
Me.Invoke(d, New Object() {[text]})
Else
Me.textBox1.Text = [text]
End If
End Sub
Your Form_Load () pls write below code part. All your problems will be solved.
'## crossed-thread parts will not be controlled by this option...
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False
Suggest to use AutomationPeer.
For example below code calls Execute button when I press F5 key. Similarly if you want a thread or Background worker to call an event or function you can use Automation Peer. In your case instead of button (Which I used here) you can use text box with its appropriate property to invoke.
'Button name=btnExecute
'Imports System.Windows.Automation.Peers
'Imports System.Windows.Automation.Provider
If e.Key = Key.F5 Then
Dim peer As New ButtonAutomationPeer(btnExecute)
Dim invokeProv As IInvokeProvider = TryCast(peer.GetPattern(PatternInterface.Invoke), IInvokeProvider)
invokeProv.Invoke()
End If
Regards
RV
CheckForIllegalCrossThreadCalls = False