Resolved Thread and updating Forms

Phix

Member
Joined
Apr 9, 2010
Messages
15
Location
San Jose
Programming Experience
3-5
I'm working on a website where we'll be getting a lot of videos to be uploaded. To keep things simple and secure, I'm just writing VB.net windows app to suck all the files in a directory in, allow you to set some options, and start the encode by spawning a new FFMpeg process.

I have a working version, but I'm trying to amp it up, one, to help me learn .net, and two, well, that's it.

I don't know much about threads and delegates, but I would think this would give me an error as I've run into a cross thread exception when trying this before... but my debugging says that InvokeRequired is false.

Chain of events:

Form loads
Grabs the last saved input directory from My.Settings
Populates a listview with the file names

What I'd like to do from here is have ffmpeg fire up without any params to get the Stream #0 and #0.1 (Audio/Video). I have it set up to grab that information, but I just can't get it back into the main form.

VB.NET:
Private Sub MainLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Startup.populateDefaults()
    Utilities.PreviewFileData()
End Sub

PreviewFileData() : Resides in a Module

VB.NET:
Public Sub PreviewFileData()
	If Not String.IsNullOrEmpty(Main.InputDir) Then
		' 
		' Irrelevant code populating a List(Of String)
		' 
		
		' Spawn thread and capture info
		Dim emptyThread As Thread = New Thread(AddressOf Run)
		emptyThread.IsBackground = True
		emptyThread.Start()
	End If 'Empty
End Sub
Friend Sub Run()
	Dim outList As New List(Of String)
	Debug.WriteLine(Main.InvokeRequired)
	For Each l As String In Utilities.list
		Dim fullPath As String = My.Settings.InputDirectory & "\" & l

		Debug.WriteLine(FFMpeg.GetStreamInfo(fullPath))
	Next
End Sub

It outputs:

VB.NET:
Stream #0.1(und): Audio: aac, 44100 Hz, mono, s16, 51 kb/s
Stream #0.1(eng): Audio: aac, 22050 Hz, stereo, s16, 30 kb/s

So it works just great, but I can't for the life of me figure out why I can't send this information back to Main (the main form). I've tried Getters/Setters, Good ol functions passing by value, nada. It just get's stuck.

I've tried every scope modifier too so stuff like Run() being a Friend is just me trying everything.

Any ideas? :)
 
Last edited:
Default form instances are thread specific, so when you access 'Main' from secondary thread you just get a new instance. This is also why InvokeRequired returns False, the form was created in that thread and invoking that is not necessary.

You can pass/keep the correct form instance available and Invoke, but you can also use the BackgroundWorker component, that makes background working and reporting to UI simple.
 
Thanks for your quickness John!

Thanks for pointing me in the right direction. There are still so many classes to get accustomed to I'm still not sure what all is available. I did a little research on the BackgroudWorker class and ended up using a Delegate and everything's peachy now.

Thanks again :)
 
The whole point of the BackgroundWorker is that you do NOT have to use a delegate. The idea of the BackgroundWorker is that all you have to do is call methods and handle events, which we've all done many times before. If you need to update the UI during the background task then you call ReportProgress and handle the ProgressChanged event. If you need to update the UI when the background task is complete then you handle the RunWorkerCompleted event. If you're not going to do that then there's generally no point using a BackgroundWorker.
 
Gotcha. Doing some more reading I just ended up going with a Thread. Delegates are still a little fuzzy to me but it works.

Thanks guys.
 
Most programming concepts don't exist in a vacuum. Think about what a delegate is outside programming. A delegate is a person charged with the responsibility of carrying out a task on behalf of another person or group of people. The term delegate is used in programming because the role is similar. A .NET delegate is an object that is responsible for invoking a method on behalf of another object. Normally, to call method X of object A, you would need a reference to object A. Instead, you can create a delegate and provide it a reference to method X. You can then pass that delegate around as a method argument or whatever, just as you can any other object. You can then invoke it somewhere that has no idea that object A even exists and successfully execute method X. The delegate invokes the method on behalf of the object that it's a member of, just as a delegate at the United Nations casts a vote on behalf of a country.

In this case, you need to use a delegate in the first place because controls are owned by a specific thread. That doesn't mean that you can't access a particular control on any thread you like. The issue is the control's Handle. Whichever thread creates the Handle is the only thread on which it's safe to access the Handle, so any operation that accesses the Handle of the control must be done on that thread. The InvokeRequired property of a control tells you whether you are currently executing on a thread other than the one that owns that Handle. the Invoke method will take a delegate, carry it across the thread boundary to the thread that owns the handle and then invoke it, thus invoking the method it refers to.

Multi-threading is certainly not the only place that delegates are used. They have been used a lot right from the word go, but now even more with the advent of LINQ and lambda expressions. Every lambda expression creates a delegate. The most common use of delegates is in handling events though. Consider the Click event of a Button. To handle that event, you will normally create a method in the form that contains the Button, right? Ever wonder how the Button calls that method? When you add the handler to the event, either with a Handles clause or an AddHandler statement, you are creating a delegate that refers to that method and passing it to the object whose event you're handling, i.e. the Button. When the Button is clicked, it goes to its Click event, which is basically a collection of EventHandler delegates, and invokes each one, thus invoking the method of the form.
 
Back
Top