Environment: VC6 SP4, NT4 SP3
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.
(continued)
In most cases of my work I develop ATL objects with worker threads. In the worker threads there must be often fire events
for signalling thread states.
The problem of firing events from worker thread is that they must enter another apartment. In this apartment the sink interfaces
are not valid and thats why some clients would not be receive the events (eg VB).
I found a simple solution for that. (A solution without PostMessage.)
Only the proxy code of wizard must be changed, to get the firing of events right. I use the global interface table (GIT) for my solution.
"The GIT holds a list of marshaled interface pointers that, on request can be unmarshaled any number of times to any number of apartment in your
process, regardless of whether the pointer is for an object or a proxy."(Professional ATL COM Programming, Dr Richard Grimes).
I wrote a new specialized class for CComDynamicUnkArray which use the GIT for accessing the sink interfaces.
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()
{
clear();
if( GIT != NULL )
{
GIT->Release();
}
}
DWORD Add(IUnknown* pUnk);
BOOL Remove(DWORD dwCookie);
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],
__uuidof(IUnknown),
reinterpret_cast< void** >(&ppv)
);
if( hr == S_OK )
{
return ppv;
}
}
return (IUnknown*)dwCookie;
}
void clear()
{
CComDynamicUnkArray::clear();
if( GIT != NULL )
{
map< DWORD, DWORD >::iterator iter;
for (iter = CookieMap.begin();
iter != CookieMap.end();
++iter )
{
GIT->RevokeInterfaceFromGlobal(
iter->second
);
}
}
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,
__uuidof(IUnknown),
&pdwCookie
);
}
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]
);
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 !
Downloads
Download CEventProxy.h source - 1 Kb
Download example source (inludes VBasic client) - 62 Kb