I'm confronted with the following code:
Public Sub fun_imageCallback(ByVal data As IntPtr, ByVal rows As Integer, ByVal cols As Integer, ByVal channels As Integer, ByVal timestamp As Long)
'Console.WriteLine(rows + " " + cols + " " + channels);
imageMutex.WaitOne()
If imageCamera IsNot Nothing Then
imageCamera.Dispose()
End If
imageCamera = New Bitmap(cols, rows, channels * cols, PixelFormat.Format8bppIndexed, data)
' The default palette has strange colours, not grayscale
Dim pal As ColorPalette = imageCamera.Palette
For i As Integer = 0 To 255
pal.Entries(i) = Color.FromArgb(i, i, i)
Next
imageCamera.Palette = pal
imageMutex.ReleaseMutex()
If Me.InvokeRequired Then
Me.BeginInvoke(
Sub()
imageMutex.WaitOne()
pbCamera.Image = DirectCast(imageCamera.Clone(), Bitmap)
imageMutex.ReleaseMutex()
End Sub
)
End If
End Sub
I don't understand what the following 4 lines do:
If Me.InvokeRequired Then
Me.BeginInvoke(
Sub()
imageMutex.WaitOne()
How is that to be read?
Thank you!
I suggest that you read this for some background first.
The point of the InvokeRequired
property is to determine whether or not you are currently executing on the thread that owns the specified control or not. InvokeRequired
is False
when you get it on the thread that created the control and True
on every other thread. Generally speaking, all controls are created on the same thread - the thread on which the application was started - which is why it's often referred to as the UI thread.
If you are executing on the thread that created the control then it is safe to access other members of that control directly. If you're on a different thread, you need to invoke a method call on the owning thread or else bad things can happen. The Invoke
and BeginInvoke
methods execute a method call on the thread that created the control. Invoke
waits for the call to complete while BeginInvoke
returns immediately and code continues executing on the current thread while the invoked method executes on the UI thread.
In your case, the important part is this:
pbCamera.Image = DirectCast(imageCamera.Clone(), Bitmap)
That is accessing the Image
property of a PictureBox
so it must be done on the UI thread. The code tests the InvokeRequired
property of the form and, if that is False
, it means that the current code is NOT executing on the UI thread so it is not safe to access that Image
property directly. The BeginInvoke
method will execute the specified Lambda expression on the UI thread, thus making it safe to access that Image
property within it.
When calling Invoke
or BeginInvoke
, you specify the method you want to execute by providing a delegate. A delegate is an object that refers to a method. Delegates for regular named methods can be created using the AddressOf
operator, which is what I do in most cases in the examples I linked to at the start of this answer. A Lambda expression is another way to create a delegate for an anonymous method. That code is like writing a named method containing this code:
imageMutex.WaitOne()
pbCamera.Image = DirectCast(imageCamera.Clone(), Bitmap)
imageMutex.ReleaseMutex()
and then using AddressOf
to create a delegate for that method. The way it works is that the Invoke
and BeginInvoke
methods work their magic in the background to switch context to the UI thread and then they execute the method that the delegate refers to on that thread.
If you're not sure how delegates work or can work, you may be interested to learn that they are the magic behind events. An event is basically a collection of delegates. Consider the Click
event of a Button
. You write a method in your form that you want executed when a Button
is clicked. How is the Button
able to execute that method? Registering an event handler is actually creating a delegate that refers to that method and adding it to the collection for that event. When the Button
raises the event, it basically loops through the collection of delegates and invokes each one.