Solved

Raising events with asynchronous callback from a dll

Posted on 2007-04-05
10
2,023 Views
Last Modified: 2013-11-26
This is a VB.Net Question
OK, so I've figured (with some help) out how to receive an asynchronous callback from a dll written in C++ using delegates.  But I've also just figured out that a) callbacks run on separate threads from the UI and b) I don't -need/want- a separate thread.  What I'd like to do is have the callback raise an event that interrupts rather than 'parallel' (a separate thread) the main thread (if possible).
So this is a three part question:
1)  Is it possible to for a callback to raise an event in the primary thread?
2) If this isn't possible, is there any way to use events for this purpose or should I simply resign myself to handling a separate thread for each callbacks?
3) Any code examples of how to do either would be great, thanks
pgb
0
Comment
Question by:brownpeterg
  • 5
  • 3
10 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 18862494
Callback function is always called in the context of calling thread. If C++ code is executed in a worker thread, callback is called in this thread.
However, there is easy way to marshal this call to main application thread - use Control.Invoke or Control.BeginInvoke methods. I don't think that C++ Dll is appropriate place to do this, it is much better to call Invoke or BeginInvoke from VB client.
0
 

Author Comment

by:brownpeterg
ID: 18862919
AlexFM,
    Thanks for responding.  Even if I wanted to I couldn't modify the dll.  Its a separately provided and while I have documentation re its functions, I have no insite into its code.  Also, I don't believe the begininvoke or invoke functions will help me either.  In order to receive an asynch callback I must 'register' the address of each of my callback functions (there are many) with the dll by using a separately providing function that gives the dll the address of each callback.  Then the dll can use the callback function addresses none, one or more times as it chooses.  Hope this clarifies.
pgb
0
 
LVL 48

Accepted Solution

by:
AlexFM earned 300 total points
ID: 18863026
Invoke or BeginInvoke functions can help you to call form functions from callback.
For example:

Delegate Sub CallbackDelegate(ByVal n1 As Integer, ByVal n2 As Integer)

Public Class MainForm Inherits System.Windows.Forms.Form

        ' this is callback running in the context of caller thread
        Private Sub CallbackFunction(ByVal n1 As Integer, ByVal n2 As Integer)
              Me.BeginInvoke( _
                  New  CallbackDelegate(AddressOf Me.HandleData), _
                  New Object() {n1, n2})
        End Sub

        ' This function runs in the main application thread and called indirectly from CallbackFunction
        Private Sub HandleData(ByVal n1 As Integer, ByVal n2 As Integer)
             ...
        End Sub

        ...

End Class
0
 

Author Comment

by:brownpeterg
ID: 18869567
AlexFM,
          Thanks again.  I'm much closer to the solution.  Your suggestion above works as long as I have my callback function in the form class.  
          Complexity and the desire to have all of my callbacks located in other classes, turns  "me.beginInvoke" into "form1.begininvoke" with requiste references to the (other) class where the CallbackDelegate delegate and HandleData function reside.  Specifically neither of the following  work.

Dim F1 as form1
    F1.BeginInvoke(New LocalCallBackDelegates.CallBackDelegate(AddressOf LocalCallBackDelegates.HandleData), New Object() {textString})
===The above compiles and runs fine but the output window shows the following:
A first chance exception of type 'System.NullReferenceException' occurred in VBproject.exe

Or Simply:
      form1.BeginInvoke(New LocalCallBackDelegates.CallBackDelegate(AddressOf LocalCallBackDelegates.HandleData), New Object() {textString})
===The above also compiles and runs fine but the output window shows the following:
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll

    I suppose I should have a better understanding of this, but how do I change statements like the above to reference (the form ) where the main thread is working to allow them to update that (or another) form's controls?

   This has been a real struggle for me.  Thanks for your help

Pete  

0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 48

Expert Comment

by:AlexFM
ID: 18869989
AddressOf LocalCallBackDelegates.HandleData

This is incorrect. You need to pass address of form method, something like this:

form1.BeginInvoke(New LocalCallBackDelegates.CallBackDelegate(AddressOf form1.HandleData), New Object() {textString})

HandleData should be Form1 method.
0
 

Author Comment

by:brownpeterg
ID: 18875040
AlexFM,
        Thanks once again!  I tried your latest suggestion in the context of the class set I have created and it wouldn’t work.  In fact I got the same error as before.  While I was able to use your previous suggestions to get the asynchronous callback to work and display its results in the primary/UI thread; I can only make this work when all of the delegate, callback and UI update code are all in the single form (ie the 'me' representation works)
           This would seem to be fine for some smaller apps that do not have but a few callbacks and a relatively limited set of controls on a single form.  But my app will have numerous callbacks and many controls on a multi-tabbed form.
           Again, I could make this work, but I would much prefer not to have all that code in the form class.  (What is absolutely necessary?) So then, how should I organize my code so that I can put all of the dll interface code is in a separate project in the solution.  The idea is to make the dll interface code readily transportable to other applications.  Obviously, it will be necessary to put some code in my own application, for example the unique code that performs work as a result of an asynchronous callback.  Ideally here, I like the asynchronous callbacks to act as if they were system events, that interrupt the main thread, execute and then return control back to the Main (UI) thread.

          To explain further what I’m doing, let me articulate the requirements I have and then I’ll show you the architecture for my code that I’m attempting to meet the requirements with.

1)   I have a DLL that I need to use in my program that interfaces to an IP address out on the web.  For the most part I look at this dll as local server to my app and a client to the remote IP address.
2)   The dll requires that I enable many different asynchronous callbacks in my code
3)   I must register my callbacks with the dll by give the dll the address of each callback subroutine using a function call to the dll. I do this of course, using delegates.
4)   Once registered the dll can use the callback subroutines as frequently or as seldom as it choses. Eg{0..N}
5)   The primary user interface consists of a single form that has numerous tabs, many controls and many underlying functions (before I even began to create an interface with the dll).
6)   So…I need to segregate the dll interface code (delegates, declarations of new delegate objects and callback functions in classes separate from the mainform (if possible?).  I need to do this for two reasons
           a.   The form code would become unwieldy and difficult to manage
           b.   Primarily, to all the dll interface code to become reuseable.  Here specifically, I need to keep my app code and my dll interface code as ‘loosely coupled’ as possible.


So I suppose my question is now how do I do this (specifically #6)?

Here, following; is the basic structure (its not functional code) of what I’ve described earlier and hopefully with some more clarity.  Hopefully this makes sense.  Again, I've researched this and tried to work it out but without success.  I did find some good information at the MSDN site that lists all of Mr Pattison's articles.


Solution: VB_and_CPP_DLL_Merge

Project1:  DLL interface Code
Class/File1:  DLL Function “Protoypes”--- Pertinent Code follows:

Public Class APIFunctions

      ‘the following function simply “registers” the callback function
    <DllImport(APIFileSpec, CharSet:=CharSet.Ansi)> Public Shared Function SetHostAddress
          (<MarshalAs(UnmanagedType.VBByRefStr)> ByRef strHostIPAddress As String,
           <MarshalAs(UnmanagedType.VBByRefStr)> ByRef strHostIPSocket As String)
           As Integer
    End Function

Class/File2:  DLL Constants--- Pertinent Code follows:

    ' Constants for callback types.
    Public Const HostLinkStateChange As Integer = 1
    Public Const PriceLinkStateChange As Integer = 2
    Public Const LogonStatus As Integer = 3

Class/File3:  Delegates— Pertinent Code follows:

Public Class Delegates
    Public Delegate Sub myLinkStateChange(ByRef LSS As LinkStateStruc)

Class/File4:  Structure Definitions— Pertinent Code follows:

Public Class StructureDefs
             <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)> Public Structure LinkStateStruc
                  <MarshalAs(UnmanagedType.U1)> Public OldState As Byte
                  <MarshalAs(UnmanagedType.U1)> Public NewState As Byte
             End Structure

Project2:  VBProject
Class/File1:  vbFrm---Pertinent Code Follows:

Public Delegate Sub MyUpdateDelegate(ByVal updateText As String)
Public Class vbfrm

    ‘the function below simply registers the callback address
    Private Sub butRegisterHostLinkStateCallback_Click(ByVal sender As System.Object, ByVal e As
         System.EventArgs) Handles butRegisterHostLinkStateCallback.Click
        Dim functionResult As Integer
        functionResult = RegisterLinkStateCallback(HostLinkStateChange, hostLinkStateCallBackDelegate)
    End Sub

    Public Sub updateStatusInfo(ByVal stbxExtraInfoText As String)
        If stbxStatusInfo.InvokeRequired Then
            Dim mad As New MyUpdateDelegate(AddressOf updateStatusInfo)
            Me.Invoke(mad, stbxExtraInfoText)
        Else
            stbxStatusInfo.AppendText(stbxExtraInfoText & vbCrLf)
            stbxStatusInfo.Invalidate()
        End If
     End Sub

Class/File2:  DeclarationsForRemoteDelegates---Pertinent Code Follows:

Partial Public Class vbfrm
    Public Shared hostLinkStateCallBackDelegate = New myLinkStateChange(AddressOf
           Event_HostLinkStateChange)

Class/File3:  vbfrm_Callbacks---Pertinent Code Follows:

Partial Public Class vbfrm
    Public Shared Sub HandleData(ByVal returnString As String)
        vbfrm.stbxStatusInfo.AppendText(returnString)
        vbfrm.stbxStatusInfo.AppendText(vbCrLf)
    End Sub

       Public Shared Sub Event_HostLinkStateChange(ByRef Data As LinkStateStruc)
    Dim textString As String
    textString = "HostLinkStateChange Variables Received:  OldState=" &
      getLinkStateCodeInfo(Data.OldState) & "  NewState= " &
      getLinkStateCodeInfo(Data.NewState) & vbCrLf
    vbfrm.BeginInvoke(New CallBackDelegate(AddressOf vbfrm.HandleData), New Object() {textString})
 End Sub
0
 

Author Comment

by:brownpeterg
ID: 18881400
This has turned out to be harder than I initially thought.
0
 

Author Comment

by:brownpeterg
ID: 18915564
The first answer that AlexFM helped a lot, but I was able to come up with the actual answer that I was looking for.  By using the using partial classes I am able to separate much of the functionality into separate files.  This helps code managment a good bit for me.  However, I am distressed that the only way that AlexFMs answer will accept the asynchronous callback in feed it into the UI is the the form that originated the callback. I keep thinking there must be some better way.....

((What I'd like to do is give AlexFM 300 points and myself 200 points.  But I can't see how to do this accepting "Multiple Answers" only allows me to see all of AlexFM's responses and non of my own...If an adminstrator looks at this, you help me out?))
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Today I had a very interesting conundrum that had to get solved quickly. Needless to say, it wasn't resolved quickly because when we needed it we were very rushed, but as soon as the conference call was over and I took a step back I saw the correct …
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now