调用 IConnectionPoint::Advise 导致崩溃
Call IConnectionPoint::Advise cause a crash
在我的项目中,我编写了一个将在服务(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在组件中生成,将用作连接点的回调。我只是用这个class来测试连接点,所以实现起来很简单。
我不知道这次崩溃是怎么发生的。
感谢您的帮助!
问题很可能出在您的 CDataCallback
class 中。它的 QueryInterface
实现不安全并且 returns 接口指针不正确。
class CDataCallback : public _IEvents
HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
{
*ppInterface = this;
return S_OK;
}
如果请求的接口不是 _IEvents
,您必须检查 iid
和 return E_NOINTERFACE
。基本上你可以在那里设置一个断点,看看你是否在崩溃之前在那里有一个调用并检查参数。
您的接收器可能会被查询其他接口(IMarhsal
等),您必须正确地表明您没有实现它们,而不是 returning 不匹配的指针,使用它会导致未定义的行为。
在我的项目中,我编写了一个将在服务(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在组件中生成,将用作连接点的回调。我只是用这个class来测试连接点,所以实现起来很简单。
我不知道这次崩溃是怎么发生的。 感谢您的帮助!
问题很可能出在您的 CDataCallback
class 中。它的 QueryInterface
实现不安全并且 returns 接口指针不正确。
class CDataCallback : public _IEvents
HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
{
*ppInterface = this;
return S_OK;
}
如果请求的接口不是 _IEvents
,您必须检查 iid
和 return E_NOINTERFACE
。基本上你可以在那里设置一个断点,看看你是否在崩溃之前在那里有一个调用并检查参数。
您的接收器可能会被查询其他接口(IMarhsal
等),您必须正确地表明您没有实现它们,而不是 returning 不匹配的指针,使用它会导致未定义的行为。