Delegate function to return value - vb.net

I am trying to get value from the delegate function. Basically I am trying to get the richboxtext line length from delegate function.
Here is my function
Private Delegate Function getrichlengthmain(ByVal TB As RichTextBox) As String
Private Function getrichlenghtdele(ByVal TB As RichTextBox) As string
Return TB.Lines.Length
End Function
And I am calling it by
Dim d As getrichlengthmain
d = AddressOf getrichlenghtdele
dim returnvalue as string
returnvalue = d.BeginInvoke(FstList,nothing,nothing)
2 problems in this code
1st problem
when I change the code from "returnvalue = d.BeginInvoke(FstList,nothing,nothing)" to "d.BeginInvoke(FstList,nothing,nothing)"
The cross-thread error popout while debuiging
2nd problem
How do I get the value from the function without getting any cross thread error.
Very thanks for your time and thanks in advance for solving it.

You are experiencing an error because the control you are trying to access was created on a different thread from the one you are accessing it on. This is a common mistake when developing threaded UI applications in .Net.
There are (at least) two ways to tackle the problem you have, the lazy way and the correct way. To solve this the lazy way, you simply stop the check for illegal cross-threading calls to controls from taking place by including this line of code somewhere in your UI component:
Control.CheckForIllegalCrossThreadCalls = False
A better way to deal with the problem is to ensure you code executes on the correct thread (therefore maintaining thread safety). One method of accomplishing this is described here.

Related

Invoke method for multi thread application?

I have a bug in my application which is the same as here which this person was running into the same problem. My application is multi threaded where the worker thread is updating the Waveformgraph on the UI. I believe that is where my problem is and why, periodically, and on occassion I get a big red X in at least one of my waveformgraph objects when running the application. From reading and research, I need to use an Invoke or BeginInvoke method? Can someone please explain better and provide a sample code that is relevant to my code? The samples that I've found so far still have me hazy on how I need to do this or what I need to do. Thank you for your help.
This code is on the swScopeOnOff click event, main thread.
thread2 = New System.Threading.Thread(AddressOf dataAcquiring)
thread2.Start()
This code is in dataAcquiring Sub
Public Sub dataAcquiring()
'While Scope switch is on, stream each Ai channel's data continuously to its respective WaveForm graph
Do While swScopeOnOff.Value = True
data = reader.ReadWaveform(readRate)
i = 0
For Each WaveformGraph In WFGS
WaveformGraph.PlotWaveformAppend(data(i)) 'This line is updating the UI's waveform graphs
i += 1
Next
i = 0
Loop
End Sub
Proper, thread-safe invocation is actually not as hard as one might think (not even for thread-safe events, but that's irrelevant for this question).
I would recommend you to use the normal Invoke method, such as Me.Invoke() (where Me is the current form, if not, use Form1 or whatever it's called instead). Using BeginInvoke() may be asynchronous but it stacks memory usage and can cause memory leaks if EndInvoke() is not called correctly.
If you target .NET 4.0 or higher you can simply do like this:
Me.Invoke(Sub() WaveformGraph.PlotWaveformAppend(data(i)))
However if you target .NET 3.5 or lower it requires a few more lines of code.
'Outside your Sub.
Delegate Sub WaveformAppendDelegate(ByRef WaveformGraph, ByRef data)
'Create a new sub.
Public Sub AppendData(ByRef WaveformGraph, ByRef data)
WaveformGraph.PlotWaveformAppend(data)
End Sub
'Inside your sub, when you're going to invoke.
Me.Invoke(New WaveformAppendDelegate(AddressOf AppendData), WaveformGraph, data(i))

Multithreading Webbrowsers

I am currently making a vb program that i plan to make very big. I have a decent knowledge of visual basic but today i came across something i do not understand. Because of the huge size of my program , i decided to try and keep the program as organized as possible by putting specific subs in modules. These subs consist of httprequest , webbrowsers(control), webclients and alot of loops. In order to prevent these subs from lagging my main application i thread them using threading.thread and i start them from my main form. But this leads to two problems.
Problem 1: The threads cannot in any way interact with the main form.
Once the a httprequest or webclient collects the information from my desired website, i am trying to make it add the info to a listbox in my main form, So what i did is it typed
Msgbox("Info Sent")
form1.listbox1.items.add(String)
The first messagebox will show but although the code right under it runs, nothing is added to the first forms listbox.I am not using delegates to transfer the information, instead, although its not a good habit, i am using checkforillegalcrossovers.
Problem 2: Threading with a webbrowser.
Threading with a webbrowser using threading.thread also does not work because it causes an active x error. After looking it up i found that a solution was to use a single threaded apartment but this would not work because i may need multiple threads running off the same sub at once.
One solution that i have found to this problem is creating another form completely and setting it invisible, and since the form is its own thread i do not need to use threading.thread , but the problem comes when i am trying to create multiple threads, or else i can somehow dynamically create the threads and put the subs inside of it programically this method wont work And even if it does i feel that it is sloppy so i will leave this for one of two last resorts.
The other solution is the most simple one in which i just put all of the code in the main form, but if i keep on doing that form1 is gonna become huge and sloppy, doing this wont solve the webbrowser problem either and even when using regions i still feel that something that 1000+ lines deserves its own class.
There must be some solution out there that solves these problems. Any help would be appreciated, Thanks.
I checked my code for updating the progress bar, and using a single thread with synclock will NOT work. They way I make it work is perform the step of the pbar each time after a thread is started as I have limited total threads (say less than 5 threads). Thus, even the progress bar steps before the threads are finished, but it will not progress further before new threads started. It is not 100% accurate but it more or less telling the progress
'update the progress bar
some_form.PBar1.PerformStep()
' This while loop is to count the existing running thread,
' and determine whether new thread should start
While 1
Dim t2 = New System.Threading.Thread(Sub() WaitForPermission())
t2.Start()
t2.Join()
If proceed_gen Then
Exit While
End If
End While
'Start doing what I need to do
Dim t1 = SomeSub()
t1.Start()
'End of code, as VB doest not have thread.detach()
Correct me if I am wrong, but you probably have to use a background worker. I know this is annoying, but this is the limitation of VB.net.
Or, you can have something like this (pseudo code, not tested)
structure some_struct
'define the strings you want to update, and their status such that
'main() knows if you need to update the stuff to the form
' You can also put how many threads are running, and the status of each thread,
'such that the main knows if all threads are completed
end structure
sub your_sub()
'Manipulate the website, and update the data structure with
'proper stuff you need
end sub
sub main(){
dim t1 = New System.Threading.Thread(Sub() your_sub())
t1.start()
' I am listing only one threads here, but start as many as you want
'check if there are strings that you need to update to the form
while 1
'check if there are any stuff you want to update from the data structure.
' Make sure you use synclock on the data structure, so each thread won't fight each other on accessing the data struct
dim should_update as boolean = false
should_update = 'Something thatyou should implement to judge whether you should update the form.
'You can start a thread and join it so the thread won't fight with other threads for accessing the data structure
dim some_string as string
if should_update
some_string = 'You may also need a thread to join to get the stuff you need. Dim the string as an array if it is required.
'You can also try pass by ref
'if you need to use thread to access the data structure to know if you need to update the form
form1.listbox1.items.add(some_string )
end if
end while
end sub
This is an ugly solution, but it will help you do the job...

How to use the same class accross multiple threads and return a variable

My apologies in advance if this has already been answered, but every search I have done does not come close to what I need. Also, this is all pseudo code.
Here is the situation: I created a form (targeting DOT NET 3.5) that does a loop on a gridview recreating a class and runs the code. After the code runs, there is a local variable on the class that gets updated and allows me to use it and the process repeats. Something like this:
For x as Integer = 0 to Me.txtTextBox.Lines.Count - 1 'Can be in the hundreds
Dim objMyClass as MyClass = New MyClass(Me.DatagridView1.Rows(x).Cells(0).Value)
if objMyClass.Start() = True then
'Do my thing with objMyClass.LocalLongVariable
End If
Next
This works just fine, but takes literally days to complete. The last time I ran this it took like 6 days, 7 hours and 40 something minutes to complete and barely bumped the CPU usage.
So, now I want to use MulitThreading to run several of these instances at the same time. I have not been able to get this to work. Everything I try returns different values every time I run it (and it should not). I believe that the threads are accessing the local variable across other threads and are incrementing at will. And SyncLock locks up the entire program. I have also tried adding a custom event that fires when the process is completed and executes a delegate on the Main form, but that has not worked either.
Now, my question is simple: How can I run multiple threads using the same base class (passing a unique string variable) and have the local class variable produce the correct results back to the UI? (And, from what I have been reading, the BackgroundWorker class in not suitable for this many threads (like hundreds); correct me if I read it incorrectly please)
I am looking for something like:
Dim thrd(Me.txtTextBox.Lines.Count) as Thread
Dim objMyClass(Me.txtTextBox.Lines.Count) as MyClass
For x as Integer = 0 to Me.txtTextBox.Lines.Count - 1
thrd(x) = new Thread (Sub()
objMyClass(x) = New MyClass(Me.GridView1.Rows(x).Cells(0).Value
If objMyClass.Start() = True Then
'Do my stuff here (maybe call a delegate??)
End If
End)
thrd(x).IsBackground = True
thrd(x).Start()
Next
Any help/advice on how to proceed will be greatly appreciated. And, if you know of any examples of your suggestion, please post the code/link.
The solution was, in fact, Synclock. My issue was that I was locking the wrong object, objMyClass, instead of the current Me AND I was failing to use Monitor.PulseAll(). Also, I switched to using the ThreadPool.QueueUserWorkItem(AddressOf objMyClass, args) and also used SyncLock on my custom event raised when the thread completes. It's a whole lot easier!! Thanks!!

How to I search for files recursively while updating a progress bar and returning the file List to the application from BackgroundWorker

Thanks for reading!
I am currently trying to develop an application which will backup large folders to a specified destination. To find all the files in the specified 'backup' directory I am using the following code.
Public Shared Function GetFilesRecursive(ByVal initial As String) As List(Of String)
Dim result As New List(Of String)
Dim stack As New Stack(Of String)
stack.Push(initial)
Do While (stack.Count > 0)
Dim dir As String = stack.Pop
Try
result.AddRange(Directory.GetFiles(dir, "*.*"))
Dim directoryName As String
For Each directoryName In Directory.GetDirectories(dir)
stack.Push(directoryName)
Next
Catch ex As Exception
'stay quiet
End Try
Loop
Return result
End Function
From here I am using BackgroundWorker to copy each file and report the progress of the completed list to the GUI via a progress bar.
This is fine and works great until I come to a large directory, say C:\Windows where it hangs and freezes the GUI until it completes, this is horrible!
I have tried putting my GetFilesRecursive function into a separate background worker to run first so I can update the GUI however I am struggling on how to return the List of found files back to my application (I get cross-threading exception) and how to update the progress bar to show the progress of the GetFileRecursive function so the user knows it is processing the list of files and has not crashed/frozen.
Thank you very much in advance for your input! :)
Steve
For reference, here's the BackgroundWorker manual page.
First, have a look at the RunWorkerAsync(Object) method. With this method, you can pass an object to your 'worker' function. This might be an empty stack, for example.
In your RunWorkerCompleted event handler, you can grab that same object (now populated with file paths) from the RunWorkerCompletedEventArgs and do what you will with it. The reason I suggest passing an object is that you avoid the need for a static variable. In this case, a static variable is a bit of a code smell and should be avoided.
If you want to report the progress of the routine, you need to call the ReportProgress(int) function from within your worker function. This will fire the ProgressChanged event which you can use to drive your progress bar.
MSDN also has a pretty good example of the pattern here. They don't do anything to demonstrate progress reporting but there's an example of that on the ReportProgress page.
Edit:
If it's a plain Stack you're using, then I suggest creating the stack, then passing it to RunWorkerAsync( ). From here, you can get the stack from the event args of BackgroundWorker_DoWork, which you can then pass to your actual worker function, in this case, GetFilesRecursive( ). So GetFilesRecursive( ) does not actually return anything, but it's able to fill the stack, which you can then access again from your ProgressCompleted handler. Mind you, you'll need to cast that object back to a Stack (or whatever type of object you decide to use) before you actually use it.
Big picture: the stack is like a bucket which you pass around, and gets filled. You first create the bucket when you call RunWorkerAsync( ), passing the bucket as an argument.

InvokeMember using GetField. Field not found in VB.NET

This is probably a simple one but I can't seem to figure it out.
I have a bunch of form items created by the form designer declared as (in frmAquRun.Designer.vb)
Public WithEvents btnAquRunEvent1 As VisibiltyButtonLib.VisibilityButton
Public WithEvents btnAquRunEvent2 As VisibiltyButtonLib.VisibilityButton
... etc
And I basically want to be able to supply a number to a function access each of these fields. So I wrote this function. (in frmAquRun.vb)
Const EVENT_BUTTON_PREFIX As String = "btnAquRunEvent"
Public Function getEventButton(ByVal id As Integer) As Windows.Forms.Button
Dim returnButton As Windows.Forms.Button = Nothing
Try
returnButton = DirectCast(Me.GetType().InvokeMember(eventButtonName, Reflection.BindingFlags.GetField Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance, Nothing, Me, Nothing), Windows.Forms.Button)
Catch ex As Exception
End Try
Return returnButton
End Function
But it always seems to be generating field not found exceptions.
The message in the exception is "Field 'ATSIS_ControlProgram.frmAquRun.btnAquRunEvent1' not found.".
The namespace and form name in the message are correct. Any idea what i'm doing wrong?
The problem is that for WithEvents fields, VB actually creates a property that does the necessary event handler attaching and detaching. The generated property has the name of the field. The actual backing field gets renamed to _ + original name.1)
So in order for your code to work just prefix the button name by _ or use the BindingFlag that corresponds to the property getter (instead of GetField).
Alternatively, you can do this a lot easier by using the Controls collection of the form:
returnButton = DirectCast(Me.Controls(eventButtonName), Windows.Forms.Button)
But beware that this only works if the button is top-level, i.e. not nested within a container control on the form.
1) This is an implementation detail of the VB compiler but it’s portable (especially to Mono’s vbnc compiler) since the handling for WithEvents fields is described in great detail in the VB language specifications.
The problem is that the event handlers aren't really fields. As compiled they're really properties that implement add_btnAquRunEventX, remove_btnAquRunEventX and fire_btnAquRunEventX methods. There are ways of using reflection to get around this, but that's probably not the best way to approach the problem. Instead you can simply create a List<> and populate it with the event handlers, then index into that list.
I'm a little rusty in VB syntax but it should look something like this:
Dim events = New List<EventHandler>()
events.Add( btnAquRunEvent1 )
events.Add( btnAquRunEvent2 )
....
events( 0 )( null, EventArgs.Empty )
Take a step back though and evaluate why you're invoking by index. There may be a simpler way of abstracting the whole thing that doesn't involve all this indirection.