【问题标题】:Callback from c++ to vb6 in UI Thread在 UI 线程中从 c++ 回调到 vb6
【发布时间】:2014-02-12 09:27:31
【问题描述】:

以下设置:

我有一个 .Net Dll,它有一个名为 LicenceVerifier 的异步方法 当方法完成时会触发一个事件。

 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();


    }

在 C++ 方面看起来是这样的:

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();

}

C++ 端的处理程序:

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

我现在遇到了问题,回调将在工作线程而不是 UI 线程中运行,这意味着显示消息框的 vb6 调用将失败。 我可以简单地在回调方法中写入一个变量,然后在 UI 线程中轮询变量以进行更改。不过不太喜欢这个主意。 任何人都有一个更清洁的解决方案的想法,可能让 c++ 端在 UI 线程中执行回调(如何?)?

非常感谢任何帮助。

【问题讨论】:

  • 中间的C++的目的是什么?您可以在 C# 中公开 COM 对象,以便 VB6 轻松使用。
  • 因为我们实际上想尽量减少 COM 组件的使用。 .Net dll 周围的 c++ 包装器是我们能够在 c++ 和 vb6 程序中使用 .net 代码的常用方法。
  • 如果您不打算在 C++ 中使用 COM,那么您需要一个隐藏窗口(静态)来发布消息,以便能够在 UI 线程上调用回调。
  • 这听起来像是一个计划,谢谢。如果您不介意,有机会举一个简短的例子吗?

标签: c# c++ asynchronous vb6 callback


【解决方案1】:

这里是如何使用隐藏的静态窗口在 UI 线程上发布 WM_USER 传递结果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);
}

【讨论】:

  • 谢谢这个例子,非常感谢。我昨天已经有了自己的解决方案,但你的解决方案对我来说看起来更干净。
猜你喜欢
  • 2019-07-11
  • 2015-03-06
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-22
相关资源
最近更新 更多