Question Need to Invoke a Method, Not a Control, From Another Thread

capella07

Member
Joined
Nov 1, 2006
Messages
18
Location
At work
Programming Experience
1-3
This is for .NET Compact Framework 2.0

Here's the flow of what I'm trying to do:

1. On the main form a scanner class is instantiated that (in the scanner's constructor) sets the scanner settings (baud rate, parity, etc) and opens the scanner for use
2. A barcode is scanned (via a scanner connected to the device in a seral port) while on the main form
3. In the scanner class, the DataRecieved event gets the needed info from the scanned code.
4. Then that info is passed back to the main form to a method RunCommandCode (which takes the scanned info as an argument)
5. On the main form the RunCommandCode calls another method CommandCodeHandler, passing the scanned info again
6. In the CommandCodeHandler sub a method is called in a Utils class (SetSelectedTab), which interacts with the form itself (determines how many tabs exist on a tab control), which is the reason for the invoke in the first place

So, before I figured out I needed to use the Invoke method (and, more specifically, that the scanner was working in a different thread), I was getting an error in step 6 on the line that got the TabControl.TabPages.Count.

Here's the DataRecieved event handler in the scanner class:

VB.NET:
Private Sub comScanner_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles comScanner.DataReceived 

    If comScanner.IsOpen Then 
        Dim sCode As String = comScanner.ReadExisting() 
        Dim asCodes() As String = sCode.Split(CChar(vbCrLf)) 

        For i As Integer = 0 To UBound(asCodes) 
            If asCodes(i).Trim() <> "" Then 
                Dim objCC As New CommandCode() 
                    objCC.CommandCode = asCodes(i) 
                Dim cc() As CommandCode = {objCC} 

                Dim frm As frmMain = CType(m_objCallForm, frmMain) 

                ' Error "ArgumentException" 
                frm.Invoke(New frmMain.RunCommandCodeInvoker(AddressOf frm.RunCommandCode), cc) 
            End If 
        Next 
    End If 

End Sub

As you can see in the code, I'm getting one of those insanely vague error messages that is completely useless (end of short rant) on the line in which I call the delegate.

Here's the delegate sub and the sub it's for:

VB.NET:
Public Delegate Sub RunCommandCodeInvoker(ByVal args() As Object) 

Public Sub RunCommandCode(ByVal args() As Object) 
    MyCommandCodeSystem.CurrentHandler = AddressOf CommandCodeHook 
    ComScanner.CurrentForm = Me 
    CommandCodeHandler(args) 
End Sub

I realized that all of the examples I was reading were for using Invoke on controls, when, in this case, I need to use it on a method. At least I'm guessing that's the reason for the error.

I'd appreciate it if anyone could provide some helpful information on this. This is my first foray into threads, delegates, and Invoke, so be gentle! :D

Thanks
 
I was told by an MS moderator at the MSDN forums that the delegate must be an instance of EventHandler, so I can't Invoke a method.

I'm wondering if there's some other way to accomplish what I'm trying to do: from the DataRecieved event of the scanner class/thread call a method in a form that interacts with a control.

There's got to be some way to do it...
 
Not sure whether this is applicable to CF, but I had the same sort of trouble with barcode scanners / network printing etc etc.

My solution (aka fudge to get around the problem) would be to suggest something like this:-

1. Take the scanner class off the main form, and put it into the ApplicationEvents. This means your barcode reader will always work, irrespective of form.

2. Place the received barcode in a Queue (of T).

3. Use your form to process the Queue (ie process the barcode and do whatever you want with it).

I think it should work - but dont hold me to it :confused:
 
Hey, InertiaM. Thanks for replying.

Please see my comments to your post:

1. Take the scanner class off the main form, and put it into the ApplicationEvents. This means your barcode reader will always work, irrespective of form.

It's already in its own class. I didn't include that code in my post, but on the main form's load event the scanner class is instantiated. The scanner itself is an always-on model, so the DataRecieved event (which is in the scanner class) fires whenever a barcode is put in the scanner's reading area.

2. Place the received barcode in a Queue (of T).

3. Use your form to process the Queue (ie process the barcode and do whatever you want with it).

I'm not sure what you intend by suggesting placing it in a "Queue (of T)", unless, having it in the separate class as mentioned above, I'm already doing what you mean...
 
A timer method on your form (for example) would be able to process the Queue independently, so you should be able to do whatever you want (ie invoke) via the form.
 
Still not sure what you're referring to when you talk about a "Queue". Maybe some pseudocode would help?

Thanks!
 
ApplicationEvents :-
VB.NET:
    Partial Friend Class MyApplication


        Public BarcodesReceived As New Queue(Of String)

        Private Sub MyApplication_Startup(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
            BarcodesReceived.Enqueue("BARCODE 1")
            BarcodesReceived.Enqueue("BARCODE 2")
            BarcodesReceived.Enqueue("BARCODE 3")
            BarcodesReceived.Enqueue("BARCODE 4")
        End Sub

    End Class

Form 1 (with timer tick set to 1000ms and enabled):

VB.NET:
Public Class Form1

    Private FirstBarcodeInList As String

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        If My.Application.BarcodesReceived.Count = 0 Then
            'no barcodes to process
            Exit Sub
        End If
        FirstBarcodeInList = My.Application.BarcodesReceived.Dequeue
        Label1.Text = FirstBarcodeInList 'no invoke required!
    End Sub

End Class
 
' Error "ArgumentException"
frm.Invoke(New frmMain.RunCommandCodeInvoker(AddressOf frm.RunCommandCode), cc)
Invoke want Object array (param). Give it Object array. RunCommandCodeInvoker want Object array. Give it Object array.
VB.NET:
Dim cc As New CommandCode
Dim objcc() As Object = {cc}
Me.Invoke(New RunCommandCodeInvoker(AddressOf Me.RunCommandCode), New Object() {objcc})
If you just want to pass a CommandCode object you can use this signature:
VB.NET:
Public Delegate Sub RunCommandCodeInvoker(ByVal cmd As CommandCode)
VB.NET:
Dim cc As New CommandCode
Me.Invoke(New RunCommandCodeInvoker(AddressOf Me.RunCommandCode), cc)
 
JohnH

You're AWESOME!!!!

That was it! I used your code passing the CommandCode object and it worked!!!!

Thank you!!!!

Think I used enough exclamation points?!?!!!!!!!!!!!!!!

Okay, I'm done.

!
;)
 
Back
Top