Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win


Raising events with asynchronous callback from a dll

Posted on 2007-04-05
Medium Priority
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
Question by:brownpeterg
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 3
LVL 48

Expert Comment

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.

Author Comment

ID: 18862919
    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.
LVL 48

Accepted Solution

AlexFM earned 1200 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
Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.


Author Comment

ID: 18869567
          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


LVL 48

Expert Comment

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.

Author Comment

ID: 18875040
        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)
            stbxStatusInfo.AppendText(stbxExtraInfoText & vbCrLf)
        End If
     End Sub

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

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

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

Partial Public Class vbfrm
    Public Shared Sub HandleData(ByVal returnString As String)
    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

Author Comment

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

Author Comment

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?))

Featured Post

Free learning courses: Active Directory Deep Dive

Get a firm grasp on your IT environment when you learn Active Directory best practices with Veeam! Watch all, or choose any amount, of this three-part webinar series to improve your skills. From the basics to virtualization and backup, we got you covered.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

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:…
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Is your data getting by on basic protection measures? In today’s climate of debilitating malware and ransomware—like WannaCry—that may not be enough. You need to establish more than basics, like a recovery plan that protects both data and endpoints.…

604 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