你在ATL中实现多线程,而且想在工作线程中触发事件,显示工作的状态么?
前些日子,一直饱受这个问题困扰,今天太开心,无意被我看这个篇文章,帮我解决了这个问题!
虽然问题解决了,但并不是非常理解!故而复制了该文件,贴于此,以供仔细研究!
非常感谢原文作者--Michael Lindig !
原文出处:http://www.codeguru.com/Cpp/COM-Tech/atl/atl/article.php/c75
原文如下:
ATL: Firing Events from Worker Threads
Michael Lindig (view profile) July 16, 2000 |
If You have ever the problem : "VB client keep crashing when compiled and not in the IDE if it has a worker thread" then here is a possible solution
Specialized class CComDynamicUnkArray_GIT
class CComDynamicUnkArray_GIT : public CComDynamicUnkArray { private: IGlobalInterfaceTable* GIT; public: CComDynamicUnkArray_GIT() : CComDynamicUnkArray() { GIT = NULL; CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, __uuidof(IGlobalInterfaceTable), reinterpret_cast< void** >(&GIT) ); } ~CComDynamicUnkArray_GIT() { //clean up the class clear(); if( GIT != NULL ) { GIT->Release(); } } DWORD Add(IUnknown* pUnk); BOOL Remove(DWORD dwCookie); //The proxy code use this function to get the interface ! CComPtr GetAt(int nIndex) { DWORD dwCookie = (DWORD)CComDynamicUnkArray::GetAt( nIndex ); if( dwCookie == 0 ) return NULL; if( CookieMap.find( dwCookie ) == CookieMap.end() ) { return (IUnknown*)dwCookie; } if( GIT != NULL ) { CComPtr ppv; HRESULT hr = GIT->GetInterfaceFromGlobal( CookieMap[dwCookie], //Cookie identifying the desired global //interface and its object __uuidof(IUnknown), //IID of the registered global interface reinterpret_cast< void** >(&ppv) //Indirect pointer //to the desired interface ); if( hr == S_OK ) { return ppv; } //Should never be reached, a ASSERT or exception is possible } return (IUnknown*)dwCookie; } //clean up the GIT void clear() { CComDynamicUnkArray::clear(); if( GIT != NULL ) { map< DWORD, DWORD >::iterator iter; for (iter = CookieMap.begin(); iter != CookieMap.end(); ++iter ) { GIT->RevokeInterfaceFromGlobal( iter->second //Cookie that was returned from //RegisterInterfaceInGlobal ); } } CookieMap.clear(); } protected: map< DWORD, DWORD > CookieMap; }; inline DWORD CComDynamicUnkArray_GIT::Add(IUnknown* pUnk) { DWORD Result = CComDynamicUnkArray::Add( pUnk ); HRESULT hr; DWORD pdwCookie = 0; if( GIT != NULL ) { hr = GIT->RegisterInterfaceInGlobal( pUnk, //Pointer to interface of type riid //of object containing global interface __uuidof(IUnknown), //IID of the interface to be registered &pdwCookie //Supplies a pointer to the cookie that //provides a caller in another apartment //access to the interface pointer ); } if( hr == S_OK ) { CookieMap[Result] = pdwCookie; } return Result; } inline BOOL CComDynamicUnkArray_GIT::Remove(DWORD dwCookie) { BOOL Result = CComDynamicUnkArray::Remove( dwCookie ); if( GIT != NULL ) { if( CookieMap.find( dwCookie ) != CookieMap.end() ) { GIT->RevokeInterfaceFromGlobal( CookieMap[dwCookie] //Cookie that was returned from //RegisterInterfaceInGlobal ); CookieMap.erase(dwCookie); } } return Result; }
Changes in proxy generated files:
Change:
template <class T> class CProxy_ISchedulerEvents : public IConnectionPointImpl<T, &DIID__ISchedulerEvents, CComDynamicUnkArray>
To
#include "CProxyEvent.h" template <class T> class CProxy_ISchedulerEvents : public IConnectionPointImpl<T, &DIID__ISchedulerEvents, CComDynamicUnkArray_GIT>
In the proxy generated event method you must make a replace as follow:
Change:
VOID Fire_Activate(IBundle * pBundle, BSTR ActivationTime) { T* pT = static_cast< T* >(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[2]; int nConnections = m_vec.GetSize(); for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++) { pT->Lock(); CComPtr< IUnknown > sp = m_vec.GetAt(nConnectionIndex); pT->Unlock(); IDispatch* pDispatch = reinterpret_cast< IDispatch* >(sp.p); if (pDispatch != NULL) { pvars[1] = pBundle; pvars[0] = ActivationTime; DISPPARAMS disp = { pvars, NULL, 2, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL); } } delete[] pvars; }
To:
VOID Fire_Activate(IBundle * pBundle, BSTR ActivationTime) { T* pT = static_cast< T* >(this); int nConnectionIndex; CComVariant* pvars = new CComVariant[2]; int nConnections = m_vec.GetSize(); for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++) { pT->Lock(); CComPtr< IUnknown > sp = m_vec.GetAt(nConnectionIndex); pT->Unlock(); CComQIPtr< IDispatch > pDispatch( sp ); if (pDispatch != NULL) { pvars[1] = pBundle; pvars[0] = ActivationTime; DISPPARAMS disp = { pvars, NULL, 2, 0 }; pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL); } } delete[] pvars; }
Simple make a replace with IDispatch* pDispatch = reinterpret_cast< IDispatch* >(sp.p) to CComQIPtr< IDispatch > pDispatch( sp ), that's all !