You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1685 lines
53 KiB
1685 lines
53 KiB
/******************************************************************************
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
MPC_COM.h
|
|
|
|
Abstract:
|
|
This file contains the declaration of various classes and macros to deal with COM.
|
|
|
|
Revision History:
|
|
Davide Massarenti (Dmassare) 06/18/99
|
|
created
|
|
|
|
******************************************************************************/
|
|
|
|
#if !defined(__INCLUDED___MPC___COM_H___)
|
|
#define __INCLUDED___MPC___COM_H___
|
|
|
|
#include <atlbase.h>
|
|
#include <atlcom.h>
|
|
|
|
#include <dispex.h>
|
|
|
|
#include <MPC_trace.h>
|
|
#include <MPC_main.h>
|
|
|
|
#include <process.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MPC_FORWARD_CALL_0(obj,method) return obj ? obj->method() : E_HANDLE
|
|
#define MPC_FORWARD_CALL_1(obj,method,a1) return obj ? obj->method(a1) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_2(obj,method,a1,a2) return obj ? obj->method(a1,a2) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_3(obj,method,a1,a2,a3) return obj ? obj->method(a1,a2,a3) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_4(obj,method,a1,a2,a3,a4) return obj ? obj->method(a1,a2,a3,a4) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_5(obj,method,a1,a2,a3,a4,a5) return obj ? obj->method(a1,a2,a3,a4,a5) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_6(obj,method,a1,a2,a3,a4,a5,a6) return obj ? obj->method(a1,a2,a3,a4,a5,a6) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_7(obj,method,a1,a2,a3,a4,a5,a6,a7) return obj ? obj->method(a1,a2,a3,a4,a5,a6,a7) : E_HANDLE
|
|
#define MPC_FORWARD_CALL_8(obj,method,a1,a2,a3,a4,a5,a6,a7,a8) return obj ? obj->method(a1,a2,a3,a4,a5,a6,a7,a8) : E_HANDLE
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_FAIL_IF_NOT_AN_OBJECT(var) \
|
|
if(((var).vt != VT_UNKNOWN && (var).vt != VT_DISPATCH) || \
|
|
((var).vt == VT_UNKNOWN && (var).punkVal == NULL ) || \
|
|
((var).vt == VT_DISPATCH && (var).pdispVal == NULL ) ) __MPC_SET_ERROR_AND_EXIT(hr, E_INVALIDARG)
|
|
|
|
#define MPC_SCRIPTHELPER_GET__DIRECT(dst,obj,prop) \
|
|
{ \
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, obj->get_##prop( &(dst) )); \
|
|
}
|
|
|
|
#define MPC_SCRIPTHELPER_GET__DIRECT__NOTNULL(dst,obj,prop) \
|
|
{ \
|
|
MPC_SCRIPTHELPER_GET__DIRECT(dst,obj,prop); \
|
|
if((dst) == NULL) __MPC_SET_ERROR_AND_EXIT(hr, E_NOINTERFACE); \
|
|
}
|
|
|
|
////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_GET_OBJECT(dst,obj,prop) \
|
|
{ \
|
|
CComPtr<IDispatch> __disp; \
|
|
\
|
|
MPC_SCRIPTHELPER_GET__DIRECT(__disp,obj,prop); \
|
|
if(__disp) \
|
|
{ \
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, __disp->QueryInterface( __uuidof(dst), (LPVOID*)&(dst) )); \
|
|
} \
|
|
}
|
|
|
|
#define MPC_SCRIPTHELPER_GET_OBJECT__NOTNULL(dst,obj,prop) \
|
|
{ \
|
|
MPC_SCRIPTHELPER_GET_OBJECT(dst,obj,prop); \
|
|
if((dst) == NULL) __MPC_SET_ERROR_AND_EXIT(hr, E_NOINTERFACE); \
|
|
}
|
|
|
|
////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_GET_OBJECT__VARIANT(dst,obj,prop) \
|
|
{ \
|
|
CComVariant __v; \
|
|
\
|
|
MPC_SCRIPTHELPER_GET__DIRECT(__v,obj,prop); \
|
|
\
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::COMUtil::VarToInterface( __v, __uuidof(dst), (IUnknown **)&(dst) )); \
|
|
}
|
|
|
|
#define MPC_SCRIPTHELPER_GET_STRING__VARIANT(dst,obj,prop) \
|
|
{ \
|
|
CComVariant __v; \
|
|
\
|
|
MPC_SCRIPTHELPER_GET__DIRECT(__v,obj,prop); \
|
|
\
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::COMUtil::VarToBSTR( __v, dst )); \
|
|
}
|
|
|
|
////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_GET_PROPERTY(dst,obj,prop) \
|
|
{ \
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::COMUtil::GetPropertyByName( obj, L#prop, dst )); \
|
|
}
|
|
|
|
#define MPC_SCRIPTHELPER_GET_PROPERTY__BSTR(dst,obj,prop) \
|
|
{ \
|
|
CComVariant __v; \
|
|
\
|
|
MPC_SCRIPTHELPER_GET_PROPERTY(__v,obj,prop); \
|
|
\
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::COMUtil::VarToBSTR( __v, dst )); \
|
|
}
|
|
|
|
////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_GET_COLLECTIONITEM(dst,obj,item) \
|
|
{ \
|
|
CComVariant __v; \
|
|
\
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, obj->get_Item( item, &__v )); \
|
|
\
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, MPC::COMUtil::VarToInterface( __v, __uuidof(dst), (IUnknown **)&(dst) )); \
|
|
if((dst) == NULL) __MPC_SET_ERROR_AND_EXIT(hr, E_NOINTERFACE); \
|
|
}
|
|
|
|
////////////////////
|
|
|
|
#define MPC_SCRIPTHELPER_PUT__DIRECT(obj,prop,val) \
|
|
{ \
|
|
__MPC_EXIT_IF_METHOD_FAILS(hr, obj->put_##prop( val )); \
|
|
}
|
|
|
|
#define MPC_SCRIPTHELPER_PUT__VARIANT(obj,prop,val) \
|
|
{ \
|
|
CComVariant v(val); \
|
|
\
|
|
MPC_SCRIPTHELPER_PUT__DIRECT(obj,prop,v); \
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef _ATL_DLL_IMPL
|
|
namespace ATL
|
|
{
|
|
#endif
|
|
|
|
#ifndef _ATL_DLL_IMPL
|
|
}; //namespace ATL
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace MPC
|
|
{
|
|
class MPCMODULE;
|
|
extern MPCMODULE _MPC_Module;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace COMUtil
|
|
{
|
|
HRESULT GetPropertyByName( /*[in]*/ IDispatch* obj, /*[in]*/ LPCWSTR szName , /*[out]*/ CComVariant& v );
|
|
HRESULT GetPropertyByName( /*[in]*/ IDispatch* obj, /*[in]*/ LPCWSTR szName , /*[out]*/ CComBSTR& bstr );
|
|
HRESULT GetPropertyByName( /*[in]*/ IDispatch* obj, /*[in]*/ LPCWSTR szName , /*[out]*/ bool& fValue );
|
|
HRESULT GetPropertyByName( /*[in]*/ IDispatch* obj, /*[in]*/ LPCWSTR szName , /*[out]*/ long& lValue );
|
|
|
|
HRESULT VarToBSTR ( /*[in]*/ CComVariant& v, /*[out]*/ CComBSTR& str );
|
|
HRESULT VarToInterface( /*[in]*/ CComVariant& v, /*[in]*/ const IID& iid, /*[out]*/ IUnknown* *obj );
|
|
|
|
template <class Base, class Itf> HRESULT CopyInterface( Base* src, Itf* *dst )
|
|
{
|
|
if(!dst) return E_POINTER;
|
|
|
|
if(src)
|
|
{
|
|
return src->QueryInterface( __uuidof(*dst), (void**)dst );
|
|
}
|
|
|
|
*dst = NULL;
|
|
|
|
return S_FALSE;
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT SafeInitializeCriticalSection( /*[in/out]*/ CRITICAL_SECTION& sec );
|
|
HRESULT SafeDeleteCriticalSection ( /*[in/out]*/ CRITICAL_SECTION& sec );
|
|
|
|
class CComSafeAutoCriticalSection
|
|
{
|
|
public:
|
|
CComSafeAutoCriticalSection ();
|
|
~CComSafeAutoCriticalSection();
|
|
|
|
void Lock ();
|
|
void Unlock();
|
|
|
|
CRITICAL_SECTION m_sec;
|
|
};
|
|
|
|
class CComSafeMultiThreadModel
|
|
{
|
|
public:
|
|
static ULONG WINAPI Increment(LPLONG p) { return InterlockedIncrement( p ); }
|
|
static ULONG WINAPI Decrement(LPLONG p) { return InterlockedDecrement( p ); }
|
|
|
|
typedef MPC::CComSafeAutoCriticalSection AutoCriticalSection;
|
|
typedef CComCriticalSection CriticalSection;
|
|
typedef CComMultiThreadModelNoCS ThreadModelNoCS;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Same as ATL::CComObjectCached, but with CreateInstance and no critical section.
|
|
//
|
|
// Base is the user's class that derives from CComObjectRoot and whatever
|
|
// interfaces the user wants to support on the object
|
|
// CComObjectCached is used primarily for class factories in DLL's
|
|
// but it is useful anytime you want to cache an object
|
|
template <class Base> class CComObjectCached : public Base
|
|
{
|
|
public:
|
|
typedef Base _BaseClass;
|
|
|
|
CComObjectCached(void* = NULL)
|
|
{
|
|
}
|
|
|
|
// Set refcount to 1 to protect destruction
|
|
~CComObjectCached()
|
|
{
|
|
m_dwRef = 1L;
|
|
FinalRelease();
|
|
}
|
|
|
|
//If InternalAddRef or InternalRelease is undefined then your class
|
|
//doesn't derive from CComObjectRoot
|
|
STDMETHOD_(ULONG, AddRef)()
|
|
{
|
|
ULONG l = InternalAddRef();
|
|
|
|
if(l == 2) _Module.Lock();
|
|
|
|
return l;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)()
|
|
{
|
|
ULONG l = InternalRelease();
|
|
|
|
if (l == 0) delete this;
|
|
else if(l == 1) _Module.Unlock();
|
|
|
|
return l;
|
|
}
|
|
|
|
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
|
|
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { return _InternalQueryInterface(iid, ppvObject); }
|
|
template <class Q> HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); }
|
|
|
|
static HRESULT WINAPI CreateInstance( CComObjectCached<Base>** pp )
|
|
{
|
|
ATLASSERT(pp != NULL);
|
|
HRESULT hRes = E_OUTOFMEMORY;
|
|
CComObjectCached<Base>* p = NULL;
|
|
ATLTRY(p = new CComObjectCached<Base>())
|
|
if(p != NULL)
|
|
{
|
|
p->SetVoid(NULL);
|
|
p->InternalFinalConstructAddRef();
|
|
hRes = p->FinalConstruct();
|
|
p->InternalFinalConstructRelease();
|
|
if(hRes != S_OK)
|
|
{
|
|
delete p;
|
|
p = NULL;
|
|
}
|
|
}
|
|
*pp = p;
|
|
return hRes;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Same as ATL::CComObjectNoLock, but with CreateInstance.
|
|
//
|
|
template <class Base> class CComObjectNoLock : public Base
|
|
{
|
|
public:
|
|
typedef Base _BaseClass;
|
|
|
|
CComObjectNoLock(void* = NULL)
|
|
{
|
|
}
|
|
|
|
// Set refcount to 1 to protect destruction
|
|
~CComObjectNoLock()
|
|
{
|
|
m_dwRef = 1L;
|
|
FinalRelease();
|
|
}
|
|
|
|
//If InternalAddRef or InternalRelease is undefined then your class
|
|
//doesn't derive from CComObjectRoot
|
|
STDMETHOD_(ULONG, AddRef)()
|
|
{
|
|
return InternalAddRef();
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)()
|
|
{
|
|
ULONG l = InternalRelease();
|
|
|
|
if(l == 0) delete this;
|
|
|
|
return l;
|
|
}
|
|
|
|
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
|
|
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject) { return _InternalQueryInterface(iid, ppvObject); }
|
|
template <class Q> HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) { return QueryInterface(__uuidof(Q), (void**)pp); }
|
|
|
|
static HRESULT WINAPI CreateInstance( CComObjectNoLock<Base>** pp )
|
|
{
|
|
ATLASSERT(pp != NULL);
|
|
HRESULT hRes = E_OUTOFMEMORY;
|
|
CComObjectNoLock<Base>* p = NULL;
|
|
ATLTRY(p = new CComObjectNoLock<Base>())
|
|
if(p != NULL)
|
|
{
|
|
p->SetVoid(NULL);
|
|
p->InternalFinalConstructAddRef();
|
|
hRes = p->FinalConstruct();
|
|
p->InternalFinalConstructRelease();
|
|
if(hRes != S_OK)
|
|
{
|
|
delete p;
|
|
p = NULL;
|
|
}
|
|
}
|
|
*pp = p;
|
|
return hRes;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Same as ATL::CComObjectGlobal, but with no module locking.
|
|
//
|
|
template <class Base> class CComObjectGlobalNoLock : public Base
|
|
{
|
|
public:
|
|
typedef Base _BaseClass;
|
|
|
|
CComObjectGlobalNoLock(void* = NULL)
|
|
{
|
|
m_hResFinalConstruct = FinalConstruct();
|
|
}
|
|
|
|
~CComObjectGlobalNoLock()
|
|
{
|
|
FinalRelease();
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)() { return 2; }
|
|
STDMETHOD_(ULONG, Release)() { return 1; }
|
|
|
|
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
|
|
{
|
|
return _InternalQueryInterface(iid, ppvObject);
|
|
}
|
|
|
|
HRESULT m_hResFinalConstruct;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
interface CComObjectRootParentBase : IUnknown
|
|
{
|
|
virtual ULONG STDMETHODCALLTYPE WeakAddRef (void) = 0;
|
|
virtual ULONG STDMETHODCALLTYPE WeakRelease(void) = 0;
|
|
|
|
void Passivate()
|
|
{
|
|
}
|
|
|
|
template <class C, class P> HRESULT CreateChild( P* pParent, C* *pVal )
|
|
{
|
|
C* obj;
|
|
HRESULT hr;
|
|
|
|
*pVal = NULL;
|
|
|
|
if(SUCCEEDED(hr = obj->CreateInstance( &obj )))
|
|
{
|
|
obj->AddRef();
|
|
obj->Child_LinkToParent( pParent );
|
|
|
|
*pVal = obj;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
template <class ThreadModel, class T> class CComObjectRootChildEx : public CComObjectRootEx<ThreadModel>
|
|
{
|
|
private:
|
|
T* m_Child_pParent;
|
|
|
|
void Child_UnlinkParent()
|
|
{
|
|
Lock();
|
|
|
|
if(m_Child_pParent)
|
|
{
|
|
m_Child_pParent->WeakRelease();
|
|
m_Child_pParent = NULL;
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
public:
|
|
CComObjectRootChildEx() : m_Child_pParent(NULL) {}
|
|
|
|
virtual ~CComObjectRootChildEx()
|
|
{
|
|
Child_UnlinkParent();
|
|
}
|
|
|
|
void Child_LinkToParent( T* pParent )
|
|
{
|
|
Lock();
|
|
|
|
Child_UnlinkParent();
|
|
|
|
if((m_Child_pParent = pParent))
|
|
{
|
|
m_Child_pParent->WeakAddRef();
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
|
|
void Child_GetParent( T* *ppParent )
|
|
{
|
|
Lock();
|
|
|
|
if((*ppParent = m_Child_pParent))
|
|
{
|
|
m_Child_pParent->AddRef();
|
|
}
|
|
|
|
Unlock();
|
|
}
|
|
};
|
|
|
|
template <class Base> class CComObjectParent : public Base
|
|
{
|
|
public:
|
|
typedef Base _BaseClass;
|
|
|
|
CComObjectParent(void* = NULL)
|
|
{
|
|
m_dwRefWeak = 0L;
|
|
|
|
::_Module.Lock();
|
|
}
|
|
|
|
// Set refcount to 1 to protect destruction
|
|
~CComObjectParent()
|
|
{
|
|
m_dwRef = 1L;
|
|
FinalRelease();
|
|
|
|
::_Module.Unlock();
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)()
|
|
{
|
|
ULONG l;
|
|
|
|
Lock();
|
|
|
|
l = ++m_dwRef;
|
|
|
|
Unlock();
|
|
|
|
return l;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)()
|
|
{
|
|
ULONG l;
|
|
bool readytodelete = false;
|
|
|
|
Lock();
|
|
|
|
l = --m_dwRef;
|
|
|
|
if(l == 0)
|
|
{
|
|
m_dwRef += 2; // Protect ourself during the "Passivate" process.
|
|
Unlock();
|
|
Passivate();
|
|
Lock();
|
|
m_dwRef -= 2;
|
|
|
|
if(m_dwRefWeak == 0)
|
|
{
|
|
readytodelete = true;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if(readytodelete)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
//if _InternalQueryInterface is undefined then you forgot BEGIN_COM_MAP
|
|
STDMETHOD(QueryInterface)(REFIID iid, void ** ppvObject)
|
|
{
|
|
return _InternalQueryInterface(iid, ppvObject);
|
|
}
|
|
|
|
|
|
STDMETHOD_(ULONG, WeakAddRef)()
|
|
{
|
|
ULONG l;
|
|
|
|
Lock();
|
|
|
|
l = ++m_dwRefWeak;
|
|
|
|
Unlock();
|
|
|
|
return l;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, WeakRelease)()
|
|
{
|
|
ULONG l;
|
|
bool readytodelete = false;
|
|
|
|
Lock();
|
|
|
|
l = --m_dwRefWeak;
|
|
|
|
if(m_dwRef == 0)
|
|
{
|
|
if(m_dwRefWeak == 0)
|
|
{
|
|
readytodelete = true;
|
|
}
|
|
}
|
|
|
|
Unlock();
|
|
|
|
if(readytodelete)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
static HRESULT WINAPI CreateInstance( CComObjectParent<Base>** pp );
|
|
|
|
ULONG m_dwRefWeak;
|
|
};
|
|
|
|
|
|
template <class Base> HRESULT WINAPI CComObjectParent<Base>::CreateInstance( CComObjectParent<Base>** pp )
|
|
{
|
|
ATLASSERT(pp != NULL);
|
|
|
|
HRESULT hRes = E_OUTOFMEMORY;
|
|
CComObjectParent<Base>* p = NULL;
|
|
|
|
ATLTRY(p = new CComObjectParent<Base>())
|
|
|
|
if(p != NULL)
|
|
{
|
|
p->SetVoid(NULL);
|
|
p->InternalFinalConstructAddRef();
|
|
|
|
hRes = p->FinalConstruct();
|
|
|
|
p->InternalFinalConstructRelease();
|
|
if(hRes != S_OK)
|
|
{
|
|
delete p;
|
|
p = NULL;
|
|
}
|
|
}
|
|
*pp = p;
|
|
return hRes;
|
|
}
|
|
|
|
template <class T, const CLSID* pclsid = &CLSID_NULL> class CComParentCoClass : public CComCoClass<T, pclsid>
|
|
{
|
|
public:
|
|
typedef CComCreator2< CComCreator< MPC::CComObjectParent< T > >, CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Smart Lock class, so that locks on ATL objects can be easily acquired and released.
|
|
//
|
|
template <class ThreadModel> class SmartLock
|
|
{
|
|
CComObjectRootEx<ThreadModel>* m_p;
|
|
|
|
SmartLock( const SmartLock& );
|
|
|
|
public:
|
|
SmartLock(CComObjectRootEx<ThreadModel>* p) : m_p(p)
|
|
{
|
|
if(p) p->Lock();
|
|
}
|
|
|
|
~SmartLock()
|
|
{
|
|
if(m_p) m_p->Unlock();
|
|
}
|
|
|
|
SmartLock& operator=( /*[in]*/ CComObjectRootEx<ThreadModel>* p )
|
|
{
|
|
if(m_p) m_p->Unlock();
|
|
if(p ) p ->Lock ();
|
|
|
|
m_p = p;
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Smart Lock class, works with every class exposing 'Lock' and 'Unlock'.
|
|
//
|
|
template <class T> class SmartLockGeneric
|
|
{
|
|
T* m_p;
|
|
|
|
SmartLockGeneric( const SmartLockGeneric& );
|
|
|
|
public:
|
|
SmartLockGeneric( T* p ) : m_p(p)
|
|
{
|
|
if(p) p->Lock();
|
|
}
|
|
|
|
~SmartLockGeneric()
|
|
{
|
|
if(m_p) m_p->Unlock();
|
|
}
|
|
|
|
SmartLockGeneric& operator=( /*[in]*/ T* p )
|
|
{
|
|
if(m_p) m_p->Unlock();
|
|
if(p ) p ->Lock ();
|
|
|
|
m_p = p;
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Class used to act as an hub for all the instances of CComPtrThreadNeutral<T>.
|
|
// It holds the Global Interface Table.
|
|
//
|
|
class CComPtrThreadNeutral_GIT
|
|
{
|
|
IGlobalInterfaceTable* m_pGIT;
|
|
CRITICAL_SECTION m_sec;
|
|
|
|
void Lock ();
|
|
void Unlock();
|
|
HRESULT GetGIT( IGlobalInterfaceTable* *ppGIT );
|
|
|
|
public:
|
|
|
|
CComPtrThreadNeutral_GIT();
|
|
~CComPtrThreadNeutral_GIT();
|
|
|
|
HRESULT Init();
|
|
HRESULT Term();
|
|
|
|
HRESULT RegisterInterface( /*[in]*/ IUnknown* pUnk, /*[in]*/ REFIID riid, /*[out]*/ DWORD *pdwCookie );
|
|
HRESULT RevokeInterface ( /*[in]*/ DWORD dwCookie );
|
|
HRESULT GetInterface ( /*[in]*/ DWORD dwCookie, /*[in]*/ REFIID riid, /*[out]*/ void* *ppv );
|
|
};
|
|
|
|
//
|
|
// This smart pointer template stores THREAD-INDEPEDENT pointers to COM objects.
|
|
//
|
|
// The best way to use it is to store an object reference into it and then assign
|
|
// the object itself to a CComPtr<T>.
|
|
//
|
|
// This way the proper proxy is looked up and the smart pointer will keep it alive.
|
|
//
|
|
template <class T> class CComPtrThreadNeutral
|
|
{
|
|
private:
|
|
DWORD m_dwCookie;
|
|
|
|
void Inner_Register( T* lp )
|
|
{
|
|
if(lp && _MPC_Module.m_GITHolder)
|
|
{
|
|
_MPC_Module.m_GITHolder->RegisterInterface( lp, __uuidof(T), &m_dwCookie );
|
|
}
|
|
}
|
|
|
|
public:
|
|
typedef T _PtrClass;
|
|
|
|
CComPtrThreadNeutral()
|
|
{
|
|
m_dwCookie = 0xFEFEFEFE;
|
|
}
|
|
|
|
CComPtrThreadNeutral( /*[in]*/ const CComPtrThreadNeutral<T>& t )
|
|
{
|
|
m_dwCookie = 0xFEFEFEFE;
|
|
|
|
*this = t;
|
|
}
|
|
|
|
CComPtrThreadNeutral( T* lp )
|
|
{
|
|
m_dwCookie = 0xFEFEFEFE;
|
|
|
|
Inner_Register( lp );
|
|
}
|
|
|
|
~CComPtrThreadNeutral()
|
|
{
|
|
Release();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
operator CComPtr<T>() const
|
|
{
|
|
CComPtr<T> res;
|
|
|
|
(void)Access( &res );
|
|
|
|
return res;
|
|
}
|
|
|
|
CComPtr<T> operator=( T* lp )
|
|
{
|
|
Release();
|
|
|
|
Inner_Register( lp );
|
|
|
|
return (CComPtr<T>)(*this);
|
|
}
|
|
|
|
CComPtrThreadNeutral& operator=( /*[in]*/ const CComPtrThreadNeutral<T>& t )
|
|
{
|
|
CComPtr<T> obj;
|
|
|
|
Release();
|
|
|
|
if(SUCCEEDED(t.Access( &obj )))
|
|
{
|
|
Inner_Register( obj );
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool operator!() const
|
|
{
|
|
return (m_dwCookie == 0xFEFEFEFE);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
void Release()
|
|
{
|
|
if(m_dwCookie != 0xFEFEFEFE)
|
|
{
|
|
_MPC_Module.m_GITHolder->RevokeInterface( m_dwCookie );
|
|
|
|
m_dwCookie = 0xFEFEFEFE;
|
|
}
|
|
}
|
|
|
|
void Attach( T* p )
|
|
{
|
|
*this = p;
|
|
|
|
if(p) p->Release();
|
|
}
|
|
|
|
T* Detach()
|
|
{
|
|
T* pt;
|
|
|
|
(void)Access( &pt );
|
|
|
|
Release();
|
|
|
|
return pt;
|
|
}
|
|
|
|
HRESULT Access( T* *ppt ) const
|
|
{
|
|
HRESULT hr;
|
|
|
|
if(ppt == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
}
|
|
else
|
|
{
|
|
*ppt = NULL;
|
|
|
|
if(m_dwCookie != 0xFEFEFEFE)
|
|
{
|
|
hr = _MPC_Module.m_GITHolder->GetInterface( m_dwCookie, __uuidof(T), (void**)ppt );
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <class T, const IID* piid, const GUID* plibid>
|
|
class ATL_NO_VTABLE IDispatchExImpl :
|
|
public IDispatchImpl<T, piid, plibid>,
|
|
public IDispatchEx
|
|
{
|
|
public:
|
|
typedef IDispatchExImpl<T, piid, plibid> self;
|
|
typedef IDispatchImpl<T, piid, plibid> super;
|
|
|
|
//
|
|
// IDispatch
|
|
//
|
|
STDMETHOD(GetTypeInfoCount)( UINT* pctinfo )
|
|
{
|
|
return super::GetTypeInfoCount( pctinfo );
|
|
}
|
|
|
|
STDMETHOD(GetTypeInfo)( UINT itinfo ,
|
|
LCID lcid ,
|
|
ITypeInfo* *pptinfo )
|
|
{
|
|
return super::GetTypeInfo( itinfo, lcid, pptinfo );
|
|
}
|
|
|
|
STDMETHOD(GetIDsOfNames)( REFIID riid ,
|
|
LPOLESTR* rgszNames ,
|
|
UINT cNames ,
|
|
LCID lcid ,
|
|
DISPID* rgdispid )
|
|
{
|
|
return super::GetIDsOfNames( riid, rgszNames, cNames, lcid, rgdispid );
|
|
}
|
|
|
|
STDMETHOD(Invoke)( DISPID dispidMember ,
|
|
REFIID riid ,
|
|
LCID lcid ,
|
|
WORD wFlags ,
|
|
DISPPARAMS* pdispparams ,
|
|
VARIANT* pvarResult ,
|
|
EXCEPINFO* pexcepinfo ,
|
|
UINT* puArgErr )
|
|
{
|
|
return super::Invoke( dispidMember, riid , lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr );
|
|
}
|
|
|
|
//
|
|
// IDispatchEx
|
|
//
|
|
STDMETHOD(GetDispID)( /*[in] */ BSTR bstrName ,
|
|
/*[in] */ DWORD grfdex ,
|
|
/*[out]*/ DISPID *pid )
|
|
{
|
|
if(grfdex & fdexNameEnsure) return E_NOTIMPL;
|
|
|
|
return GetIDsOfNames( IID_NULL, &bstrName, 1, 0, pid );
|
|
}
|
|
|
|
STDMETHOD(InvokeEx)( /*[in] */ DISPID id ,
|
|
/*[in] */ LCID lcid ,
|
|
/*[in] */ WORD wFlags ,
|
|
/*[in] */ DISPPARAMS* pdp ,
|
|
/*[out]*/ VARIANT* pvarRes ,
|
|
/*[out]*/ EXCEPINFO* pei ,
|
|
/*[in] */ IServiceProvider* pspCaller )
|
|
{
|
|
return Invoke( id, IID_NULL, lcid, wFlags, pdp, pvarRes, pei, NULL );
|
|
}
|
|
|
|
STDMETHOD(DeleteMemberByName)( /*[in]*/ BSTR bstrName ,
|
|
/*[in]*/ DWORD grfdex )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(DeleteMemberByDispID)( /*[in]*/ DISPID id )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(GetMemberProperties)( /*[in] */ DISPID id ,
|
|
/*[in] */ DWORD grfdexFetch ,
|
|
/*[out]*/ DWORD *pgrfdex )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(GetMemberName)( /*[in] */ DISPID id ,
|
|
/*[out]*/ BSTR *pbstrName )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(GetNextDispID)( /*[in] */ DWORD grfdex ,
|
|
/*[in] */ DISPID id ,
|
|
/*[out]*/ DISPID *pid )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(GetNameSpaceParent)( /*[out]*/ IUnknown* *ppunk )
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
};
|
|
|
|
class CComConstantHolder
|
|
{
|
|
typedef std::map<DISPID,CComVariant> MemberLookup;
|
|
typedef MemberLookup::iterator MemberLookupIter;
|
|
typedef MemberLookup::const_iterator MemberLookupIterConst;
|
|
|
|
////////////////////
|
|
|
|
const GUID* m_plibid;
|
|
WORD m_wMajor;
|
|
WORD m_wMinor;
|
|
|
|
CComPtr<ITypeLib> m_pTypeLib;
|
|
MemberLookup m_const;
|
|
|
|
HRESULT EnsureLoaded( /*[in]*/ LCID lcid );
|
|
|
|
public:
|
|
CComConstantHolder( /*[in]*/ const GUID* plibid ,
|
|
/*[in]*/ WORD wMajor = 1 ,
|
|
/*[in]*/ WORD wMinor = 0 );
|
|
|
|
HRESULT GetIDsOfNames( /*[in]*/ LPOLESTR* rgszNames ,
|
|
/*[in]*/ UINT cNames ,
|
|
/*[in]*/ LCID lcid ,
|
|
/*[out]*/ DISPID* rgdispid );
|
|
|
|
HRESULT GetValue( /*[in]*/ DISPID dispidMember ,
|
|
/*[in]*/ LCID lcid ,
|
|
/*[out]*/ VARIANT* pvarResult );
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Template used to manage work threads.
|
|
// Class 'T' should implement a method like this: 'HRESULT <method>()'.
|
|
//
|
|
template <class T, class Itf, DWORD dwThreading = COINIT_MULTITHREADED> class Thread
|
|
{
|
|
public:
|
|
typedef HRESULT (T::*THREAD_RUN)();
|
|
|
|
private:
|
|
CRITICAL_SECTION m_sec;
|
|
|
|
T* m_SelfDirect;
|
|
THREAD_RUN m_Callback;
|
|
CComPtrThreadNeutral<Itf> m_Self; // The keep the object alive will the thread is running.
|
|
|
|
HANDLE m_hThread; // The thread itself.
|
|
bool m_fRunning; // If true the thread is still running.
|
|
|
|
HANDLE m_hEvent; // Used to notify the worker thread.
|
|
HANDLE m_hEventReverse; // Used to notify the main thread.
|
|
bool m_fAbort; // Used to tell the thread to abort and exit.
|
|
|
|
// Passed to _beginthreadex.
|
|
static unsigned __stdcall Thread_Startup( /*[in]*/ void* pv )
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Startup" );
|
|
|
|
HRESULT hRes = ::CoInitializeEx( NULL, dwThreading );
|
|
Thread<T,Itf,dwThreading>* pThis = (Thread<T,Itf,dwThreading>*)pv;
|
|
|
|
if(SUCCEEDED(hRes))
|
|
{
|
|
try
|
|
{
|
|
pThis->Thread_InnerRun();
|
|
}
|
|
catch(...)
|
|
{
|
|
pThis->Thread_Lock();
|
|
|
|
pThis->m_fAbort = true;
|
|
|
|
pThis->Thread_Unlock();
|
|
}
|
|
|
|
::CoUninitialize();
|
|
}
|
|
|
|
pThis->Thread_Lock();
|
|
|
|
pThis->m_fRunning = false;
|
|
|
|
pThis->Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(0);
|
|
}
|
|
|
|
//
|
|
// The core loop that would keep calling 'Thread_Run' until:
|
|
//
|
|
// a) m_fAbort is set, and
|
|
//
|
|
// b) hr reports a success.
|
|
//
|
|
HRESULT Thread_InnerRun()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_InnerRun" );
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
Thread_Lock();
|
|
|
|
if(m_SelfDirect && m_Callback)
|
|
{
|
|
while(m_fAbort == false && SUCCEEDED(hr))
|
|
{
|
|
Thread_Unlock(); // Always unlock before waiting for signal.
|
|
(void)MPC::WaitForSingleObject( m_hEvent, INFINITE );
|
|
Thread_Lock(); // Lock again.
|
|
|
|
if(m_fAbort == false)
|
|
{
|
|
Thread_Unlock(); // Unlock while handling the request.
|
|
hr = (m_SelfDirect->*m_Callback)();
|
|
Thread_Lock(); // Lock again.
|
|
}
|
|
}
|
|
}
|
|
|
|
Thread_Release();
|
|
|
|
Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(hr);
|
|
}
|
|
|
|
void Thread_Lock () { ::EnterCriticalSection( &m_sec ); }
|
|
void Thread_Unlock() { ::LeaveCriticalSection( &m_sec ); }
|
|
|
|
protected:
|
|
DWORD Thread_WaitForEvents( HANDLE hEvent, DWORD dwTimeout )
|
|
{
|
|
HANDLE hEvents[2] = { m_hEvent, hEvent };
|
|
DWORD dwWait;
|
|
|
|
return ::WaitForMultipleObjects( hEvent ? 2 : 1, hEvents, FALSE, dwTimeout );
|
|
}
|
|
|
|
HANDLE Thread_GetSignalEvent()
|
|
{
|
|
return m_hEvent;
|
|
}
|
|
|
|
public:
|
|
Thread()
|
|
{
|
|
::InitializeCriticalSection( &m_sec );
|
|
|
|
// CRITICAL_SECTION m_sec;
|
|
//
|
|
m_SelfDirect = NULL; // T* m_SelfDirect;
|
|
// CComPtrThreadNeutral<Itf> m_Self
|
|
//
|
|
m_hThread = NULL; // HANDLE m_hThread;
|
|
m_fRunning = false; // bool m_fRunning;
|
|
//
|
|
m_hEvent = NULL; // HANDLE m_hEvent;
|
|
m_hEventReverse = NULL; // HANDLE m_hEventReverse;
|
|
m_fAbort = false; // bool m_fAbort;
|
|
}
|
|
|
|
~Thread()
|
|
{
|
|
Thread_Wait();
|
|
|
|
::DeleteCriticalSection( &m_sec );
|
|
}
|
|
|
|
|
|
HRESULT Thread_Start( /*[in]*/ T* selfDirect, /*[in]*/ THREAD_RUN callback, /*[in]*/ Itf* self )
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Start" );
|
|
|
|
HRESULT hr;
|
|
DWORD dwThreadID;
|
|
|
|
|
|
//
|
|
// First of all, kill the currently running thread, if any.
|
|
//
|
|
Thread_Abort();
|
|
Thread_Wait ();
|
|
|
|
|
|
Thread_Lock();
|
|
|
|
m_SelfDirect = selfDirect;
|
|
m_Callback = callback;
|
|
m_Self = self;
|
|
m_fAbort = false;
|
|
m_fRunning = false;
|
|
|
|
//
|
|
// Create the event used to signal the worker thread about changes in the queue or termination requests.
|
|
//
|
|
// The Event is created in the SET state, so the worker thread doesn't wait in the WaitForSingleObject the first time.
|
|
//
|
|
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_hEvent = ::CreateEvent( NULL, FALSE, TRUE, NULL )));
|
|
|
|
//
|
|
// Create the event used to signal the main thread.
|
|
//
|
|
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_hEventReverse = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
|
|
|
|
//
|
|
// Create the worker thread.
|
|
//
|
|
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (m_hThread = (HANDLE)_beginthreadex( NULL, 0, Thread_Startup, this, 0, (unsigned*)&dwThreadID )));
|
|
|
|
m_fRunning = true;
|
|
hr = S_OK;
|
|
|
|
|
|
__MPC_FUNC_CLEANUP;
|
|
|
|
Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(hr);
|
|
}
|
|
|
|
bool Thread_SameThread()
|
|
{
|
|
Thread_Lock();
|
|
|
|
bool fRes = (::GetCurrentThread() == m_hThread);
|
|
|
|
Thread_Unlock();
|
|
|
|
return fRes;
|
|
}
|
|
|
|
void Thread_Wait( /*[in]*/ bool fForce = true, /*[in]*/ bool fNoMsg = false )
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Wait" );
|
|
|
|
Thread_Lock();
|
|
|
|
if(m_hThread && m_hEvent)
|
|
{
|
|
if(::GetCurrentThread() != m_hThread)
|
|
{
|
|
while(m_fRunning == true)
|
|
{
|
|
if(fForce) Thread_Abort();
|
|
|
|
Thread_Unlock(); // Always unlock before waiting for signal.
|
|
if(fNoMsg)
|
|
{
|
|
(void)::WaitForSingleObject( m_hThread, INFINITE );
|
|
}
|
|
else
|
|
{
|
|
(void)MPC::WaitForSingleObject( m_hThread, INFINITE );
|
|
}
|
|
Thread_Lock(); // Lock again.
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_hEventReverse)
|
|
{
|
|
::CloseHandle( m_hEventReverse );
|
|
m_hEventReverse = NULL;
|
|
}
|
|
|
|
if(m_hEvent)
|
|
{
|
|
::CloseHandle( m_hEvent );
|
|
m_hEvent = NULL;
|
|
}
|
|
|
|
if(m_hThread)
|
|
{
|
|
::CloseHandle( m_hThread );
|
|
m_hThread = NULL;
|
|
}
|
|
|
|
|
|
Thread_Release();
|
|
|
|
Thread_Unlock();
|
|
}
|
|
|
|
DWORD Thread_WaitNotificationFromWorker( /*[in]*/ DWORD dwTimeout, /*[in]*/ bool fNoMessagePump )
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_WaitNotificationFromWorker" );
|
|
|
|
DWORD dwRes = WAIT_TIMEOUT;
|
|
|
|
Thread_Lock();
|
|
|
|
if(Thread_IsRunning() && m_hEventReverse && ::GetCurrentThread() != m_hThread)
|
|
{
|
|
Thread_Unlock(); // Always unlock before waiting for signal.
|
|
|
|
if(fNoMessagePump)
|
|
{
|
|
dwRes = ::WaitForSingleObject( m_hEventReverse, dwTimeout );
|
|
}
|
|
else
|
|
{
|
|
dwRes = MPC::WaitForSingleObject( m_hEventReverse, dwTimeout );
|
|
}
|
|
|
|
Thread_Lock(); // Lock again.
|
|
}
|
|
|
|
Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(dwRes);
|
|
}
|
|
|
|
void Thread_Release()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Release" );
|
|
|
|
Thread_Lock();
|
|
|
|
m_SelfDirect = NULL;
|
|
m_Callback = NULL;
|
|
m_Self = NULL;
|
|
|
|
Thread_Unlock();
|
|
}
|
|
|
|
void Thread_Signal()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Signal" );
|
|
|
|
Thread_Lock();
|
|
|
|
if(m_hEvent)
|
|
{
|
|
::SetEvent( m_hEvent );
|
|
}
|
|
|
|
Thread_Unlock();
|
|
}
|
|
|
|
void Thread_SignalMain()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_SignalMain" );
|
|
|
|
Thread_Lock();
|
|
|
|
if(m_hEventReverse)
|
|
{
|
|
::SetEvent( m_hEventReverse );
|
|
}
|
|
|
|
Thread_Unlock();
|
|
}
|
|
|
|
void Thread_Abort()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Abort" );
|
|
|
|
Thread_Lock();
|
|
|
|
if(m_hEvent)
|
|
{
|
|
m_fAbort = true;
|
|
::SetEvent( m_hEvent );
|
|
}
|
|
|
|
Thread_Unlock();
|
|
}
|
|
|
|
CComPtr<Itf> Thread_Self()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_Self" );
|
|
|
|
//
|
|
// Do NOT use locking, because:
|
|
//
|
|
// 1) It's not needed (m_Self is used internally to the thread, it's not a shared resource).
|
|
// 2) GIT can cause a thread switch and a deadlock...
|
|
//
|
|
CComPtr<Itf> res = m_Self;
|
|
|
|
__MPC_FUNC_EXIT(res);
|
|
}
|
|
|
|
bool Thread_IsAborted()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_IsAborted" );
|
|
|
|
bool res;
|
|
|
|
Thread_Lock();
|
|
|
|
res = m_fAbort;
|
|
|
|
Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(res);
|
|
}
|
|
|
|
bool Thread_IsRunning()
|
|
{
|
|
__MPC_FUNC_ENTRY( COMMONID, "MPC::Thread::Thread_IsRunning" );
|
|
|
|
bool res;
|
|
|
|
Thread_Lock();
|
|
|
|
res = m_fRunning;
|
|
|
|
Thread_Unlock();
|
|
|
|
__MPC_FUNC_EXIT(res);
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Async Invoke class, used to call IDispatch interfaces in an asynchronous way.
|
|
//
|
|
class AsyncInvoke : // hungarian: mpcai
|
|
public CComObjectRootEx<MPC::CComSafeMultiThreadModel>, // For the locking support...
|
|
public Thread<AsyncInvoke,IUnknown>
|
|
{
|
|
public:
|
|
//
|
|
// These two classes, CallItem and CallDesc, are useful also outside this class,
|
|
// because "CallDesc" allows to have an object that can call any IDispatch-related methods,
|
|
// irregardless to the apartment model, while "CallItem" does the same for VARIANTs.
|
|
//
|
|
class CallItem // hungarian: ci
|
|
{
|
|
VARTYPE m_vt;
|
|
CComPtrThreadNeutral<IUnknown> m_Unknown;
|
|
CComPtrThreadNeutral<IDispatch> m_Dispatch;
|
|
CComVariant m_Other;
|
|
|
|
public:
|
|
CallItem();
|
|
|
|
CallItem& operator=( const CComVariant& var );
|
|
|
|
operator CComVariant() const;
|
|
};
|
|
|
|
class CallDesc // hungarian: cd
|
|
{
|
|
CComPtrThreadNeutral<IDispatch> m_dispTarget;
|
|
DISPID m_dispidMethod;
|
|
CallItem* m_rgciVars;
|
|
DWORD m_dwVars;
|
|
|
|
public:
|
|
CallDesc( IDispatch* dispTarget, DISPID dispidMethod, const CComVariant* rgvVars, int dwVars );
|
|
~CallDesc();
|
|
|
|
HRESULT Call();
|
|
};
|
|
|
|
private:
|
|
typedef std::list< CallDesc* > List;
|
|
typedef List::iterator Iter;
|
|
typedef List::const_iterator IterConst;
|
|
|
|
List m_lstEvents;
|
|
|
|
HRESULT Thread_Run();
|
|
|
|
public:
|
|
HRESULT Init();
|
|
HRESULT Term();
|
|
|
|
HRESULT Invoke( IDispatch* dispTarget, DISPID dispidMethod, const CComVariant* rgvVars, int dwVars );
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// This template facilitate the implementation of Collections and Enumerators.
|
|
//
|
|
// It implements a Collections of objects, all implementing the <class Itf> interface, through Type Library <const GUID* plibid>.
|
|
//
|
|
// All the client has to do is call "AddItem", to add elements to the collection:
|
|
//
|
|
// class ATL_NO_VTABLE CNewClass : // Hungarian: hsc
|
|
// public MPC::CComCollection< INewClass, &LIBID_NewLib, CComMultiThreadModel>
|
|
// {
|
|
// public:
|
|
// BEGIN_COM_MAP(CNewClass)
|
|
// COM_INTERFACE_ENTRY(IDispatch)
|
|
// COM_INTERFACE_ENTRY(INewClass)
|
|
// END_COM_MAP()
|
|
// };
|
|
//
|
|
template <class Itf, const GUID* plibid, class ThreadModel>
|
|
class CComCollection :
|
|
public CComObjectRootEx<ThreadModel>,
|
|
public ICollectionOnSTLImpl<
|
|
IDispatchImpl< Itf, &__uuidof(Itf), plibid >,
|
|
std::list < VARIANT >,
|
|
VARIANT ,
|
|
_Copy < VARIANT >,
|
|
CComEnumOnSTL< IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT>, std::list< VARIANT >, ThreadModel >
|
|
>
|
|
{
|
|
public:
|
|
typedef std::list< VARIANT > CollectionList;
|
|
typedef CollectionList::iterator CollectionIter;
|
|
typedef CollectionList::const_iterator CollectionIterConst;
|
|
|
|
void FinalRelease()
|
|
{
|
|
Erase();
|
|
}
|
|
|
|
void Erase()
|
|
{
|
|
MPC::SmartLock<_ThreadModel> lock( this );
|
|
|
|
MPC::ReleaseAllVariant( m_coll );
|
|
}
|
|
|
|
HRESULT AddItem( /*[in]*/ IDispatch* pDisp )
|
|
{
|
|
MPC::SmartLock<_ThreadModel> lock( this );
|
|
|
|
if(pDisp)
|
|
{
|
|
VARIANT vItem;
|
|
|
|
::VariantInit( &vItem );
|
|
|
|
vItem.vt = VT_DISPATCH;
|
|
vItem.pdispVal = pDisp; pDisp->AddRef();
|
|
|
|
m_coll.push_back( vItem );
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
template <class Base, const IID* piid, class ThreadModel> class ConnectionPointImpl :
|
|
public CComObjectRootEx<ThreadModel>,
|
|
public IConnectionPointContainerImpl< Base >,
|
|
public IConnectionPointImpl < Base, piid, CComDynamicUnkArray >
|
|
{
|
|
public:
|
|
BEGIN_CONNECTION_POINT_MAP(Base)
|
|
CONNECTION_POINT_ENTRY((*piid))
|
|
END_CONNECTION_POINT_MAP()
|
|
|
|
protected:
|
|
//
|
|
// Event firing methods.
|
|
//
|
|
HRESULT FireAsync_Generic( DISPID dispid, CComVariant* pVars, DWORD dwVars, CComPtr<IDispatch> pJScript )
|
|
{
|
|
HRESULT hr;
|
|
MPC::IDispatchList lst;
|
|
|
|
//
|
|
// Only this part should be inside a critical section, otherwise deadlocks could occur.
|
|
//
|
|
{
|
|
MPC::SmartLock<_ThreadModel> lock( this );
|
|
|
|
MPC::CopyConnections( m_vec, lst ); // Get a copy of the connection point clients.
|
|
}
|
|
|
|
hr = MPC::FireAsyncEvent( dispid, pVars, dwVars, lst, pJScript );
|
|
|
|
MPC::ReleaseAll( lst );
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT FireSync_Generic( DISPID dispid, CComVariant* pVars, DWORD dwVars, CComPtr<IDispatch> pJScript )
|
|
{
|
|
HRESULT hr;
|
|
MPC::IDispatchList lst;
|
|
|
|
//
|
|
// Only this part should be inside a critical section, otherwise deadlocks could occur.
|
|
//
|
|
{
|
|
MPC::SmartLock<_ThreadModel> lock( this );
|
|
|
|
MPC::CopyConnections( m_vec, lst ); // Get a copy of the connection point clients.
|
|
}
|
|
|
|
hr = MPC::FireEvent( dispid, pVars, dwVars, lst, pJScript );
|
|
|
|
MPC::ReleaseAll( lst );
|
|
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
HRESULT FireAsyncEvent( DISPID dispid, CComVariant* pVars, DWORD dwVars, const IDispatchList& lst, IDispatch* pJScript = NULL, bool fFailOnError = false );
|
|
HRESULT FireEvent ( DISPID dispid, CComVariant* pVars, DWORD dwVars, const IDispatchList& lst, IDispatch* pJScript = NULL, bool fFailOnError = false );
|
|
|
|
template <class Coll> HRESULT CopyConnections( Coll& coll ,
|
|
IDispatchList& lst )
|
|
{
|
|
int nConnectionIndex;
|
|
int nConnections = coll.GetSize();
|
|
|
|
//
|
|
// nConnectionIndex == -1 is a special case, for calling a JavaScript function!
|
|
//
|
|
for(nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
|
|
{
|
|
CComQIPtr<IDispatch> sp( coll.GetAt(nConnectionIndex) );
|
|
|
|
if(sp)
|
|
{
|
|
lst.push_back( sp.Detach() );
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
class MPCMODULE
|
|
{
|
|
class AnchorBase
|
|
{
|
|
public:
|
|
virtual void Call ( ) = 0;
|
|
virtual bool Match( void* pObj ) = 0;
|
|
};
|
|
|
|
template <class C> class Anchor : public AnchorBase
|
|
{
|
|
typedef void (C::*CLASS_METHOD)();
|
|
|
|
C* m_pThis;
|
|
CLASS_METHOD m_pCallback;
|
|
|
|
public:
|
|
Anchor( /*[in]*/ C* pThis, /*[in]*/ CLASS_METHOD pCallback )
|
|
{
|
|
m_pThis = pThis;
|
|
m_pCallback = pCallback;
|
|
}
|
|
|
|
void Call()
|
|
{
|
|
(m_pThis->*m_pCallback)();
|
|
}
|
|
|
|
bool Match( /*[in]*/ void* pObj )
|
|
{
|
|
return m_pThis == (C*)pObj;
|
|
}
|
|
};
|
|
|
|
|
|
typedef std::list< AnchorBase* > List;
|
|
typedef List::iterator Iter;
|
|
typedef List::const_iterator IterConst;
|
|
|
|
|
|
static LONG m_lInitialized;
|
|
static LONG m_lInitializing;
|
|
static CComCriticalSection m_sec;
|
|
static List* m_lstTermCallback;
|
|
|
|
////////////////////////////////////////
|
|
|
|
static HRESULT Initialize();
|
|
|
|
HRESULT RegisterCallbackInner ( /*[in]*/ AnchorBase* pElem, /*[in]*/ void* pThis );
|
|
HRESULT UnregisterCallbackInner( /*[in]*/ void* pThis );
|
|
|
|
public:
|
|
CComPtrThreadNeutral_GIT* m_GITHolder;
|
|
AsyncInvoke* m_AsyncInvoke;
|
|
|
|
HRESULT Init();
|
|
HRESULT Term();
|
|
|
|
|
|
template <class C> HRESULT RegisterCallback( C* pThis, void (C::*pCallback)() )
|
|
{
|
|
if(pThis == NULL) return E_POINTER;
|
|
|
|
return RegisterCallbackInner( new Anchor<C>( pThis, pCallback ), pThis );
|
|
}
|
|
|
|
template <class C> HRESULT UnregisterCallback( C* pThis )
|
|
{
|
|
return UnregisterCallbackInner( pThis );
|
|
}
|
|
};
|
|
|
|
extern MPCMODULE _MPC_Module;
|
|
|
|
|
|
//
|
|
// Function to call a method in an asynchronous mode (however, no return values will be given).
|
|
//
|
|
HRESULT AsyncInvoke( IDispatch* dispTarget, DISPID dispidMethod, const CComVariant* rgvVars, int dwVars );
|
|
|
|
//
|
|
// This is like Sleep(), but it spins the message pump.
|
|
//
|
|
void SleepWithMessagePump( /*[in]*/ DWORD dwTimeout );
|
|
|
|
//
|
|
// Functions to wait on events even in an STA context.
|
|
//
|
|
DWORD WaitForSingleObject ( /*[in]*/ HANDLE hEvent , /*[in]*/ DWORD dwTimeout = INFINITE );
|
|
DWORD WaitForMultipleObjects( /*[in]*/ DWORD dwEvents, /*[in]*/ HANDLE* rgEvents, /*[in]*/ DWORD dwTimeout = INFINITE );
|
|
|
|
|
|
}; // namespace
|
|
|
|
|
|
#endif // !defined(__INCLUDED___MPC___COM_H___)
|