ATL 线程触发事件解决方案

步骤1

将下以代码另存为: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__

步骤2

找到项目_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>

 

在CProxy_IXXXEvents类实现中,修改触发函数Fire_XXXX(…),将   pThis->Lock();

                     CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

                     pThis->Unlock();

                     修改为:

                     CComPtr<IUnknown> punkConnection;

            punkConnection.Attach (GetInterfaceAt(iConnection));

步骤3

在你的调用线程中,记着要在线程开始和结束时初始化和卸载com库,调用CoInitializeEx(NULL, COINIT_MULTITHREADED);和CoUninitialize();函数;现在可以在其他线程中调用触发函数Fire_XXX函数了!

本人测试环境VS2008+SP1

原文地址:https://www.cnblogs.com/ytjjyy/p/2453352.html