dynamic timer in vb.net - vb.net

I've got a vb app in .Net 4 that i'm working on. The app fires up dynamic threads to run some jobs. Each thread runs a certain job and within that thread, it performs a infinate loop. It needs to be this way, i can't have the thread and then begin again...
anyway.. i was using threading.thread.sleep in the loop to have the loop pause for a bit before running again. It seems that this is not a good way to go and not recommended. So i thought perhaps i could use a dynamic timer or something else to have the thread wait for say 30 seconds before running again. i was hoping someone could give me some direction and example code.
thanks
shannon
~~~~~~~~~~edit
hey.. i figured out the problem and no.. it wasn't the sleep in the background thread.... it was a sleep i didn't realize i put in in the UI thread.. I started commented out everything and started adding in 1 by 1 and found it. Sorry PEBKAM error...
Thanks for the input

Use the following class to do the delay, i developed with same concept of javascript:settimeout :
Public Class JSsetTimeout
Public res As Object = Nothing
Dim WithEvents tm As Timer = Nothing
Dim _MethodName As String
Dim _args() As Object
Dim _ClassInstacne As Object = Nothing
Public Shared Sub SetTimeout(ByVal ClassInstacne As Object, ByVal obj As String, ByVal TimeSpan As Integer, ByVal ParamArray args() As Object)
Dim jssto As New JSsetTimeout(ClassInstacne, obj, TimeSpan, args)
End Sub
Public Sub New(ByVal ClassInstacne As Object, ByVal obj As String, ByVal TimeSpan As Integer, ByVal ParamArray args() As Object)
If obj IsNot Nothing Then
_MethodName = obj
_args = args
_ClassInstacne = ClassInstacne
tm = New Timer With {.Interval = TimeSpan, .Enabled = False}
AddHandler tm.Tick, AddressOf tm_Tick
tm.Start()
End If
End Sub
Private Sub tm_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tm.Tick
tm.Stop()
RemoveHandler tm.Tick, AddressOf tm_Tick
If Not String.IsNullOrEmpty(_MethodName) AndAlso _ClassInstacne IsNot Nothing Then
res = CallByName(_ClassInstacne, _MethodName, CallType.Method, _args)
Else
res = Nothing
End If
End Sub
End Class
Usage
JSsetTimeout.SetTimeout(ClassContainingMethod, "MethodName", 30000, OptionalParameters)

Related

Display results scraped from Webpage in Treeview Control from Class

I'm working on a Visual Basic Project. My working environment is :
Windows 10 32bit
Visual Studio 2015
.Net Framework 4.8
Winform
At this stage,I have :
Class (Class1.vb)
Form1 (Form1.vb) with TreeView Control
I'm supposed to Scrape a webpage (i.e: https://www.example.com), I want to display the result of Scraping in a Treeview Control placed on Form1. I have tried some approaches and they worked fine, except that they require using Webbrowser Control which I do not wish to use. I found a method that I'm using now, but it seems to not letting me display the Results on the Form.
Here is my Code of Class1.vb and it's working fine
Imports System.Threading.Tasks
Public Class Class1
' Create a WebBrowser instance.
Private Event DocumentCompleted As WebBrowserDocumentCompletedEventHandler
Private ManufacturersURi As New Uri("https://www.example.com/Webpage.php3")
Public ManList As New List(Of TreeNode)
Public Sub GettHelpPage()
' Create a WebBrowser instance.
Dim webBrowserForPrinting As New WebBrowser() With {.ScriptErrorsSuppressed = True}
' Add an event handler that Scrape Data after it loads.
AddHandler webBrowserForPrinting.DocumentCompleted, New _
WebBrowserDocumentCompletedEventHandler(AddressOf GetManu_Name)
' Set the Url property to load the document.
webBrowserForPrinting.Url = ManufacturersURi
End Sub
Private Sub GetManu_Name(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
Dim webBrowserForPrinting As WebBrowser = CType(sender, WebBrowser)
Dim Divs = webBrowserForPrinting.Document.Body.GetElementsByTagName("Div")
' Scrape the document now that it is fully loaded.
Dim T As Task(Of List(Of TreeNode)) =
Task.Run(Function()
Dim LinksCount As Integer = 0
For Each Div As HtmlElement In Divs
If InStr(Div.GetAttribute("ClassName").ToString, "Div-Name", CompareMethod.Text) Then
LinksCount = Div.GetElementsByTagName("a").Count - 1
For I As Integer = 0 To LinksCount
Dim Txt() As String = Div.GetElementsByTagName("a").Item(I).InnerHtml.Split("<BR>")
Dim Manu_TreeNode As New TreeNode() With
{.Name = I.ToString, .Text = Txt(0)}
ManList.Add(Manu_TreeNode)
Next
End If
Next
Return ManList
End Function)
' Dispose the WebBrowser now that the task is complete.
Debug.WriteLine(T.Result.Count) 'Result is 116
webBrowserForPrinting.Dispose()
End Sub
The above Code results 116 TreeNodes, which are the count of Tags that I scraped. Now when I attempt to display this result on Form1_Load, nothing happens, because the Form loads before the Code finishes executing.
Here is the Form1_Load Code :
Public Class Form1
Dim ThisClass As New Class1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ThisClass.GetHelpPage()
TreeView1.Nodes.Clear()
For I As Integer = 0 To ThisClass.ManList.Count - 1
TreeView1.Nodes.Add(ThisClass.ManList(I))
Next
End Sub
End Class
I noticed that if I placed an empty msgbox("") in the Form1_Load somewhere before For..Next, it forces the Form1_Load Event to wait and successfully populates the TreeView Control.
What am I doing wrong ? or What am I missing there ?
I noticed that if I placed an empty msgbox("") in the Form1_Load somewhere before For..Next, it forces the Form1_Load Event to wait and successfully populates the TreeView Control.
Yes, it plays the await role if you keep it open long enough until the task in the GetManu_Name method is completed. Since the MsgBox is a modal window which blocks the next lines from being executed until it been closed.
Now, either you make it a complete synchronous call by removing the Task.Run(...) from the GetManu_Name method, or utilize an asynchronous pattern in such a way as:
Public Class WebStuff
Public Shared Async Function ToTreeNodes(url As String) As Task(Of IEnumerable(Of TreeNode))
Dim tcsNavigated As New TaskCompletionSource(Of Boolean)
Dim tcsCompleted As New TaskCompletionSource(Of Boolean)
Dim nodes As New List(Of TreeNode)
Using wb As New WebBrowser With {.ScriptErrorsSuppressed = True}
AddHandler wb.Navigated,
Sub(s, e)
If tcsNavigated.Task.IsCompleted Then Return
tcsNavigated.SetResult(True)
End Sub
AddHandler wb.DocumentCompleted,
Sub(s, e)
If wb.ReadyState <> WebBrowserReadyState.Complete OrElse
tcsCompleted.Task.IsCompleted Then Return
tcsCompleted.SetResult(True)
End Sub
wb.Navigate(url)
Await tcsNavigated.Task
'Navigated.. if you need to do something here...
Await tcsCompleted.Task
'DocumentCompeleted.. Now we can process the Body...
Dim Divs = wb.Document.Body.GetElementsByTagName("Div")
Dim LinksCount As Integer = 0
For Each Div As HtmlElement In Divs
If Div.GetAttribute("ClassName").
IndexOf("Div-Name", StringComparison.InvariantCultureIgnoreCase) > -1 Then
LinksCount = Div.GetElementsByTagName("a").Count - 1
For I As Integer = 0 To LinksCount
Dim Txt = Div.GetElementsByTagName("a").Item(I).InnerHtml.
Split({"<BR>"}, StringSplitOptions.RemoveEmptyEntries)
Dim n As New TreeNode With {
.Name = I.ToString, .Text = Txt.FirstOrDefault
}
nodes.Add(n)
Next
End If
Next
End Using
Return nodes
End Function
End Class
Notes on the method:
A single Async function to do the lengthy task and returns IEnumerable(Of TreeNode) to the caller.
Lambda Expressions are used to add the WebBrowser.Navigated and WebBrowser.DocumentCompleted events.
The TaskCompletionSource is necessary here to wait for the completion of the WebBrowser.DocumentCompleted event in order to be able to process the HTML contents.
You need to add the Async modifier to the caller's signature to call the function and wait for the result. For example, the Form.Load event:
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim nodes = Await WebStuff.ToTreeNodes("www....")
TreeView1.Nodes.AddRange(nodes.ToArray)
End Sub
Or Async method:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PopulateTree()
End Sub
Private Async Sub PopulateTree()
Dim nodes = Await WebStuff.ToTreeNodes("www....")
TreeView1.Nodes.AddRange(nodes.ToArray)
End Sub
Try this
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ThisClass As New Class1
Dim i As Integer = 0
Do Until ThisClass.IsCompleted
Threading.Thread.Sleep(100)
'if the document takes too much time
i += 1
If i > 30 Then Exit Do 'more than 3 sec
Loop
TreeView1.Nodes.Clear()
For I As Integer = 0 To ThisClass.ManList.Count - 1
TreeView1.Nodes.Add(ThisClass.ManList(I))
Next
End Sub
End Class
Class Class1
Dim Completed As Boolean = False
ReadOnly Property IsCompleted As Boolean
Get
Return Completed
End Get
End Property
Private Sub GetManu_Name(ByVal sender As Object, ByVal e As WebBrowserDocumentCompletedEventArgs)
'your code
Completed = True
Return ManList
End Sub

TWAIN events in BackgroundWorker

I have a project in which I use a backgroundworker to do the scanning function from a scanner asynchronously by WIA2. It works well with WIA.
Now I am trying to do the same by scanning with TWAIN. I can scan using TWAIN ok. However when I try to make it work in the background I cannot make it work properly since the event of scanning in TWAIN have a TransferImage handler and a ScanningComplete event handler which get aroused when the scan finishes. The transferImage is ok since it does not affect my background event. However I want to access a panel in the scanningComplete event, make it .Visible = False
A piece of what happens:
Private Sub rBEScan_Click(sender As Object, e As EventArgs) Handles rBEScan.Click
rPScanning.Visible = True
Me.rBEScan.Enabled = False
bGWScan.RunWorkerAsync()
End Sub
Private Sub bGWScan_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bGWScan.DoWork
Dim path As String = ""
Dim correct As Boolean = False
If scanMode = 1 Then
correct = ScannerRead(path, 1)
Else 'TWAIN
images = Nothing
images = New List(Of System.Drawing.Bitmap)
correct = scanTWAIN(gLocalScanner, path)
End If
Dim obj As Object
obj = correct
e.Result = obj
End Sub
Private Sub bGWScan_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bGWScan.RunWorkerCompleted
Dim obj As Object
obj = e.Result
Dim lobj As Boolean
lobj = DirectCast(obj, Boolean)
rPScanning.Visible = False
Me.rBEScannerEskaneatu.Enabled = True
End Sub
Private Shared Function scanTWAIN(ByVal id As String, ByVal path As String) As Boolean ' prompt to scan more pages,
' SCAN TWAIN FUNCTION
AddHandler twain.TransferImage,
Sub(sender As Object, args As TwainDotNet.TransferImageEventArgs)
If (Not (args.Image Is Nothing)) Then
images.Add(args.Image)
End If
End Sub
' Re-enable the form after scanning completes
AddHandler twain.ScanningComplete,
Sub(sender As Object, e As TwainDotNet.ScanningCompleteEventArgs)
'Enabled = True
Dim lobj As String = ""
lobj = FuncionScanner.pdfIrudiraTwain(images, path, gLocalPreguntarRotacion, orritxurisep, orriDok, orriguztiakDok)
PrincipalR.rPScanning.Visible = False
PrincipalR.rBEScan.Enabled = True
End Sub
ScanningFunctionOfTwain With my settings.
End Sub
My problem is that I end the background worker before scanning the images, since the events are handled and aroused in another function that is called asynchronously.
Any idea of how can I put the rPScanning.Visible = False
and rBEScan.Visible = False
When the event within the background worker ends.
If it is not possible should I use another backgroundworker in the eventHandler of the scan pages in TWAIN.
Thank #JQSOFT,
As you said I had to use a delegate sub to do handle the panel visibility. Here is the change in the code:
Private Shared Function scanTWAIN(ByVal id As String, ByVal path As String) As Boolean ' prompt to scan more pages,
' SCAN TWAIN FUNCTION
AddHandler twain.TransferImage,
Sub(sender As Object, args As TwainDotNet.TransferImageEventArgs)
If (Not (args.Image Is Nothing)) Then
images.Add(args.Image)
End If
End Sub
' Re-enable the form after scanning completes
AddHandler twain.ScanningComplete,
Sub(sender As Object, e As TwainDotNet.ScanningCompleteEventArgs)
Dim objNewThread As New Thread(Sub() FuncionScanner.pdfTwainThread(images, path, gLocalPreguntarRotacion,
orritxurisep, orriDok, orriguztiakDok,
rPScanning, rBEScan))
objNewThread.IsBackground = True
objNewThread.Start()
End Sub
ScanningFunctionOfTwain With my settings.
End Sub
I have put it in a new thread the after scan function and it works well.
Since I use the new thread in another class I put the new delegate sub as well.
Public Shared Sub pdfTwainThread(ByVal lista As List(Of System.Drawing.Bitmap),
ByVal path As String, ByVal ori As Boolean,
ByVal orritxurisep As Boolean, ByVal orriDok As Boolean,
ByVal orriguztiakDok As Boolean,
ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rb As Telerik.WinControls.UI.RadButtonElement)
Dim l As String = ""
l = pdfIrudiraTwain(lista, path, ori, orritxurisep, orriDok, orriguztiakDok)
panel_visible(False, rp, rb)
Dim txt As String = ""
Dim txt1 As String = ""
txt1 = Func_nombre(366)
If l <> "" Then
txt = Func_nombre(436)
MessageBox.Show(txt, txt1, MessageBoxButtons.OK, MessageBoxIcon.Information)
Else
txt = Func_nombre(437)
MessageBox.Show(txt, txt1, MessageBoxButtons.OK, MessageBoxIcon.Error)
End If
End Sub
Delegate Sub PanelV(vf As Boolean, ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rbscan As Telerik.WinControls.UI.RadButtonElement)
Private Shared Sub panel_visible_hemen(ByVal vis As Boolean, ByRef rp As Telerik.WinControls.UI.RadPanel,
ByRef rbscan As Telerik.WinControls.UI.RadButtonElement)
If rp.InvokeRequired Then
Dim d As New PanelV(AddressOf panel_visible_hemen)
rp.Invoke(d, New Object() {vis, rp, rbscan})
Else
rp.SendToBack()
rp.Visible = vis
rbscan.Enabled = True
End If
End Sub
I pass the panel and the buttonElement byref to the new function which I update in a delegate sub.
Thanks a lot for the help #JQSOFT.
I have post the answer just in case someone else needs.

Replicate Windows Unhide folders and files function

I'm re-visiting a tool that I wrote in VB.Net for my helpdesk team a while back and want to add a couple of checkboxes to replicate the same function that Windows uses to show hidden files and folders / re-hide, as well as protected operating system files.
I know I can do this by editing a registry entry and restarting explorer.exe, but that closes all open Explorer Windows and I don't want that.
Does anyone know how Windows is able to do this by a simple click of a checkbox and how I may be able to code it in VB.net?
Any input on this is greatly appreciated in advance.
EDIT: So it looks like I have found a refresh method that works to refresh Windows Explorer / File Explorer which can be applied to Drarig's answer below but I am having trouble converting it to VB.net as the original example is in C#.
'Original at http://stackoverflow.com/questions/2488727/refresh-windows-explorer-in-win7
Private Sub refreshExplorer(ByVal explorerType As String)
Dim CLSID_ShellApplication As Guid = Guid.Parse("13709620-C279-11CE-A49E-444553540000")
Dim shellApplicationType As Type = Type.GetTypeFromCLSID(CLSID_ShellApplication, True)
Dim shellApplication As Object = Activator.CreateInstance(shellApplicationType)
Dim windows As Object = shellApplicationType.InvokeMember("Windows", Reflection.BindingFlags.InvokeMethod, Nothing, shellApplication, New Object() {})
Dim windowsType As Type = windows.GetType()
Dim count As Object = windowsType.InvokeMember("Count", Reflection.BindingFlags.GetProperty, Nothing, windows, Nothing)
For i As Integer = 0 To CType(count, Integer)
Dim item As Object = windowsType.InvokeMember("Item", Reflection.BindingFlags.InvokeMethod, Nothing, windows, New Object() {i})
Dim itemType As Type = item.GetType()
'Only fresh Windows explorer Windows
Dim itemName As String = CType(itemType.InvokeMember("Name", Reflection.BindingFlags.GetProperty, Nothing, item, Nothing), String)
If itemName = explorerType Then
itemType.InvokeMember("Refresh", Reflection.BindingFlags.InvokeMethod, Nothing, item, Nothing)
End If
Next
End Sub
I am getting an exception Object reference not set to an instance of an object when I set itemType as Type = item.GetType() above. I can't figure out which object isn't being created. When I step through the code it looks like windowsType contains an object for windows. Does anyone have any idea on this? Once this is worked out I can then apply it to Drarig's solution below.
Alright I wish I could have got this to you sooner, but busy lately at work. I took a little time today to figure this out as I love digging into something I have not done before. This is the whole class from a new project; didn't have time to wrap it up in a separate class. I am sure this will get you what you need. It was a little harder than I thought as getting the correct handle and then send the command, but I got it. I hope you find it useful.
P.S. Some of the things you can leave out, specifically the boolean used for loading, this was so I can pull the current value back on load and either check/uncheck the CheckBox.
Note: This is tried and tested on Windows 7, 8 and 10
Imports Microsoft.Win32
Imports System.Reflection
Imports System.Runtime.InteropServices
Public Class Form1
<Flags()> _
Public Enum KeyboardFlag As UInteger
KEYBOARDF_5 = &H74
End Enum
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindow(ByVal hl As Long, ByVal vm As Long) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Private blnLoading As Boolean = False
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
Form1.HideFilesExtension(Me.CheckBox1.Checked)
If Not blnLoading Then NotifyFileAssociationChanged()
RefreshExplorer()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, False)
blnLoading = True
Me.CheckBox1.Checked = CBool(key.GetValue("Hidden"))
key.Close()
blnLoading = False
End Sub
Private Shared Sub HideFilesExtension(ByVal Hide As Boolean)
Dim name As String = "Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Dim key As RegistryKey = Registry.CurrentUser.OpenSubKey(name, True)
key.SetValue("Hidden", If(Hide, 1, 0))
key.Close()
End Sub
Public Shared Sub RefreshExplorer()
Dim clsid As New Guid("13709620-C279-11CE-A49E-444553540000")
Dim typeFromCLSID As Type = Type.GetTypeFromCLSID(clsid, True)
Dim objectValue As Object = Activator.CreateInstance(typeFromCLSID)
Dim obj4 As Object = typeFromCLSID.InvokeMember("Windows", BindingFlags.InvokeMethod, Nothing, objectValue, New Object(0 - 1) {})
Dim type1 As Type = obj4.GetType
Dim obj2 As Object = type1.InvokeMember("Count", BindingFlags.GetProperty, Nothing, obj4, Nothing)
If (CInt(obj2) <> 0) Then
Dim num2 As Integer = (CInt(obj2) - 1)
Dim i As Integer = 0
Do While (i <= num2)
Dim obj5 As Object = type1.InvokeMember("Item", BindingFlags.InvokeMethod, Nothing, obj4, New Object() {i})
Dim type3 As Type = obj5.GetType
Dim str As String = CStr(type3.InvokeMember("Name", BindingFlags.GetProperty, Nothing, obj5, Nothing))
If (str = "File Explorer") Then
type3.InvokeMember("Refresh", BindingFlags.InvokeMethod, Nothing, obj5, Nothing)
End If
i += 1
Loop
End If
End Sub
Public Shared Sub NotifyFileAssociationChanged()
'Find the actual window...
Dim hwnd As IntPtr = FindWindow("Progman", "Program Manager")
'Get the window handle and refresh option...
Dim j = GetWindow(hwnd, 3)
'Finally post the message...
PostMessage(j, 256, KeyboardFlag.KEYBOARDF_5, 3)
End Sub
End Class
Here's a solution for everything excepting the refreshing of the explorer.
I've translated the code, but I'm unable to find how to refresh the explorer/desktop without restarting it.
Const keyName As String = "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
Const Hidden As String = "Hidden"
Const SHidden As String = "ShowSuperHidden"
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim St As Integer = GetRegValue(Hidden)
If St = 2 Then
SetRegValue(Hidden, 1)
SetRegValue(SHidden, 1)
Else
SetRegValue(Hidden, 2)
SetRegValue(SHidden, 0)
End If
End Sub
Private Function GetRegValue(valueName As String) As Integer
Return CInt(My.Computer.Registry.GetValue(keyName, valueName, 0))
End Function
Private Sub SetRegValue(valueName As String, value As Integer)
My.Computer.Registry.SetValue(keyName, valueName, value, Microsoft.Win32.RegistryValueKind.DWord)
End Sub
I have a few ideas to refresh the desktop :
Send a key to a running process. I tried this (source) :
Dim pp As Process() = Process.GetProcessesByName("explorer")
If pp.Length > 0 Then
For Each p In pp
AppActivate(p.Id)
SendKeys.SendWait("{F5}")
Next
End If
Refresh using SHChangeNotify (source),
Refresh broadcasting a WM_SETTINGCHANGE message (source),
etc.
I think you'll be forced to manually refresh or restart the explorer.

Raising an event on a new thread in VB.NET

I need to raise an event from a form on a new thread.
(I don't believe the reason for this is relevant, but just in case: I'll be raising events from code within a form's WndProc sub. If the code handling the event blocks with something on a form [such as a msgbox], then all sorts of trouble occurs with disconnected contexts and what not. I've confirmed that raising events on new threads fixing the problem.)
This is what I am currently doing:
Public Event MyEvent()
Public Sub RaiseMyEvent()
RaiseEvent MyEvent
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Dim t As New Threading.Thread(AddressOf RaiseMyEvent)
t.Start()
End Sub
Is there a better way?
It is my understanding that events in VB are actually made up of delegates in the background. Is there any way to raise events in new threads without creating subs for each? Or, is there a more appropriate method I should be using?
You can eliminate the RaiseMyEvent sub like this:
Public Class Class1
Public Event MyEvent()
Sub Demo()
Dim t As New Threading.Thread(Sub() RaiseEvent MyEvent())
t.Start()
End Sub
End Class
Don't know if this will help, but i'll always do threading and events like this:
Event MyEvent(ByVal Var1 As String, ByVal Var2 As String)
Private Delegate Sub del_MyEvent(ByVal Var1 As String, ByVal Var2 As String)
Private Sub StartNewThread()
'MAIN UI THREAD
Dim sVar1 As String = "Test"
Dim sVar2 As String = "Second Var"
Dim oThread As New Threading.Thread(New Threading.ParameterizedThreadStart(AddressOf StartNewThread_Threaded))
With oThread
.IsBackground = True
.Priority = Threading.ThreadPriority.BelowNormal
.Name = "StartNewThread_Threaded"
.Start(New Object() {sVar1, sVar2})
End With
End Sub
Private Sub StartNewThread_Threaded(ByVal o As Object)
'CHILD THREAD
Dim sVar1 As String = o(0)
Dim sVar2 As String = o(1)
'Do threaded operation
Threading.Thread.Sleep(1000)
'Raise event
RaiseEvent_MyEvent(sVar1, sVar2)
End Sub
Public Sub RaiseEvent_MyEvent(ByVal Var1 As String, ByVal Var2 As String)
If Me.InvokeRequired Then
'Makes the sub threadsafe (I.e. the event will only be raised in the UI Thread)
Dim oDel As New del_MyEvent(AddressOf RaiseEvent_MyEvent)
Me.Invoke(oDel, Var1, Var2)
Exit Sub
End If
'MAIN UI THREAD
RaiseEvent MyEvent(Var1, Var2)
End Sub

Calling Sub from EventHandler

I'm using the below to update controls from another thread (works great) How would I call a Sub (Named UpdateList)? The UpdateList updates a listview with a list of databases on a selected SQL instance, requires no arguments.
Private Sub CompleteEventHandler(ByVal sender As Object, ByVal e As Microsoft.SqlServer.Management.Common.ServerMessageEventArgs)
SetControlPropertyValue(Label8, "text", e.ToString)
UpdateList()
MessageBox.Show("Restore Complete")
End Sub
Delegate Sub SetControlValueCallback(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
Private Sub SetControlPropertyValue(ByVal oControl As Control, ByVal propName As String, ByVal propValue As Object)
If (oControl.InvokeRequired) Then
Dim d As New SetControlValueCallback(AddressOf SetControlPropertyValue)
oControl.Invoke(d, New Object() {oControl, propName, propValue})
Else
Dim t As Type = oControl.[GetType]()
Dim props As PropertyInfo() = t.GetProperties()
For Each p As PropertyInfo In props
If p.Name.ToUpper() = propName.ToUpper() Then
p.SetValue(oControl, propValue, Nothing)
End If
Next
End If
End Sub
Based On:
http://www.shabdar.org/cross-thread-operation-not-valid.html
Calling a method from an event handler is not a special case – use a normal call.
The issue here is the cross-thread call from a background thread to the GUI thread. In order to overcome it, you can place the following code at the beginning of the UpdateList code:
If Me.InvokeRequired Then
Me.Invoke(New Action(AddressOf UpdateList))
Return
End If