I am trying to subscribe to MBN Events. Here is my code:
void subscribeToMbnEvents()
{
dwError = CoInitializeEx(NULL, COINIT_MULTITHREADED);
SAFEARRAY* mbnInterfaces;
CComPtr<IMbnInterfaceManager> intMgr = NULL;
dwError = CoCreateInstance(CLSID_MbnInterfaceManager, NULL, CLSCTX_ALL, IID_IMbnInterfaceManager, (void**)&intMgr);
if (dwError != ERROR_SUCCESS)
{
CoUninitialize();
std::cout << getTimeStamp() << " failed to initialize IMbnInterfaceManager \n";
}
dwError = intMgr->GetInterfaces(&mbnInterfaces);
if (dwError != ERROR_SUCCESS)
{
CoUninitialize();
std::cout << getTimeStamp() << " failed to get MBN Interfaces \n";
}
if (dwError == ERROR_SUCCESS)
{
LONG indexOfFirstMBNInterface;
dwError = SafeArrayGetLBound(mbnInterfaces, 1, &indexOfFirstMBNInterface);
if (dwError != ERROR_SUCCESS)
{
std::cout << getTimeStamp() << " failed to get first index of MBN Interface \n";
}
CComPtr<IMbnInterface> MbnInt = NULL;
dwError = SafeArrayGetElement(mbnInterfaces, &indexOfFirstMBNInterface, (void*)(&MbnInt));
if (dwError != ERROR_SUCCESS)
{
std::cout << getTimeStamp() << " failed to get MBN Interface \n";
}
IConnectionPointContainer* icpc;
dwError = intMgr->QueryInterface(IID_IMbnInterfaceManager, (void**)&icpc);
if (dwError != ERROR_SUCCESS)
{
std::cout << "Error querying interface" << std::endl;
}
IConnectionPoint *icp;
dwError = icpc->FindConnectionPoint(IID_IMbnInterfaceEvents, &icp);
if (dwError != ERROR_SUCCESS)
{
std::cout << "Error finding connection point" << std::endl;
}
}
}
Since the documentation is (imho) a Little bit lacking i oriented myself at some code samples i found on the net. up until i call FindConnectionPoint
everything works as it should. When calling FindConnectionPoint
i get an Access Violation writing to Memory so i guess the Problem is with my IConnectionPoint
pointer which is declared as in multiple code examples i found.
Hopefully someone with a Little bit more oversight is able to help with this. Thanks in advance
The code retrieving the IConnectionPointContainer
is wrong:
IConnectionPointContainer* icpc;
dwError = intMgr->QueryInterface(IID_IMbnInterfaceManager, (void**)&icpc);
// ^^^^^^^^^^^^^^^^^^^^^^^^ wrong interface ID
if (dwError != ERROR_SUCCESS)
{
std::cout << "Error querying interface" << std::endl;
}
This code returns an IMbnInterfaceManager
interface, but reinterprets it as an IConnectionPointContainer
. When it continues to execute icpc->FindConnectionPoint
it really is calling a random interface method of IMbnInterfaceManager
1.
To address this issue, the code needs to be changed to this:
IConnectionPointContainer* icpc = nullptr;
HRESULT hr = intMgr->QueryInterface(IID_ConnectionPointContainer, (void**)&icpc);
if (FAILED(hr))
{
std::cout << "Error querying interface" << std::endl;
}
It's easier and safer yet to use the IID_PPV_ARGS macro. It deduces the interface ID that matches the pointer type:
HRESULT hr = intMgr->QueryInterface(IID_PPV_ARGS(&icpc));
FindConnectionPoint
is the second entry in the IConnectionPointContainer
interface, i.e. the fifth entry in the v-table (accounting for the 3 IUnknown
methods). The same spot in the IMbnInterfaceManager
is occupied by the GetInterfaces method. It's first argument is an [out] parameter, so that explains the access violation on write.