Call IConnectionPoint::Advise cause a crash

898 views Asked by At

In my project, I wrote a component which will be called in a service(COM server). There is another process will get this component's interface and register a callback interface through connection point. So the service could use the callback interface to do some feedback.

But I found that when I use the IConnectionPoint::Advise to register my callback interface cause a crash.

I implement the connection point manually. Here are part of my code:

class ATL_NO_VTABLE CUploadManager :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CUploadManager, &CLSID_UploadManager>,
public IUploadManager, 
public IConnectionPointContainer, 
public IConnectionPoint

...

// IConnectionPointContainer Functions
STDMETHODIMP EnumConnectionPoints(IEnumConnectionPoints **ppEnum);
STDMETHODIMP FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP);

// IConnectionPoint Functions
STDMETHODIMP GetConnectionInterface(IID *pIID);
STDMETHODIMP GetConnectionPointContainer(IConnectionPointContainer **ppCPC);
STDMETHODIMP Advise(IUnknown *pUnkSink, DWORD *pdwCookie);
STDMETHODIMP Unadvise(DWORD dwCookie);
STDMETHODIMP EnumConnections(IEnumConnections **ppEnum);

In the client, I have get the component interface already, then I get the connection point and try to call function Advise:

...
CComPtr<IConnectionPointContainer> pCPC;
hr = pUnknown->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pCPC);
if (FAILED(hr))
{
    return ;
}

CComPtr<IConnectionPoint> pConnectionPoint;
hr = pCPC->FindConnectionPoint(__uuidof(_IEvents), &pConnectionPoint);
if (SUCCEEDED(hr))
{
    DWORD dwCookie = 0;
    CDataCallback* pCallback = new CDataCallback();
    IUnknown* pUnknown = NULL;
    pCallback->QueryInterface(IID_IUnknown, (void**)&pUnknown);
    hr = pConnectionPoint->Advise(pUnknown, &dwCookie);

...

All interface pointers could be got successfully, but when I call Advise, the crash occurs. The strange thing is the crash is not happened in the Advise implementation, it seems occurs in the RPC call process, the call stack is like this:

003d0070() <----crash here
combase.dll!76eea3ab()
[Frames below may be incorrect and/or missing, no symbols loaded for combase.dll]
combase.dll!76ee9d00()
rpcrt4.dll!7612a53d()
mfc90ud.dll!CDialog::OnCmdMsg
...

But if the first parameter of Advise is NULL, the function Advise could be executed.

Here is the Sink code implemented in client:

class CDataCallback : public _IEvents
{
public:
    CDataCallback() {}
    ~CDataCallback() {}

    HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
    {
        *ppInterface = this;
        return S_OK;
    }

    ULONG STDMETHODCALLTYPE AddRef()
    {
        return 1;
    }

    ULONG STDMETHODCALLTYPE Release()
    {
        return 0;
    }

    HRESULT __stdcall TestFunc()
    {
        ...
        return S_OK;
    }
};

The interface _IEvents is generated in the component which will be used as a callback in the connection point. I just use this class to test the connection point, so the implementation is very simple.

I have no idea about how this crash occurs. Thanks for all your help!

1

There are 1 answers

1
Roman Ryltsov On BEST ANSWER

The problem is likely to be in your CDataCallback class. It's QueryInterface implementation is unsafe and returns interface pointer incorrectly.

class CDataCallback : public _IEvents

HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
{
  *ppInterface = this;
  return S_OK;
}

You have to check iid and return E_NOINTERFACE in case requested interface is not _IEvents. Basically you can set a breakpoint there and see if you have a call there prior to crash and check the arguments.

Your sink might be queried for other interfaces (IMarhsal etc) and you have to properly indicate that you are not implementing them, instead of returning unmatching pointer, use of which leads to undefined behavior.