WindowsXP-SP1/admin/pchealth/core/mpc_common/utils_com.cpp
2020-09-30 16:53:49 +02:00

1068 lines
28 KiB
C++

/******************************************************************************
Copyright (c) 1999 Microsoft Corporation
Module Name:
Utils_COM.cpp
Abstract:
This file contains the implementation of commodity classes related to COM.
Revision History:
Davide Massarenti (Dmassare) 06/18/99
created
Davide Massarenti (Dmassare) 07/21/99
move under "core"
******************************************************************************/
#include "stdafx.h"
#include <process.h>
/////////////////////////////////////////////////////////////////////////////
HRESULT MPC::COMUtil::GetPropertyByName( /*[in]*/ IDispatch* obj ,
/*[in]*/ LPCWSTR szName ,
/*[out]*/ CComVariant& v )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::GetPropertyByName" );
HRESULT hr;
CComDispatchDriver disp( obj );
__MPC_PARAMCHECK_BEGIN(hr)
__MPC_PARAMCHECK_NOTNULL(obj);
__MPC_PARAMCHECK_END();
v.Clear();
hr = disp.GetPropertyByName( CComBSTR( szName ), &v );
if(FAILED(hr) && hr != DISP_E_UNKNOWNNAME) __MPC_FUNC_LEAVE;
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::COMUtil::GetPropertyByName( /*[in]*/ IDispatch* obj ,
/*[in]*/ LPCWSTR szName ,
/*[out]*/ CComBSTR& bstr )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::GetPropertyByName" );
HRESULT hr;
CComVariant v;
__MPC_EXIT_IF_METHOD_FAILS(hr, GetPropertyByName( obj, szName, v ));
__MPC_EXIT_IF_METHOD_FAILS(hr, VarToBSTR ( v, bstr ));
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::COMUtil::GetPropertyByName( /*[in]*/ IDispatch* obj ,
/*[in]*/ LPCWSTR szName ,
/*[out]*/ bool& fValue )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::GetPropertyByName" );
HRESULT hr;
CComVariant v;
__MPC_EXIT_IF_METHOD_FAILS(hr, GetPropertyByName( obj, szName, v ));
if(v.vt != VT_NULL &&
v.vt != VT_EMPTY )
{
__MPC_EXIT_IF_METHOD_FAILS(hr, v.ChangeType( VT_BOOL ));
fValue = v.boolVal == VARIANT_TRUE;
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::COMUtil::GetPropertyByName( /*[in]*/ IDispatch* obj ,
/*[in]*/ LPCWSTR szName ,
/*[out]*/ long& lValue )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::GetPropertyByName" );
HRESULT hr;
CComVariant v;
__MPC_EXIT_IF_METHOD_FAILS(hr, GetPropertyByName( obj, szName, v ));
if(v.vt != VT_NULL &&
v.vt != VT_EMPTY )
{
__MPC_EXIT_IF_METHOD_FAILS(hr, v.ChangeType( VT_I4 ));
lValue = v.lVal;
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
////////////////////
HRESULT MPC::COMUtil::VarToBSTR( /*[in] */ CComVariant& v ,
/*[out]*/ CComBSTR& str )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::VarToBSTR" );
HRESULT hr;
str.Empty();
if(v.vt != VT_NULL &&
v.vt != VT_EMPTY )
{
__MPC_EXIT_IF_METHOD_FAILS(hr, v.ChangeType( VT_BSTR ));
str = v.bstrVal;
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::COMUtil::VarToInterface( /*[in]*/ CComVariant& v ,
/*[in]*/ const IID& iid ,
/*[out]*/ IUnknown* *obj )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::COMUtil::VarToInterface" );
HRESULT hr;
__MPC_PARAMCHECK_BEGIN(hr)
__MPC_PARAMCHECK_POINTER_AND_SET(obj,NULL);
__MPC_PARAMCHECK_END();
if(v.vt != VT_NULL &&
v.vt != VT_EMPTY )
{
__MPC_EXIT_IF_METHOD_FAILS(hr, v.ChangeType( VT_UNKNOWN ));
if(v.punkVal)
{
__MPC_EXIT_IF_METHOD_FAILS(hr, v.punkVal->QueryInterface( iid, (LPVOID*)obj ));
}
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
/////////////////////////////////////////////////////////////////////////////
MPC::CComConstantHolder::CComConstantHolder( /*[in]*/ const GUID* plibid ,
/*[in]*/ WORD wMajor ,
/*[in]*/ WORD wMinor )
{
m_plibid = plibid;
m_wMajor = wMajor;
m_wMinor = wMinor;
}
HRESULT MPC::CComConstantHolder::EnsureLoaded( /*[in]*/ LCID lcid )
{
HRESULT hr;
if(!m_pTypeLib)
{
if(SUCCEEDED(hr = ::LoadRegTypeLib( *m_plibid, m_wMajor, m_wMinor, lcid, &m_pTypeLib )))
{
UINT cTypes = m_pTypeLib->GetTypeInfoCount();
for(UINT uType=0; uType<cTypes; uType++)
{
CComPtr<ITypeInfo> pTypeInfo;
if(SUCCEEDED(m_pTypeLib->GetTypeInfo( uType, &pTypeInfo )))
{
TYPEATTR* pTypeAttr = NULL;
if(SUCCEEDED(pTypeInfo->GetTypeAttr( &pTypeAttr )) && pTypeAttr)
{
if(pTypeAttr->typekind == TKIND_ENUM)
{
for(WORD i=0; i<pTypeAttr->cVars; i++)
{
VARDESC* pVarDesc = NULL;
if(SUCCEEDED(pTypeInfo->GetVarDesc( i, &pVarDesc )) && pVarDesc)
{
if(pVarDesc->varkind == VAR_CONST && pVarDesc->lpvarValue)
{
m_const[pVarDesc->memid] = *pVarDesc->lpvarValue;
}
pTypeInfo->ReleaseVarDesc( pVarDesc );
}
}
}
pTypeInfo->ReleaseTypeAttr( pTypeAttr );
}
}
}
}
}
else
{
hr = S_OK;
}
return hr;
}
HRESULT MPC::CComConstantHolder::GetIDsOfNames( LPOLESTR* rgszNames ,
UINT cNames ,
LCID lcid ,
DISPID* rgdispid )
{
HRESULT hr;
if(SUCCEEDED(hr = EnsureLoaded( lcid )))
{
UINT uMissing = cNames;
for(UINT uName=0; uName<cNames; uName++)
{
CComPtr<ITypeInfo> pTypeInfo;
MEMBERID memID;
USHORT uFound = 1;
if(SUCCEEDED(m_pTypeLib->FindName( rgszNames[uName], 0, &pTypeInfo, &memID, &uFound )) && uFound == 1)
{
rgdispid[uName] = memID; uMissing--;
}
}
if(uMissing != 0) hr = DISP_E_MEMBERNOTFOUND;
}
return hr;
}
HRESULT MPC::CComConstantHolder::GetValue( /*[in]*/ DISPID dispidMember ,
/*[in]*/ LCID lcid ,
/*[out]*/ VARIANT* pvarResult )
{
HRESULT hr;
if(SUCCEEDED(hr = EnsureLoaded( lcid )))
{
MemberLookupIter it = m_const.find( dispidMember );
if(it == m_const.end())
{
hr = DISP_E_MEMBERNOTFOUND;
}
else
{
hr = ::VariantCopy( pvarResult, &(it->second) );
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT MPC::SafeInitializeCriticalSection( /*[in/out]*/ CRITICAL_SECTION& sec )
{
try
{
::ZeroMemory( &sec, sizeof( sec ) );
::InitializeCriticalSection( &sec );
}
catch(...)
{
return E_FAIL;
}
return S_OK;
}
HRESULT MPC::SafeDeleteCriticalSection( /*[in/out]*/ CRITICAL_SECTION& sec )
{
try
{
::DeleteCriticalSection( &sec );
::ZeroMemory( &sec, sizeof( sec ) );
}
catch(...)
{
return E_FAIL;
}
return S_OK;
}
MPC::CComSafeAutoCriticalSection::CComSafeAutoCriticalSection () { SafeInitializeCriticalSection( m_sec ); }
MPC::CComSafeAutoCriticalSection::~CComSafeAutoCriticalSection() { SafeDeleteCriticalSection ( m_sec ); }
void MPC::CComSafeAutoCriticalSection::Lock () { try{ ::EnterCriticalSection( &m_sec ); } catch(...) {} }
void MPC::CComSafeAutoCriticalSection::Unlock() { try{ ::LeaveCriticalSection( &m_sec ); } catch(...) {} }
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// //
// MPC::CComPtrThreadNeutral_GIT //
// //
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
void MPC::CComPtrThreadNeutral_GIT::Lock()
{
::EnterCriticalSection( &m_sec );
}
void MPC::CComPtrThreadNeutral_GIT::Unlock()
{
::LeaveCriticalSection( &m_sec );
}
HRESULT MPC::CComPtrThreadNeutral_GIT::GetGIT( IGlobalInterfaceTable* *ppGIT )
{
_ASSERTE(ppGIT != NULL);
HRESULT hr = E_FAIL;
Lock();
if((*ppGIT = m_pGIT))
{
m_pGIT->AddRef();
hr = S_OK;
}
Unlock();
return hr;
}
MPC::CComPtrThreadNeutral_GIT::CComPtrThreadNeutral_GIT()
{
m_pGIT = NULL;
MPC::SafeInitializeCriticalSection( m_sec );
}
MPC::CComPtrThreadNeutral_GIT::~CComPtrThreadNeutral_GIT()
{
Term();
MPC::SafeDeleteCriticalSection( m_sec );
}
HRESULT MPC::CComPtrThreadNeutral_GIT::Init()
{
HRESULT hr = S_OK;
Lock();
if(m_pGIT == NULL)
{
hr = ::CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&m_pGIT );
}
Unlock();
return hr;
}
HRESULT MPC::CComPtrThreadNeutral_GIT::Term()
{
HRESULT hr = S_OK;
Lock();
if(m_pGIT)
{
m_pGIT->Release();
m_pGIT = NULL;
}
Unlock();
return hr;
}
HRESULT MPC::CComPtrThreadNeutral_GIT::RegisterInterface( /*[in] */ IUnknown* pUnk ,
/*[in] */ REFIID riid ,
/*[out]*/ DWORD *pdwCookie )
{
CComPtr<IGlobalInterfaceTable> pGIT;
HRESULT hr;
if(SUCCEEDED(hr = GetGIT( &pGIT )))
{
hr = pGIT->RegisterInterfaceInGlobal( pUnk, riid, pdwCookie );
}
return hr;
}
HRESULT MPC::CComPtrThreadNeutral_GIT::RevokeInterface( /*[in]*/ DWORD dwCookie )
{
CComPtr<IGlobalInterfaceTable> pGIT;
HRESULT hr;
if(SUCCEEDED(hr = GetGIT( &pGIT )))
{
hr = pGIT->RevokeInterfaceFromGlobal( dwCookie );
}
return hr;
}
HRESULT MPC::CComPtrThreadNeutral_GIT::GetInterface( /*[in] */ DWORD dwCookie ,
/*[in] */ REFIID riid ,
/*[out]*/ void* *ppv )
{
CComPtr<IGlobalInterfaceTable> pGIT;
HRESULT hr;
if(SUCCEEDED(hr = GetGIT( &pGIT )))
{
hr = pGIT->GetInterfaceFromGlobal( dwCookie, riid, ppv );
}
return hr;
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// //
// AsyncInvoke, AsyncInvoke::CallDesc & AsyncInvoke::CallItem //
// //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
MPC::AsyncInvoke::CallItem::CallItem()
{
m_vt = VT_EMPTY; // VARTYPE m_vt;
// CComPtrThreadNeutral<IUnknown> m_Unknown;
// CComPtrThreadNeutral<IDispatch> m_Dispatch;
// CComVariant m_Other;
}
MPC::AsyncInvoke::CallItem& MPC::AsyncInvoke::CallItem::operator=( const CComVariant& var )
{
switch(m_vt = var.vt)
{
case VT_UNKNOWN : m_Unknown = var.punkVal ; break;
case VT_DISPATCH: m_Dispatch = var.pdispVal; break;
default : m_Other = var ; break;
}
return *this;
}
MPC::AsyncInvoke::CallItem::operator CComVariant() const
{
CComVariant res;
switch(m_vt)
{
case VT_UNKNOWN : res = (CComPtr<IUnknown> )m_Unknown ; break;
case VT_DISPATCH: res = (CComPtr<IDispatch>)m_Dispatch; break;
default : res = m_Other ; break;
}
return res;
}
MPC::AsyncInvoke::CallDesc::CallDesc( IDispatch* dispTarget ,
DISPID dispidMethod ,
const CComVariant* rgvVars ,
int dwVars )
{
m_dispTarget = dispTarget;
m_dispidMethod = dispidMethod;
m_rgciVars = new CallItem[dwVars];
m_dwVars = dwVars;
if(m_rgciVars)
{
for(int i=0; i<dwVars; i++)
{
m_rgciVars[i] = rgvVars[i];
}
}
}
MPC::AsyncInvoke::CallDesc::~CallDesc()
{
if(m_rgciVars) delete [] m_rgciVars;
}
HRESULT MPC::AsyncInvoke::CallDesc::Call()
{
HRESULT hr;
CComPtr<IDispatch> dispTarget = m_dispTarget;
if(dispTarget == NULL)
{
hr = E_POINTER;
}
else
{
CComVariant* pvars = new CComVariant[m_dwVars];
DISPPARAMS disp = { pvars, NULL, m_dwVars, 0 };
CComVariant vResult;
if(pvars)
{
for(int i=0; i<m_dwVars; i++)
{
pvars[i] = m_rgciVars[i];
}
hr = dispTarget->Invoke( m_dispidMethod, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &vResult, NULL, NULL );
delete [] pvars;
}
else
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
HRESULT MPC::AsyncInvoke::Init()
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::AsyncInvoke::Init" );
return Thread_Start( this, Thread_Run, NULL );
}
HRESULT MPC::AsyncInvoke::Term()
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::AsyncInvoke::Term" );
Thread_Wait();
return S_OK;
}
HRESULT MPC::AsyncInvoke::Thread_Run()
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::AsyncInvoke::Thread_Run" );
HRESULT hr;
IterConst it;
CallDesc* cd;
while(Thread_IsAborted() == false)
{
////////////////////////////////////////
//
// Start of Critical Section.
//
SmartLock<_ThreadModel> lock( this );
//
// If no event in the list, go back to WaitForSingleObject.
//
it = m_lstEvents.begin();
if(it == m_lstEvents.end()) break;
//
// Get the first event in the list.
//
cd = *it;
//
// Remove the event from the list.
//
m_lstEvents.erase( it );
lock = NULL; // Unlock.
//
// End of Critical Section.
//
////////////////////////////////////////
//
// Fire the event.
//
if(cd)
{
__MPC_PROTECT( (void)cd->Call() );
delete cd;
}
}
hr = S_OK;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::AsyncInvoke::Invoke( IDispatch* dispTarget ,
DISPID dispidMethod,
const CComVariant* rgvVars ,
int dwVars )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::AsyncInvoke::Invoke" );
HRESULT hr;
__MPC_TRY_BEGIN();
CallDesc* cd;
SmartLock<_ThreadModel> lock( NULL ); // Don't lock immediately, first create the CallDesc object (deadlocks...)
__MPC_EXIT_IF_ALLOC_FAILS(hr, cd, new CallDesc( dispTarget, dispidMethod, rgvVars, dwVars ));
lock = this;
m_lstEvents.push_back( cd );
hr = S_OK;
Thread_Signal();
__MPC_FUNC_CLEANUP;
__MPC_TRY_CATCHALL(hr);
__MPC_FUNC_EXIT(hr);
}
/////////////////////////////////////////////////////////////////////////////
HRESULT MPC::FireAsyncEvent( DISPID dispid ,
CComVariant* pVars ,
DWORD dwVars ,
const IDispatchList& lst ,
IDispatch* pJScript ,
bool fFailOnError )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::FireAsyncEvent" );
HRESULT hr;
IDispatchIterConst it;
if(pJScript)
{
HRESULT hr2;
hr2 = MPC::AsyncInvoke( pJScript, 0, pVars, dwVars );
if(FAILED(hr2) && fFailOnError)
{
__MPC_SET_ERROR_AND_EXIT(hr, hr2);
}
}
for(it=lst.begin(); it != lst.end(); it++)
{
if(*it != NULL)
{
HRESULT hr2;
CComVariant vResult;
hr2 = MPC::AsyncInvoke( *it, dispid, pVars, dwVars );
if(FAILED(hr2) && fFailOnError)
{
__MPC_SET_ERROR_AND_EXIT(hr, hr2);
}
}
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::FireEvent( DISPID dispid ,
CComVariant* pVars ,
DWORD dwVars ,
const IDispatchList& lst ,
IDispatch* pJScript ,
bool fFailOnError )
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::FireEvent" );
HRESULT hr;
IDispatchIterConst it;
DISPPARAMS disp = { pVars, NULL, dwVars, 0 };
if(pJScript)
{
HRESULT hr2;
CComVariant vResult;
hr2 = pJScript->Invoke( 0, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &vResult, NULL, NULL );
if(FAILED(hr2) && fFailOnError)
{
__MPC_SET_ERROR_AND_EXIT(hr, hr2);
}
}
for(it=lst.begin(); it != lst.end(); it++)
{
if(*it != NULL)
{
HRESULT hr2;
CComVariant vResult;
hr2 = (*it)->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &vResult, NULL, NULL );
if(FAILED(hr2) && fFailOnError)
{
__MPC_SET_ERROR_AND_EXIT(hr, hr2);
}
}
}
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::AsyncInvoke( /*[in]*/ IDispatch* dispTarget ,
/*[in]*/ DISPID dispidMethod ,
/*[in]*/ const CComVariant* rgvVars ,
/*[in]*/ int dwVars )
{
return MPC::_MPC_Module.m_AsyncInvoke ? MPC::_MPC_Module.m_AsyncInvoke->Invoke( dispTarget, dispidMethod, rgvVars, dwVars ) : E_FAIL;
}
void MPC::SleepWithMessagePump( /*[in]*/ DWORD dwTimeout )
{
HANDLE hEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL );
if(hEvent)
{
(void)MPC::WaitForSingleObject( hEvent, dwTimeout );
::CloseHandle( hEvent );
}
}
DWORD MPC::WaitForSingleObject( /*[in]*/ HANDLE hEvent ,
/*[in]*/ DWORD dwTimeout )
{
return MPC::WaitForMultipleObjects( 1, &hEvent, dwTimeout );
}
DWORD MPC::WaitForMultipleObjects( /*[in]*/ DWORD dwEvents ,
/*[in]*/ HANDLE* rgEvents ,
/*[in]*/ DWORD dwTimeout )
{
DWORD dwTickPre;
DWORD dwTickPost;
DWORD dwRet;
MSG msg;
while(1)
{
dwTickPre = ::GetTickCount();
while(1)
{
//
// Commenting out 'dwRet >= WAIT_OBJECT_0', thanks to our extremely smart compiler...
//
dwRet = ::WaitForMultipleObjects( dwEvents, rgEvents, FALSE, 0 );
if((/*dwRet >= WAIT_OBJECT_0 &&*/ dwRet < WAIT_OBJECT_0 + dwEvents) || // An event was signaled.
( dwRet >= WAIT_ABANDONED_0 && dwRet < WAIT_ABANDONED_0 + dwEvents) ) // An event was abandoned.
{
return dwRet;
}
if(!::PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE )) break;
//
// There is one or more window message available. Dispatch them.
//
::TranslateMessage( &msg );
::DispatchMessage ( &msg );
}
////////////////////
dwRet = ::MsgWaitForMultipleObjects( dwEvents, rgEvents, FALSE, dwTimeout, QS_ALLINPUT );
dwTickPost = ::GetTickCount();
//
// Commenting out 'dwRet >= WAIT_OBJECT_0', thanks to our extremely smart compiler...
//
if((/*dwRet >= WAIT_OBJECT_0 &&*/ dwRet < WAIT_OBJECT_0 + dwEvents) || // An event was signaled.
( dwRet >= WAIT_ABANDONED_0 && dwRet < WAIT_ABANDONED_0 + dwEvents) || // An event was abandoned.
( dwRet != WAIT_OBJECT_0 + dwEvents) ) // Something else happened
{
return dwRet;
}
////////////////////
//
// Take care of timeout.
//
if(dwTimeout != INFINITE)
{
dwTickPost -= dwTickPre;
if(dwTimeout < dwTickPost)
{
return WAIT_TIMEOUT;
}
dwTimeout -= dwTickPost;
}
}
return -1;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// //
// MPC::MPCMODULE //
// //
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
MPC::MPCMODULE MPC::_MPC_Module;
LONG MPC::MPCMODULE::m_lInitialized = 0;
LONG MPC::MPCMODULE::m_lInitializing = 0;
CComCriticalSection MPC::MPCMODULE::m_sec;
MPC::MPCMODULE::List* MPC::MPCMODULE::m_lstTermCallback;
////////////////////////////////////////
//
// The problem we are trying to resolve is calling RegisterCallbackInner from static constructors.
//
// Because there's no order guaranteed, any static class constructor can call our method BEFORE
// the class has been initialized by the C runtime...
//
// So we cannot rely on any member variable been initialized properly.
// This means no Critical Section up and running, no list ready to use...
//
HRESULT MPC::MPCMODULE::Initialize()
{
HRESULT hr = S_OK;
if(m_lInitialized == 0) // m_lInitialized only changes from 0 to 1, so it's safe to look at it without locking.
{
//
// Our primitive critical section...
//
while(::InterlockedExchange( &m_lInitializing, 1 ) != 0)
{
::Sleep( 0 ); // Yield processor...
}
while(m_lInitialized == 0)
{
m_sec.Init();
m_lstTermCallback = new List;
if(m_lstTermCallback == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
//
// Make sure that the TEMP environment variable is defined.
//
{
WCHAR rgBuf[MAX_PATH];
if(::GetEnvironmentVariableW( L"TEMP", rgBuf, MAXSTRLEN(rgBuf) ) == 0)
{
::GetTempPathW ( MAXSTRLEN(rgBuf), rgBuf );
::SetEnvironmentVariableW( L"TEMP" , rgBuf );
}
}
m_lInitialized = 1;
}
::InterlockedExchange( &m_lInitializing, 0 );
}
return hr;
}
////////////////////////////////////////
HRESULT MPC::MPCMODULE::RegisterCallbackInner( /*[in]*/ AnchorBase* pElem, /*[in]*/ void* pThis )
{
HRESULT hr;
if(pElem == NULL)
{
hr = E_OUTOFMEMORY;
}
else if(SUCCEEDED(hr = Initialize()))
{
UnregisterCallbackInner( pThis );
m_sec.Lock();
m_lstTermCallback->push_back( pElem );
m_sec.Unlock();
}
return hr;
}
HRESULT MPC::MPCMODULE::UnregisterCallbackInner( /*[in/out]*/ void* pThis )
{
HRESULT hr;
if(SUCCEEDED(hr = Initialize()))
{
Iter it;
m_sec.Lock();
for(it = m_lstTermCallback->begin(); it != m_lstTermCallback->end(); )
{
AnchorBase* pElem = *it;
if(pElem->Match( pThis ))
{
m_lstTermCallback->erase( it ); delete pElem;
it = m_lstTermCallback->begin();
}
else
{
it++;
}
}
m_sec.Unlock();
}
return hr;
}
////////////////////////////////////////
HRESULT MPC::MPCMODULE::Init()
{
__MPC_FUNC_ENTRY( COMMONID, "MPC::MPCMODULE::Init" );
HRESULT hr;
__MPC_EXIT_IF_METHOD_FAILS(hr, Initialize());
__MPC_EXIT_IF_ALLOC_FAILS (hr, m_GITHolder, new CComPtrThreadNeutral_GIT);
__MPC_EXIT_IF_METHOD_FAILS(hr, m_GITHolder->Init() );
__MPC_EXIT_IF_ALLOC_FAILS (hr, m_AsyncInvoke, new class MPC::AsyncInvoke);
__MPC_EXIT_IF_METHOD_FAILS(hr, m_AsyncInvoke->Init() );
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr);
}
HRESULT MPC::MPCMODULE::Term()
{
HRESULT hr;
//
// Call the registered termination methods.
//
if(SUCCEEDED(hr = Initialize()))
{
m_sec.Lock();
for(Iter it = m_lstTermCallback->begin(); it != m_lstTermCallback->end(); it++)
{
AnchorBase* pElem = *it;
pElem->Call();
}
m_sec.Unlock();
}
if(m_AsyncInvoke) { m_AsyncInvoke->Term(); delete m_AsyncInvoke; m_AsyncInvoke = NULL; }
if(m_GITHolder ) { m_GITHolder ->Term(); delete m_GITHolder ; m_GITHolder = NULL; }
return hr;
}