设置我的ATL COM回调函数

时间:2017-03-24 18:27:09

标签: c++ visual-c++ com atl idl

this question的后续内容,我有以下CerberusNative.idl文件(这是一个用Visual C ++编写的暴露COM对象的ATL项目):

[
    object,
    uuid(AECE8D0C-F902-4311-A374-ED3A0EBB6B49),
    nonextensible,
    pointer_default(unique)
]
interface ICallbacks : IUnknown
{
    [id(1)] HRESULT UserExit([in] int errorCode, [in] BSTR errorMessage);
    [id(2)] HRESULT UserAttemptingReconnection();
    [id(3)] HRESULT UserReconnected();
};

[
    object,
    uuid(B98A7D3F-651A-49BE-9744-2B1D8C896E9E),
    dual,
    nonextensible,
    pointer_default(unique)
]
interface ICerberusSession : IDispatch {
    ...
    [id(5)] HRESULT SetCallbacks([in] ICallbacks* callbacks);
};

我正在尝试创建一个用于设置回调方法的接口,该回调方法从COM对象路由回调用者的所述方法的实现。

我正在尝试运行以下代码:

HRESULT prolonguedDisconnection(int code, BSTR *message) {
    std::wcout << code << ": " << message << std::endl;
}
HRESULT reconnecting() {
    std::wcout << "Reconnecting." << std::endl;
}
HRESULT reconnected() {
    std::wcout << "Reconnected." << std::endl;
}
...
CoInitialize(NULL);
CerberusNativeLib::ICallbacksPtr callbacks;
callbacks.CreateInstance(__uuidof(CerberusNativeLib::ICallbacks));
callbacks->UserExit = prolonguedDisconnection;
callbacks->UserAttemptingReconnection = reconnecting;
callbacks->UserReconnected = reconnected;
CerberusNativeLib::ICerberusSessionPtr session;
session.CreateInstance(__uuidof(CerberusNativeLib::CerberusSession));
session->SetCallbacks(callbacks);

但是,我不确定如何正确设置回调方法。关于如何做到这一点的任何想法?我在诸如callbacks->UserExit = prolonguedDisconnection;

之类的行上遇到此编译器错误
  

错误C2659&#39; =&#39;:作为左操作数运行

2 个答案:

答案 0 :(得分:3)

您将ICallbacks定义为接口,实现ICerberusSession的对象接受ICallbacks指针,以便它可以在某些事件上调用回调。这是一个很好的设计,效果很好。但是,它通常假定您的代码(底部的最后一个代码片段)通过CreateInstance实例化会话对象,而另一个具有回调方法的接口则在您身边实现。

您的代码实现了一个COM对象,该对象又实现了ICallbacks。您可以创建此类COM对象的实例(特别是没有CoCreateInstace - 它通常是您身边的客户端代码),并在ICallbacks调用中将SetCallbacks接口作为参数传递。请注意,不涉及任务。期望会话对象进行呼叫,例如提供指针的ICallbacks::UserExit,这就是您的代码通过回调接口获得控制的方式 - ICallbacks的客户端代码实现调用了UserExit方法。

答案 1 :(得分:-2)

我只需要在一个单独的类中实现接口:

class Callbacks : public CerberusNativeLib::ICallbacks {

#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
        const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}

MIDL_DEFINE_GUID(IID, IID_ICallbacks, 0xAECE8D0C, 0xF902, 0x4311, 0xA3, 0x74, 0xED, 0x3A, 0x0E, 0xBB, 0x6B, 0x49);

public:

    HRESULT(*user_exit) (int, BSTR) = NULL;
    HRESULT(*user_attempting_reconnection) () = NULL;
    HRESULT(*user_reconnected) () = NULL;

    Callbacks::Callbacks(HRESULT(*disconnected)(int, BSTR), HRESULT(*reconnecting)(), HRESULT(*reconnected)()) : m_cRef(0) {
        user_exit = disconnected;
        user_attempting_reconnection = reconnecting;
        user_reconnected = reconnected;
    }

    HRESULT __stdcall UserExit(int ErrorCode, BSTR ErrorMessage) {
        return user_exit(ErrorCode, ErrorMessage);
    }
    HRESULT __stdcall UserAttemptingReconnection() {
        return user_attempting_reconnection();
    }
    HRESULT __stdcall UserReconnected() {
        return user_reconnected();
    }
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR *ppvObject) {
        if (!ppvObject)
            return E_INVALIDARG;
        *ppvObject = NULL;
        if (riid == IID_IUnknown || riid == IID_ICallbacks)
        {
            *ppvObject = (LPVOID)this;
            AddRef();
            return NOERROR;
        }
        return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        InterlockedIncrement(&m_cRef);
        return m_cRef;
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG ulRefCount = InterlockedDecrement(&m_cRef);
        if (0 == m_cRef)
            delete this;
        return ulRefCount;
    }

private:
    ULONG m_cRef;
};

然后,使用它:

HRESULT result = CoInitialize(NULL);
if (result != S_OK)
{
    std::wcout << "Failed to initialize the COM library on the current thread or to identify the concurrency model as single-thread apartment (STA)." << std::endl;
    std::wcout << result << ": " << _com_error(result).ErrorMessage() << std::endl;
    std::wcout << "Press the enter key to exit." << std::endl;
    std::cin.get();
    return 0;
}
Callbacks *callbacks = new Callbacks(&prolonguedDisconnection, &reconnecting, &reconnected);
CerberusNativeLib::ICerberusSessionPtr session;
result = session.CreateInstance(__uuidof(CerberusNativeLib::CerberusSession));
if (result != S_OK)
{
    std::wcout << "Failed to create a new Cerberus session." << std::endl;
    std::wcout << result << ": " << _com_error(result).ErrorMessage() << std::endl;
    std::wcout << "Press the enter key to exit." << std::endl;
    std::cin.get();
    return 0;
}
result = session->SetCallbacks(callbacks);
if (result != S_OK)
{
    std::wcout << "Failed to set Cerberus session callbacks." << std::endl;
    std::wcout << result << ": " << _com_error(result).ErrorMessage() << std::endl;
    std::wcout << "Press the enter key to exit." << std::endl;
    std::cin.get();
    return 0;
}