问题
A follow-up to this question, I have the following CerberusNative.idl
file (this is an ATL project written in Visual C++ which exposes a COM object):
[
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);
};
I am attempting to create an interface for setting up callback methods which route from the COM object back to an implementation of said methods from the caller.
I am trying to run the following code:
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);
However, I am not sure how to properly set up the callback methods. Any ideas on how to do this? I get this compiler error on lines such as callbacks->UserExit = prolonguedDisconnection;
:
Error C2659 '=': function as left operand
回答1:
You defined ICallbacks
as an interface and the object that implements ICerberusSession
accepts a ICallbacks
pointer so that it could invoke calls back on certain events. This is a good design and works well. However, it usually assumes that your code (last code snippet at the bottom) instantiates session object via CreateInstance
as you do, and the other interface with callback methods is implemented on your side.
Your code implements a COM object, which in turn implements ICallbacks
. You create an instance of such COM object (esp. without CoCreateInstace
- it's typically client code on your side) and you pass ICallbacks
interface as an argument in SetCallbacks
call. Note that there is no assignment involved. Session object is expected to do a call, e.g. ICallbacks::UserExit
on supplied pointer and this is how your code receives control through the callback interface - your client side code implementation of ICallbacks
has its UserExit
method called.
回答2:
I needed simply to implement the interface in a separate class:
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;
};
Then, to use it:
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;
}
来源:https://stackoverflow.com/questions/43006702/setting-up-my-atl-com-callback-functions