Callback from C dll to VB6
Has anyone seen a working example of making an asynchronous callback from a C++ dll to VB6?
The steps are:
1. Set callback address by sending addressof subroutine to dll
2. dll stores this value in a global variable for later use
3. dll launches a seperate thread and returns control to VB
4. New thread performs some work and makes a callback to VB using the previously set address.
The problem is with the parameters passed back to VB
The logic works within the IDE
If compiled, the callback is made, but if I try to access the parameters passed, it crashes. This tells me there is something wrong with the stack?
Here is a demo code
C++
Code:
/ All declarations which are needed resides in "windows.h"
#include <windows.h>
// Placeholder for function-export macros
#define LIBRARY_EXPORT __declspec(dllexport) __stdcall
// Function Declarations for Static Export
extern "C" {
long LIBRARY_EXPORT GetDBData(void);
long LIBRARY_EXPORT SetCallbackProcAddr(long);
}
// Function Declaration for Callback function of the Calling App
//typedef void (CALLBACK* TDataReadNotify)(long);
typedef void (__stdcall *TDataReadNotify)(long);
/ Vars for Imported Functions
TDataReadNotify DataReadNotify;
HANDLE hThread;
DWORD pThreadId;
static DWORD DoTheWork(LPVOID lpParameter)
{
static long lDBData;
// Doing stuff that takes 5 sec., i.e. reading DB Data.
Sleep(5000);
lDBData = 123;
// Notify the caller app via CB-function.
if (DataReadNotify != NULL) DataReadNotify(lDBData);
return 0;
};
long LIBRARY_EXPORT GetDBData(void)
{
long lProcAdr;
// Get ProcAddress for Thread-Func and create/run the thread.
lProcAdr = (long)(&DoTheWork);
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)lProcAdr, NULL, 0, &pThreadId);
// Return to caller app.
return 0;
};
long LIBRARY_EXPORT SetCallbackProcAddr(long lProcAddress)
{
long Result = 0;
// Set the Proc-Address for the notify-func of the caller app.
DataReadNotify = (TDataReadNotify)lProcAddress;
if (DataReadNotify == NULL) return 0;
return 1;
};
VB Code:
Option Explicit
Private iWait As Long
Private sDBData As String
Private sMessage As String
Public Declare Function GetDBData Lib "DLL_Callback.dll" Alias "_GetDBData@0" () As Long
Public Declare Function SetCallbackProcAddr Lib "DLL_Callback.dll" Alias "_SetCallbackProcAddr@4" (ByVal lProcAddress As Long) As Long
Sub Main()
iWait = 1
' Send Address-Pointer Information to the DLL
Call SetCallbackProcAddr(AddressOf DataReadNotify)
' Call the function of the DLL, which reads DB-Info or something else.
GetDBData
' if the DLL-function is still busy, loop until the DLL notifies via the CALLBACK-function "DataReadNotify()".
Do While iWait = 1
DoEvents
Loop
iWait = iWait
End Sub
Public Sub DataReadNotify(ByVal lDBData As Long)
MsgBox "Sample App: " & CStr(lDBData), vbOKOnly + vbInformation
' Set Wait to 0, so that WinMain can go further.
iWait = 0
End Sub
Re: Callback from C dll to VB6
I think the problem is the MsgBox call. Since the callback in called through another thread you can't show blocking msgboxes.
Re: Callback from C dll to VB6
Yes, it is true that the messageBox call is causing an error so this is not a good example to illustrate the problem.
Suppose I start the program from a form rather than sub main.
The DataReadNotify sub will run in another thread than the form. I've found that I can set global variables from the DataReadNotify sub and even access text boxes on the form, but certain things cannot be done such as use Hex (i.e. Form1.text1 = Hex(LBDData)).
How is it that certain API functions can perform callbacks in the same thread as my main program? Is there some way I can execute a function in a running thread from outside the thread?
Re: Callback from C dll to VB6
I'm not sure. But for some reason I haven't had problem with many other callback functions. For example I've been able to call up MsgBoxes and such in a TimerProc callback after a SetTimer call. Maybe you could use that... in your callback procedure in VB you simply create a timer with a low interval, just enough for your callback to finish executing and then do the job in the callback procedure for the timer... Just a stupid work-around suggestion.
Re: Callback from C dll to VB6
Here's another idea if you're using Forms: Subclass your form and send it a message you've defined yourself each time the callback procedure is called. That way you can use the wParam or lParam arguments to pass on the data without storing them in global variables first.
Re: Callback from C dll to VB6
Yes, there are many work arounds that I could implement, but shouldn't I be able to build my callback so that it acts like callbacks from APIs? I would just like to know how they do it.
Quote:
For example I've been able to call up MsgBoxes and such in a TimerProc callback after a SetTimer call
This is exactly my point. If I don't have problems with callbacks MS devises why is mine not working?
Quote:
in your callback procedure in VB you simply create a timer with a low interval, just enough for your callback to finish executing and then do the job in the callback procedure for the timer.
I doubt that this would work since if the problem is as I expect that the thread has not been properly initialized, then a callback to an unintialized thread isn't going to work either. I may try it just to confirm my suspicions however. Thanks
Re: Callback from C dll to VB6
Quote:
Originally Posted by moeur
Quote:
in your callback procedure in VB you simply create a timer with a low interval, just enough for your callback to finish executing and then do the job in the callback procedure for the timer
I doubt that this would work since if the problem is as I expect that the thread has not been properly initialized, then a callback to an unintialized thread isn't going to work either. I may try it just to confirm my suspicions however. Thanks
Maybe you're right! However the subclassing and sending a message to your form approach should work, since it's then sent to the regular thread in the VB process.
Re: Callback from C dll to VB6
Code:
lProcAdr = (long)(&DoTheWork);
what do if i change DoTheWork to :
Code:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
I got Error when i do this
Code:
lProcAdr = (long)(GetMsgProc);
Re: Callback from C dll to VB6
You need the ampursand before the function name.
Code:
lProcAdr = (long)(&GetMsgProc)