Jan
Jan

Reputation: 139

Callback from c++ to vb6 in UI Thread

Following setup:

I got a .Net Dll that got an async method called LicenceVerifier An event will be fired when the method completed.

 public class LicenceVerifier
 {

    private readonly ILicence _licence;
    private readonly int _programkey;


    public delegate void LicencedCheckedEventHandler(object sender, LicenceVerifierResultArgs args);
    public event LicencedCheckedEventHandler LicencedChecked;

    public LicenceVerifier(int programKey)
    {
        _programkey = programKey;
       _licence =  GetLicensing();
    }


    public void IsValidLicenceAsync()
    {
        new Task(() =>
        {
            LicenceVerifierResult valid = LicenceVerifierResult.NotAvailable;

            if (_licence != null)
                valid = _licence.IsValid(_programkey);

            LicencedChecked(this, new LicenceVerifierResultArgs(valid));

        }).Start();


    }

On the C++ side it looks that way:

void __stdcall CheckLicenseAsyncVb(int iPrgKey, int cbAddress)
{

    LicenceVerifierCallback^ callback = gcnew LicenceVerifierCallback();
    callback->lCallbackAddress = cbAddress;
    callback->callbackType = VB;

    //.Net object
    LicenceVerifier^ licenceVerifier = gcnew LicenceVerifier(iPrgKey);
    licenceVerifier->LicencedChecked += gcnew LicenceVerifier::LicencedCheckedEventHandler(callback, &LicenceVerifierCallback::handler);
    licenceVerifier->IsValidLicenceAsync();

}

the handler on the C++ side:

public ref class LicenceVerifierCallback
{

public:

CallbackType callbackType;

long lCallbackAddress;
void(*callbackFunction)(int);

void handler(System::Object^ sender, LicenceVerifierResultArgs^ e)
{

    if(callbackType == VB)
        ExecuteCallbackVb(convertResult(e->Result));

    if(callbackType == C)
        ExecuteCallbackC(callbackFunction, convertResult(e->Result));
};

int convertResult(LicenceVerifierResult verifierResult)
{
    if(verifierResult == LicenceVerifierResult::Available)
        return 0;

    return 1;
}

void ExecuteCallbackVb(int result) 
{
    typedef void ( *FUNCPTR) (int iResult);

    FUNCPTR callBackFunction;

    callBackFunction = (FUNCPTR)lCallbackAddress;
    callBackFunction(result);
};

vb6:

Private Sub LicenceCheck_Click()
    Call CheckLicenseAsyncVb(20110, AddressOf ResultCallback)
End Sub

Public Declare Sub CheckLicenseAsyncVb Lib "LicensingIntfd.dll" Alias "_CheckLicenseAsyncVb@8" (ByVal prgKey As Long, ByVal address As Long)

Public Sub ResultCallback(ByVal result As Long) '
   'MsgBox "I'll be never a friend of vb6: " & result
End Sub

I got the problem now, that the callback will run in the workerthread and not the UI thread, means the vb6 call to show the messagebox gonna fail. I could simply write into a variable in the callback method and then poll the variable in the UI thread for a change. Don't really like that idea though. Anyone got an idea with a cleaner solution, probably get the c++ side to execute the callback already in the UI thread (how?)?

Any help is much appreciated.

Upvotes: 0

Views: 481

Answers (1)

wqw
wqw

Reputation: 11991

Here is how to use a hidden static window to post WM_USER on the UI thread passing result in wParam

// VC6
LRESULT CALLBACK RedirectWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

class LicenceVerifierVbCallback
{
    LPVOID m_cbAddress;
    HWND m_hWnd;
    WNDPROC m_pOrigWndProc;

public:
    LicenceVerifierVbCallback(LPVOID cbAddress) 
    {
        m_cbAddress = cbAddress;
        m_hWnd = CreateWindow("STATIC", NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0);
        SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
        m_pOrigWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)RedirectWndProc);
    }

    ~LicenceVerifierVbCallback()
    { 
        DestroyWindow(m_hWnd); 
    }

    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        if (msg == WM_USER)
            ExecuteCallbackVb((int)wParam);
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    void ExecuteCallbackVb(int result) 
    {
        // ToDo: impl
    }

    void handler(LPVOID sender, LPVOID e)
    {
        WPARAM wParam = 0; // wParam = convertResult(e->Result)
        PostMessage(m_hWnd, WM_USER, wParam, 0);
    }
};

LRESULT CALLBACK RedirectWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    LicenceVerifierVbCallback *pthis = (LicenceVerifierVbCallback *)GetWindowLong(hwnd, GWL_USERDATA);
    if (pthis != NULL)
        return pthis->WndProc(hwnd, msg, wParam, lParam);
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

Upvotes: 1

Related Questions