简体   繁体   中英

Invoke callback into VB6 from native C DLL

I am trying to get a native C function to invoke a callback in a VB6 application. In VB, the code looks like this:

Private Declare Sub StartUp Lib "library.dll" (ByVal cbAddress As Long)

Public Sub MySub
    Call StartUp(AddressOf MyCallback)
End Sub

Public Sub MyCallback(ByVal str As String)
' Use str in UI
End Sub

When I run this from the VB6 IDE everything works great: the callback is invoked and I can use the str parameter no problem. However, when I compile the VB6 application into an exe, the application will crash when the callback is invoked. From what I can tell in my Googling it seems this is a threading issue of some sort. Some people suggest using the Win32 SendMessage to avoid this threading issue somehow. I can't quite figure out how to do this. Would I call SendMessage in my callback? How would I receive this message on the main thread? Also, how would I pass my string into SendMessage?

Edit: I just found this: Vb6 application works in the IDE but the executable crashes

However, my initial questions still remain.

Edit2: I should have included in the C code as well. This is how the callback is defined and called:

typedef void (__stdcall *FUNCPTR)(BSTR);
int wslen = MultiByteToWideChar(CP_ACP, 0, cstr, strlen(cstr), 0, 0);
BSTR bstr = SysAllocStringLen(0, wslen);
MultiByteToWideChar(CP_ACP, 0, cstr, strlen(cstr), bstr, wslen);
((FUNCPTR) _cbAddress)(bstr);
SysFreeString(bstr);

Again, this works fine when invoked from the IDE...

Obviously, the ideal thing would be for you to ensure that the callback always occurs on the VB application's main thread. Failing this, you are forced to use SendMessage() to a window in your application to take advantage of Window's own marshalling ability between Windows.

Ages ago that I wrote a VB app which took a callback from a DLL which was used to integrate with a remote control. But the DLL used another thread to integrate with the hardware. In my callback, I used SendMessage() with WM_COPYDATA to marshall the data to the main VB thread, which was picked up by subclassing code.

Of course, since you are writing the C DLL and you are simply passing back ASCII text, you can skip a few stages and not have to write a callback procedure or have subclassing code. Simply write text directly to a TextBox with the SetWindowText() API call. Ensure you create this TextBox with Visible = False on a form which is always loaded. You then just need to use this code:

BOOL WINAPI SetWindowText(
  _In_      HWND hWnd,
  _In_opt_  LPCTSTR lpString
);

HWND m_hWndTextBox

void StartUp(HWND hWndTextBox)
{
    m_hWndTextBox = hWndTextBox;
    // other stuff
}

void Callback(char *cstr) {
    SetWindowText(m_hWndTextBox, cstr);
}

And the VB code:

' Form "Form1" with text box "Text1".

Private Declare Sub StartUp Lib "library.dll" (ByVal hWndTextBox As Long)

Public Sub MySub
    Call StartUp(Text1.hWnd)
End Sub

Private Sub Text1_Change()
    DoStuffWithDLLUpdateString Text1.Text
End Sub

And you don't even have to convert the string to a BSTR - VB does that for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM