调用IConnectionPoint :: Advise会导致崩溃

时间:2015-06-10 08:17:14

标签: c++ com rpc

在我的项目中,我编写了一个将在服务(COM服务器)中调用的组件。还有另一个进程将获得此组件的接口并通过连接点注册回调接口。因此,服务可以使用回调接口来做一些反馈。

但是我发现当我使用IConnectionPoint :: Advise来注册我的回调接口时会导致崩溃。

我手动实现连接点。以下是我的代码的一部分:

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

在客户端,我已经获得了组件接口,然后我得到了连接点并尝试调用函数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);

...

所有接口指针都可以成功获得,但是当我调用Advise时,会发生崩溃。奇怪的是在Advise实现中没有发生崩溃,似乎发生在RPC调用过程中,调用堆栈是这样的:

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
...

但是如果Advise的第一个参数是NULL,则可以执行Advise函数。

以下是客户端中实现的Sink代码:

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;
    }
};

接口_IEvents在组件中生成,该组件将用作连接点中的回调。我只是使用这个类来测试连接点,所以实现非常简单。

我不知道这次崩溃是怎么发生的。 谢谢你的帮助!

1 个答案:

答案 0 :(得分:2)

问题可能出在您的CDataCallback课程中。 QueryInterface实现是不安全的,并且错误地返回接口指针。

class CDataCallback : public _IEvents

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

如果请求的界面不是iid,您必须检查E_NOINTERFACE并返回_IEvents。基本上你可以在那里设置一个断点,看看你是否在崩溃之前有一个呼叫并检查参数。

您的接收器可能会被查询其他接口(IMarhsal等)并且您必须正确地指示您没有实现它们,而不是返回不匹配的指针,使用它会导致未定义的行为。