3

Asynchronous Callback Functions

Perspective: I am upgrading several VB6 ActiveX applications to C#.net, which all talk to each other using callback functions which they register with a referenced VC++.net executable.

I cannot replicate the following VB6 functionality in C#: VB6's ability to pass to VC++, an instantiated class containing a method, as a callback function parameter, which VC++ then registers as callback function for asynchronous communication.

The upgrade has gone very well apart from this one problem: CallBack Functions ... and I have been stuck on it now for two weeks. Please help me!!!

I have figured out how to pass a callback function as a delegate, which I have managed to get working with C# DynamicInvoke, however I really need this to work in VC++.

The error message I keep getting from the VC++ invoke statement is "Invalid Number of Parameters".


BELOW I have outlined the VB6 and VC++ functionality which handles the asynchronous callbacks. The VB6 ActiveX components are each passing a class containing a single method as a callback function to the VC++ Executable, which saves the callbacks in an array for later use. As this is the existing code, it works as expected.

The following is the VB6 Class Class1 being instantiated and used as a callback:
Please note: Attribute Notify.VB_UserMemId = 0

VERSION 1.0 CLASS
BEGIN
 MultiUse = -1       
 Persistable = 0 
 DataBindingBehavior = 0 
 DataSourceBehavior  = 0 
 MTSTransactionMode  = 0 
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
 
Sub Notify(ByVal message As Integer, ByVal data As Variant)
Attribute Notify.VB_UserMemId = 0
  MsgBox Str$(message) + "  " + data, vbInformation, Form1.Text2
End Sub

The above code has been simplified to avoid confusion.

Below is the VB6 code that instantiates the VC++ executable(VCCallbackHandler), and passes it the instantiated Class1 as the callback parameter

Dim VCCallbackHandler New VCCallbackHandler.VCCallbackHandler 
Dim c1 As New Class1

Private Sub Register_Click()
   Dim i as int
   i = VCCallbackHandler.Register(c1, "NameOfApplication")
End Sub

The VC++ code registers the callbacks (see below), and then later(asynchronously) the VC++ can utilise the callbacks, if prompted by some other event (see below 'BroadCast'). In this case the VC++ exe is acting as a central callback handler for several concurrantly running apps. Each app has registered their callback with the VC++ callback handler, and when one app prompts the VC++ callbackHandler by calling another event, all of the callbacks are invoked. In this way the callback handler is allowing all these other applications to communicate with each other.

Below is the relavant VC++.Net callback code.

Registering the callbacks:

 #define MAXREG  20

 typedef struct tagRegElement {
    char    name[20];       // Name of registered application
    _Callback   *p_Callback;    // Callback wrapper class
 } REG_ELEMENT;

public:
     REG_ELEMENT Registry[MAXREG];


short CBreqDlgAutoProxy::Register(LPDISPATCH callback, LPCTSTR name) 
{
    for (int i = 0;i<MAXREG;i++){
        if(!(theApp.Registry[i].name[0]))
        {
            RegIndex = i;
            strcpy(theApp.Registry[i].name,name);
            theApp.Registry[i].p_Callback = new _Callback(callback);
            return i;
        }
    }
 
    return -1;
 }

Invoking the callbacks:

 BOOL CBreqDlgAutoProxy::Broadcast(short message, const VARIANT FAR& data) 
 {
    for (int i = 0;i<MAXREG;i++){
        if(theApp.Registry[i].name[0] && (i != RegIndex)){
            if (!theApp.Registry[i].p_Callback->Notify(message,data,theApp.Registry[i].name))
                DeRegister(i);
        }
    }
    
    return TRUE;
 }

 BOOL _Callback::Notify(short message, VARIANT data, char* RegisteredName)
 {
    static BYTE parms[] = VTS_I2 VTS_VARIANT;
        
    InvokeHelper(0x0, DISPATCH_METHOD, VT_EMPTY, NULL, parms, message, &data);
     
    return TRUE;
 }

NOTE. THE ABOVE WORKS.

There are two possible solutions:

  1. C#: how to get C# to pass a method as a parameter. I figured out how to do it using a delegate, but the VC++ wants a method not a delegate.
  2. VC++: How to get VC++ to handle a delegate instead of a method as the callback to invoke.

I have had no success with any of the following c# code snippets: `

  • Marshal.GetFunctionPointerForDelegate
  • GCHandle
  • KeepAlive

I hope someone out there has had this problem, and can go... SNAP... its easy.. use this... Crossed fingers.

Community
  • 1
  • 1

1 Answers1

0

I did something like this sometime back , but I used it for passing variables .

C#: how to get C# to pass a method as a parameter. I figured out how to do it using a delegate, but the VC++ wants a method not a delegate.

I had used C++/CLI as C# code (managed ) cannot talk directly to VC++ code ( unmanaged ).

So if you can write a CLI/C++ wrapper , that would help.

PS. I would like to know how to pass the delegate to VC++. If you can post the code that would be great.

Sujay Ghosh
  • 2,670
  • 6
  • 27
  • 40
  • declare a delegate of a method. public delegate void NotifyDG(int message, string msgData); static void notify(int message, string msgData) { System.Windows.Forms.MessageBox.Show("test"); } then call the vc++ code passing in the delegate NotifyDG d = new NotifyDG(notify); i = VCCallbackHandler.Register(d,"NameOfApplication"); However, as I stated earlier.. while the registration step works, when I try to use the invokeHelper method it fails. – rap van winkle May 05 '11 at 06:33
  • nnfghfhhdhdgfsdgf... sorry couldn't make this prettier – rap van winkle May 05 '11 at 06:40
  • Thanks Rip. Did my solution work , maybe you can create a POC , with a function named add(int a, int b) and see if my approach works. – Sujay Ghosh May 05 '11 at 07:01
  • @Rip , Please go this tutorial - http://www.codeproject.com/KB/mcpp/quickcppcli.aspx – Sujay Ghosh May 06 '11 at 06:00