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:
- 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.
- 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.
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.