用vc封装COM接口给VB调用时,在com里面使用了异步回调事件通知给VB程序。VB程序在调试的时候是没有问题的,但是编译成release后直接运行exe程序后却直接崩溃掉。这个问题郁闷了我好久,也查阅了很多资料终于发现原来是多线程搞的鬼。在VB程序里不能使用多线程回调给他,不过还是终于找到了解决方案,特此记录。
封装COM接口时,需要使用微软提供的ATLCPImplMT.h,后边我会将整个头文件的内容放在该文章的尾部。把ATLCPImplMT.h添加到项目工程中。
需要注意的是。使用ATLCPImplMT.h这个头文件需要在vs2008下使用,在vs2010以上的版本都会报错编译不通过。
第一步:
找到项目_xxxxxEvents_CP.h
template<class T>
class CProxy_IXXXEvents :
public IConnectionPointImpl<T, &__uuidof(_IXXXEvents)>
改为:
template<class T>
class CProxy_IXXXEvents :
public IConnectionPointImplMT<T, &__uuidof(_IXXXEvents), CComDynamicUnkArray>
要记得引入头文件 #include "ATLCPImplMT.h"
第二步:
在CProxy_IXXXEvents类实现中,修改触发函数Fire_XXXX(…),将
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
修改为:
CComPtr<IUnknown> punkConnection;
punkConnection.Attach (GetInterfaceAt(iConnection));
第三步:
在调用Fire_XXXX(…)的地方加上CoInitializeEx(NULL, COINIT_MULTITHREADED); 和 CoUninitialize();
例如:
//异步回调函数
void CallBack(BSTR vParam)
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
Fire_XXXX(vParam);
CoUninitialize();
}
将下以代码另存为:ATLCPImplMT.h,然后将ATLCPImplMT.h添加到项目中
// This is a supplement to the Active Template Library 3.0.
// Copyright (C) 2000 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended for illustration.
#ifndef __CPIMPLMT_H__
#define __CPIMPLMT_H__
#include <atlcom.h>
//
// IConnectionPointImplMT: Used instead of IConnectionPointImpl:
template <class T, const IID* piid, class CDV = CComDynamicUnkArray>
class ATL_NO_VTABLE IConnectionPointImplMT : public _ICPLocator<piid>
{
typedef CComEnum<IEnumConnections, &IID_IEnumConnections, CONNECTDATA,
_Copy<CONNECTDATA> > CComEnumConnections;
// Use CDV, but store DWORDs instead of IUnknown pointers.
// ASSUMPTION: sizeof(DWORD) == sizeof(IUnknown *):
typedef CDV _CDV;
public:
// Added constructor:
IConnectionPointImplMT();
~IConnectionPointImplMT();
STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject)
{
if (InlineIsEqualGUID(riid, IID_IConnectionPoint) || InlineIsEqualUnknown(riid))
{
if (ppvObject == NULL)
return E_POINTER;
*ppvObject = this;
AddRef();
#ifdef _ATL_DEBUG_INTERFACES
_Module.AddThunk((IUnknown**)ppvObject, _T("IConnectionPointImplMT"), riid);
#endif // _ATL_DEBUG_INTERFACES
return S_OK;
}
else
return E_NOINTERFACE;
}
STDMETHOD(GetConnectionInterface)(IID* piid2)
{
if (piid2 == NULL)
return E_POINTER;
*piid2 = *piid;
return S_OK;
}
STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC)
{
T* pT = static_cast<T*>(this);
// No need to check ppCPC for NULL since QI will do that for us
return pT->QueryInterface(IID_IConnectionPointContainer, (void**)ppCPC);
}
STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie);
STDMETHOD(Unadvise)(DWORD dwCookie);
STDMETHOD(EnumConnections)(IEnumConnections** ppEnum);
CDV m_vec;
// Using the GIT for access across threads:
IGlobalInterfaceTable *m_pGIT;
// Need a separate critical section object:
CComGlobalsThreadModel::AutoCriticalSection m_CPMTCritSec;
// For each generated function (named Fire_{EventName}) within the proxy class:
// Comment out the following lines, within the generated for loop:
// pT->Lock();
// CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
// pT->Unlock();
//
// Instead, use this function as follows:
// CComPtr<IUnknown> sp;
// sp.Attach (GetInterfaceAt(nConnectionIndex));
LPUNKNOWN GetInterfaceAt(int nConnectionIndex);
};
template <class T, const IID* piid, class CDV>
IConnectionPointImplMT<T, piid, CDV>::IConnectionPointImplMT()
{
// Get the GIT per-process singleton:
CoCreateInstance(CLSID_StdGlobalInterfaceTable, 0,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
reinterpret_cast<void**>(&m_pGIT));
ATLASSERT(m_pGIT != NULL);
}
template <class T, const IID* piid, class CDV>
IConnectionPointImplMT<T, piid, CDV>::~IConnectionPointImplMT()
{
// Revoke interfaces from the GIT:
DWORD* pDWCookie = (DWORD *)(m_vec.begin());
while (pDWCookie < (DWORD *) (m_vec.end()))
{
if (*pDWCookie != NULL)
{
m_pGIT->RevokeInterfaceFromGlobal(*pDWCookie);
}
pDWCookie++;
}
}
template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImplMT<T, piid, CDV>::Advise(IUnknown* pUnkSink,
DWORD* pdwCookie)
{
IUnknown* p;
HRESULT hRes = S_OK;
if (pUnkSink == NULL || pdwCookie == NULL)
return E_POINTER;
IID iid;
GetConnectionInterface(&iid);
hRes = pUnkSink->QueryInterface(iid, (void**)&p);
if (SUCCEEDED(hRes))
{
m_CPMTCritSec.Lock();
DWORD dwGITCookie;
hRes = m_pGIT->RegisterInterfaceInGlobal(
p, iid, &dwGITCookie);
if(hRes == S_OK)
{
// Using the CCom(Dynamic)UnkArray to store the cookie instead of an IUnknown *:
*pdwCookie = m_vec.Add(reinterpret_cast<IUnknown *>(dwGITCookie));
hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
if (hRes != S_OK)
m_pGIT->RevokeInterfaceFromGlobal(dwGITCookie);
}
m_CPMTCritSec.Unlock();
// GIT will have AddRef'ed p:
p->Release();
}
else if (hRes == E_NOINTERFACE)
hRes = CONNECT_E_CANNOTCONNECT;
if (FAILED(hRes))
*pdwCookie = 0;
return hRes;
}
template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImplMT<T, piid, CDV>::Unadvise(DWORD dwCookie)
{
m_CPMTCritSec.Lock();
//DWORD dwGITCookie = (DWORD)_CDV::GetUnknown(dwCookie);
DWORD dwGITCookie = (DWORD)m_vec.GetUnknown(dwCookie);
HRESULT hRes = m_vec.Remove(dwCookie) ? S_OK : CONNECT_E_NOCONNECTION;
m_CPMTCritSec.Unlock();
if (hRes == S_OK && dwGITCookie != NULL)
{
hRes = m_pGIT->RevokeInterfaceFromGlobal(dwGITCookie);
}
return hRes;
}
template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointImplMT<T, piid, CDV>::EnumConnections(
IEnumConnections** ppEnum)
{
if (ppEnum == NULL)
return E_POINTER;
*ppEnum = NULL;
CComObject<CComEnumConnections>* pEnum = NULL;
ATLTRY(pEnum = new CComObject<CComEnumConnections>)
if (pEnum == NULL)
return E_OUTOFMEMORY;
m_CPMTCritSec.Lock();
CONNECTDATA* pcd = NULL;
ATLTRY(pcd = new CONNECTDATA[m_vec.end()-m_vec.begin()])
if (pcd == NULL)
{
delete pEnum;
m_CPMTCritSec.Unlock();
return E_OUTOFMEMORY;
}
CONNECTDATA* pend = pcd;
// Copy the valid CONNECTDATA's
for (DWORD* pDWCookie = (DWORD *)(m_vec.begin());pDWCookie<(DWORD *)(m_vec.end());pDWCookie++)
{
if (*pDWCookie != NULL)
{
IID iid;
GetConnectionInterface(&iid);
IUnknown *pUnk;
HRESULT hr = m_pGIT->GetInterfaceFromGlobal(
*pDWCookie, iid, reinterpret_cast<void **>(&pUnk));
if(hr == S_OK)
{
// AddRef() implicit in GetInterfaceFromGlobal():
pend->pUnk = pUnk;
//pend->dwCookie = _CDV::GetCookie(reinterpret_cast<IUnknown **>(pDWCookie));
pend->dwCookie = m_vec.GetCookie(reinterpret_cast<IUnknown **>(pDWCookie));
pend++;
}
}
}
// don't copy the data, but transfer ownership to it
pEnum->Init(pcd, pend, NULL, AtlFlagTakeOwnership);
m_CPMTCritSec.Unlock();
HRESULT hRes = pEnum->_InternalQueryInterface(IID_IEnumConnections, (void**)ppEnum);
if (FAILED(hRes))
delete pEnum;
return hRes;
}
template <class T, const IID* piid, class CDV>
LPUNKNOWN IConnectionPointImplMT<T, piid, CDV>::GetInterfaceAt(
int nConnectionIndex)
{
m_CPMTCritSec.Lock();
LPUNKNOWN pUnk = NULL;
// IConnectionPointImplMT Vector stores DWORDs instead of IUnknown pointers,
// explicit cast required:
DWORD dwGITCookie = (DWORD)(m_vec.GetAt(nConnectionIndex));
if (dwGITCookie != NULL)
{
IID iid;
GetConnectionInterface(&iid);
HRESULT hr = m_pGIT->GetInterfaceFromGlobal(
dwGITCookie, iid, reinterpret_cast<void **>(&pUnk));
ATLASSERT(hr == S_OK);
}
m_CPMTCritSec.Unlock();
return pUnk;
}
#endif // __CPIMPLMT_H__