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.
3627 lines
98 KiB
3627 lines
98 KiB
/*===================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1996 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: Application Object Manager
|
|
|
|
File: Applmgr.cpp
|
|
|
|
Owner: PramodD
|
|
|
|
This is the Application Manager source file.
|
|
===================================================================*/
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "activdbg.h"
|
|
#include "mtacb.h"
|
|
#include "debugger.h"
|
|
#include "memchk.h"
|
|
#include <iismsg.h>
|
|
|
|
PTRACE_LOG CAppln::gm_pTraceLog = NULL;
|
|
CApplnMgr g_ApplnMgr;
|
|
CApplnCleanupMgr g_ApplnCleanupMgr;
|
|
DWORD g_nApplications = 0;
|
|
DWORD g_nApplicationObjectsActive = 0;
|
|
DWORD g_nApplicationsRestarting = 0;
|
|
DWORD g_nApplicationsRestarted = 0;
|
|
LONG g_fProceedWithShutdownAppln = 1;
|
|
|
|
#define DENALI_FILE_NOTIFY_FILTER 0
|
|
|
|
#pragma warning (disable: 4355) // ignore: "'this' used in base member init
|
|
|
|
/*===================================================================
|
|
S c r i p t E n g i n e C l e a n u p
|
|
|
|
Node type for linked list of script engines to cleanup
|
|
===================================================================*/
|
|
|
|
struct CScriptEngineCleanupElem : CDblLink
|
|
{
|
|
CActiveScriptEngine *m_pEngine;
|
|
CScriptEngineCleanupElem(CActiveScriptEngine *pEngine) : m_pEngine(pEngine)
|
|
{
|
|
m_pEngine->AddRef();
|
|
}
|
|
|
|
~CScriptEngineCleanupElem()
|
|
{
|
|
m_pEngine->FinalRelease();
|
|
}
|
|
};
|
|
|
|
/*===================================================================
|
|
C A p p l n V a r i a n t s
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CApplnVariants::CApplnVariants
|
|
|
|
Constructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CApplnVariants::CApplnVariants()
|
|
:
|
|
m_cRefs(1),
|
|
m_pAppln(NULL),
|
|
m_ctColType(ctUnknown),
|
|
m_ISupportErrImp(this, this, IID_IVariantDictionary)
|
|
{
|
|
CDispatch::Init(IID_IVariantDictionary);
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::~CApplnVariants
|
|
|
|
Constructor
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
===================================================================*/
|
|
CApplnVariants::~CApplnVariants()
|
|
{
|
|
Assert(!m_pAppln);
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::Init
|
|
|
|
Init ApplnVariants
|
|
|
|
Parameters:
|
|
CAppln *pAppln application
|
|
CompType ctColType component collection type
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::Init
|
|
(
|
|
CAppln *pAppln,
|
|
CompType ctColType
|
|
)
|
|
{
|
|
Assert(pAppln);
|
|
pAppln->AddRef();
|
|
|
|
Assert(!m_pAppln);
|
|
|
|
m_pAppln = pAppln;
|
|
m_ctColType = ctColType;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::UnInit
|
|
|
|
UnInit ApplnVariants
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::UnInit()
|
|
{
|
|
if (m_pAppln)
|
|
{
|
|
m_pAppln->Release();
|
|
m_pAppln = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::QueryInterface
|
|
CApplnVariants::AddRef
|
|
CApplnVariants::Release
|
|
|
|
IUnknown members for CApplnVariants object.
|
|
===================================================================*/
|
|
STDMETHODIMP CApplnVariants::QueryInterface
|
|
(
|
|
REFIID iid,
|
|
void **ppvObj
|
|
)
|
|
{
|
|
if (iid == IID_IUnknown || iid == IID_IDispatch ||
|
|
iid == IID_IVariantDictionary)
|
|
{
|
|
AddRef();
|
|
*ppvObj = this;
|
|
return S_OK;
|
|
}
|
|
else if (iid == IID_ISupportErrorInfo)
|
|
{
|
|
m_ISupportErrImp.AddRef();
|
|
*ppvObj = &m_ISupportErrImp;
|
|
return S_OK;
|
|
}
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CApplnVariants::AddRef()
|
|
{
|
|
return InterlockedIncrement((LPLONG)&m_cRefs);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CApplnVariants::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement((LPLONG)&m_cRefs);
|
|
|
|
if (cRef == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return cRef;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::ObjectNameFromVariant
|
|
|
|
Gets name from variant. Resolves operations by index.
|
|
Allocates memory for name.
|
|
|
|
Parameters:
|
|
vKey VARIANT
|
|
ppwszName [out] allocated name
|
|
fVerify flag - check existance if named
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::ObjectNameFromVariant
|
|
(
|
|
VARIANT &vKey,
|
|
WCHAR **ppwszName,
|
|
BOOL fVerify
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
*ppwszName = NULL;
|
|
|
|
if (!m_pAppln->PCompCol())
|
|
return E_FAIL;
|
|
|
|
VARIANT *pvarKey = &vKey;
|
|
VARIANT varKeyCopy;
|
|
VariantInit(&varKeyCopy);
|
|
if (V_VT(pvarKey) != VT_BSTR && V_VT(pvarKey) != VT_I2 && V_VT(pvarKey) != VT_I4)
|
|
{
|
|
if (FAILED(VariantResolveDispatch(&varKeyCopy, &vKey, IID_IVariantDictionary, IDE_APPLICATION)))
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
VariantClear(&varKeyCopy);
|
|
return E_FAIL;
|
|
}
|
|
pvarKey = &varKeyCopy;
|
|
}
|
|
|
|
LPWSTR pwszName = NULL;
|
|
|
|
switch (V_VT(pvarKey))
|
|
{
|
|
case VT_BSTR:
|
|
{
|
|
pwszName = V_BSTR(pvarKey);
|
|
|
|
if (fVerify && pwszName)
|
|
{
|
|
CComponentObject *pObj = NULL;
|
|
|
|
Assert(m_pAppln);
|
|
Assert(m_pAppln->PCompCol());
|
|
|
|
if (m_ctColType == ctTagged)
|
|
m_pAppln->PCompCol()->GetTagged(pwszName, &pObj);
|
|
else
|
|
m_pAppln->PCompCol()->GetProperty(pwszName, &pObj);
|
|
|
|
if (!pObj || pObj->GetType() != m_ctColType)
|
|
pwszName = NULL; // as if not found
|
|
}
|
|
break;
|
|
}
|
|
|
|
case VT_I1: case VT_I2: case VT_I8:
|
|
case VT_UI1: case VT_UI2: case VT_UI4: case VT_UI8:
|
|
case VT_R4: case VT_R8:
|
|
// Coerce all integral types to VT_I4
|
|
if (FAILED(VariantChangeType(pvarKey, pvarKey, 0, VT_I4)))
|
|
return E_FAIL;
|
|
|
|
// fallthru to VT_I4
|
|
|
|
case VT_I4:
|
|
{
|
|
int i;
|
|
// Look up the object by index
|
|
i = V_I4(pvarKey);
|
|
|
|
if (i > 0)
|
|
{
|
|
Assert(m_pAppln);
|
|
Assert(m_pAppln->PCompCol());
|
|
|
|
m_pAppln->PCompCol()->GetNameByIndex
|
|
(
|
|
m_ctColType,
|
|
i,
|
|
&pwszName
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pwszName)
|
|
{
|
|
*ppwszName = StringDupW(pwszName);
|
|
if (*ppwszName == NULL)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
VariantClear(&varKeyCopy);
|
|
return hr;
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::get_Item
|
|
|
|
Function called from DispInvoke to get keys from the collection.
|
|
|
|
Parameters:
|
|
vKey VARIANT [in], which parameter to get the value of - integers access collection as an array
|
|
pvarReturn VARIANT *, [out] value of the requested parameter
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::get_Item
|
|
(
|
|
VARIANT varKey,
|
|
VARIANT *pVar
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
// Initialize return value
|
|
VariantInit(pVar);
|
|
|
|
if (!m_pAppln->PCompCol())
|
|
return S_OK;
|
|
|
|
// Get HitObj from Viper if Tagged Variants
|
|
CHitObj *pHitObj = NULL;
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
ViperGetHitObjFromContext(&pHitObj);
|
|
if (!pHitObj)
|
|
return S_OK; // return emtpy variant
|
|
}
|
|
|
|
m_pAppln->Lock();
|
|
|
|
// Get name
|
|
WCHAR *pwszName = NULL;
|
|
HRESULT hr = ObjectNameFromVariant(varKey, &pwszName);
|
|
if (!pwszName)
|
|
{
|
|
m_pAppln->UnLock();
|
|
return hr;
|
|
}
|
|
|
|
// Find object by name
|
|
CComponentObject *pObj = NULL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
Assert(pHitObj);
|
|
// need to go through HitObj for instantiation
|
|
pHitObj->GetComponent(csAppln, pwszName, CbWStr(pwszName), &pObj);
|
|
if (pObj && (pObj->GetType() != ctTagged))
|
|
pObj = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_pAppln->PCompCol()->GetProperty(pwszName, &pObj);
|
|
}
|
|
|
|
if (pObj)
|
|
pObj->GetVariant(pVar);
|
|
|
|
m_pAppln->UnLock();
|
|
|
|
free(pwszName);
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::put_Item
|
|
|
|
OLE automation put for Item property
|
|
|
|
Parameters:
|
|
varKey key
|
|
Var value
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::put_Item
|
|
(
|
|
VARIANT varKey,
|
|
VARIANT Var
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION,
|
|
IDE_CANT_MOD_STATICOBJECTS);
|
|
return E_FAIL;
|
|
}
|
|
|
|
Assert(m_ctColType == ctProperty);
|
|
|
|
if (!m_pAppln->PCompCol())
|
|
return E_FAIL;
|
|
|
|
m_pAppln->Lock();
|
|
|
|
// Resolve the variant
|
|
VARIANT varResolved;
|
|
HRESULT hr = VariantResolveDispatch
|
|
(
|
|
&varResolved,
|
|
&Var,
|
|
IID_IApplicationObject,
|
|
IDE_APPLICATION
|
|
);
|
|
if (FAILED(hr))
|
|
{
|
|
m_pAppln->UnLock();
|
|
return hr; // exception already raised
|
|
}
|
|
|
|
// Get name
|
|
WCHAR *pwszName = NULL;
|
|
hr = ObjectNameFromVariant(varKey, &pwszName);
|
|
if (pwszName)
|
|
{
|
|
// Set the property
|
|
if (m_pAppln->PCompCol())
|
|
hr = m_pAppln->PCompCol()->AddProperty(pwszName, &varResolved);
|
|
else
|
|
hr = E_FAIL; // not likely if application not UnInited
|
|
}
|
|
|
|
VariantClear(&varResolved);
|
|
m_pAppln->UnLock();
|
|
|
|
if (hr == RPC_E_WRONG_THREAD)
|
|
{
|
|
// We use RPC_E_WRONG_THREAD to indicate bad model object
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_APPLICATION_CANT_STORE_OBJECT);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
free(pwszName);
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::putref_Item
|
|
|
|
OLE automation putref for Item property
|
|
|
|
Parameters:
|
|
varKey key
|
|
Var value
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::putref_Item
|
|
(
|
|
VARIANT varKey,
|
|
VARIANT Var
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION,
|
|
IDE_CANT_MOD_STATICOBJECTS);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (FIsIntrinsic(&Var))
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION, IDE_APPLICATION_CANT_STORE_INTRINSIC);
|
|
return E_FAIL;
|
|
}
|
|
|
|
Assert(m_ctColType == ctProperty);
|
|
|
|
if (!m_pAppln->PCompCol())
|
|
return E_FAIL;
|
|
|
|
m_pAppln->Lock();
|
|
|
|
// Get name
|
|
WCHAR *pwszName = NULL;
|
|
HRESULT hr = ObjectNameFromVariant(varKey, &pwszName);
|
|
if (pwszName)
|
|
{
|
|
// Set the property
|
|
if (m_pAppln->PCompCol())
|
|
hr = m_pAppln->PCompCol()->AddProperty(pwszName, &Var);
|
|
else
|
|
hr = E_FAIL; // not likely if application not UnInited
|
|
}
|
|
|
|
m_pAppln->UnLock();
|
|
|
|
if (hr == RPC_E_WRONG_THREAD)
|
|
{
|
|
// We use RPC_E_WRONG_THREAD to indicate bad model object
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_APPLICATION_CANT_STORE_OBJECT);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (pwszName)
|
|
free(pwszName);
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::get_Key
|
|
|
|
Function called from DispInvoke to get values from the collection.
|
|
|
|
Parameters:
|
|
vKey VARIANT [in], which parameter to get the value of - integers access collection as an array
|
|
pvarReturn VARIANT *, [out] value of the requested parameter
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::get_Key
|
|
(
|
|
VARIANT varKey,
|
|
VARIANT *pVar
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
VariantInit(pVar);
|
|
|
|
if (!m_pAppln->PCompCol())
|
|
return S_OK;
|
|
|
|
m_pAppln->Lock();
|
|
|
|
// Get name
|
|
WCHAR *pwszName = NULL;
|
|
HRESULT hr = ObjectNameFromVariant(varKey, &pwszName, TRUE);
|
|
|
|
m_pAppln->UnLock();
|
|
|
|
if (!pwszName)
|
|
return hr;
|
|
|
|
// Return BSTr
|
|
BSTR bstrT = SysAllocString(pwszName);
|
|
free(pwszName);
|
|
|
|
if (!bstrT)
|
|
return E_OUTOFMEMORY;
|
|
|
|
V_VT(pVar) = VT_BSTR;
|
|
V_BSTR(pVar) = bstrT;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::get_Count
|
|
|
|
Parameters:
|
|
pcValues - count is stored in *pcValues
|
|
===================================================================*/
|
|
STDMETHODIMP CApplnVariants::get_Count
|
|
(
|
|
int *pcValues
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
*pcValues = m_pAppln->m_pApplCompCol->GetTaggedObjectCount();
|
|
else
|
|
*pcValues = m_pAppln->m_pApplCompCol->GetPropertyCount();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::get__NewEnum
|
|
|
|
Return a new enumerator
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::get__NewEnum
|
|
(
|
|
IUnknown **ppEnumReturn
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
*ppEnumReturn = NULL;
|
|
|
|
CVariantsIterator *pIterator = new CVariantsIterator(m_pAppln, m_ctColType);
|
|
if (pIterator == NULL)
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_SESSION, IDE_OOM);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*ppEnumReturn = pIterator;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::Remove
|
|
|
|
Remove item
|
|
|
|
Parameters:
|
|
varKey key
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::Remove
|
|
(
|
|
VARIANT varKey
|
|
)
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION,
|
|
IDE_CANT_MOD_STATICOBJECTS);
|
|
return E_FAIL;
|
|
}
|
|
|
|
Assert(m_ctColType == ctProperty);
|
|
|
|
m_pAppln->Lock();
|
|
|
|
// Get name
|
|
WCHAR *pwszName = NULL;
|
|
ObjectNameFromVariant(varKey, &pwszName);
|
|
if (pwszName)
|
|
{
|
|
CComponentCollection *pCompCol = m_pAppln->PCompCol();
|
|
|
|
// Set the property
|
|
if (pCompCol)
|
|
pCompCol->RemoveProperty(pwszName);
|
|
|
|
free(pwszName);
|
|
}
|
|
|
|
m_pAppln->UnLock();
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnVariants::RemoveAll
|
|
|
|
Remove all items
|
|
|
|
Parameters:
|
|
varKey key
|
|
|
|
Returns:
|
|
S_OK on success, E_FAIL on failure.
|
|
===================================================================*/
|
|
HRESULT CApplnVariants::RemoveAll()
|
|
{
|
|
if (!m_pAppln || FAILED(m_pAppln->CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_ctColType == ctTagged)
|
|
{
|
|
ExceptionId(IID_IVariantDictionary, IDE_APPLICATION,
|
|
IDE_CANT_MOD_STATICOBJECTS);
|
|
return E_FAIL;
|
|
}
|
|
|
|
Assert(m_ctColType == ctProperty);
|
|
|
|
m_pAppln->Lock();
|
|
|
|
CComponentCollection *pCompCol = m_pAppln->PCompCol();
|
|
|
|
if (pCompCol)
|
|
{
|
|
pCompCol->RemoveAllProperties();
|
|
}
|
|
|
|
m_pAppln->UnLock();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
C A p p l n
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CAppln::CAppln
|
|
|
|
Constructor
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
|
|
CAppln::CAppln()
|
|
:
|
|
m_fInited(FALSE),
|
|
m_fFirstRequestRan(FALSE),
|
|
m_fGlobalChanged(FALSE),
|
|
m_fDeleteInProgress(FALSE),
|
|
m_fTombstone(FALSE),
|
|
m_fDebuggable(FALSE),
|
|
m_fInternalLockInited(FALSE),
|
|
m_fApplnLockInited(FALSE),
|
|
m_cRefs(1),
|
|
m_pszMetabaseKey(NULL),
|
|
m_pszApplnPath(NULL),
|
|
m_pszGlobalAsa(NULL),
|
|
m_pGlobalTemplate(NULL),
|
|
m_cSessions(0),
|
|
m_cRequests(0),
|
|
m_pSessionMgr(NULL),
|
|
m_pApplCompCol(NULL),
|
|
m_pProperties(NULL),
|
|
m_pTaggedObjects(NULL),
|
|
m_pAppRoot(NULL),
|
|
m_pActivity(NULL),
|
|
m_pUnkFTM(NULL),
|
|
m_dwLockThreadID(INVALID_THREADID),
|
|
m_cLockRefCount(0),
|
|
m_pdispGlobTypeLibWrapper(NULL),
|
|
m_pServicesConfig(NULL),
|
|
m_fSWC_PartitionAccessDenied(FALSE),
|
|
m_fSWC_InvalidPartitionGUID(FALSE)
|
|
{
|
|
|
|
// COM stuff
|
|
m_ISuppErrImp.Init(static_cast<IApplicationObject *>(this),
|
|
static_cast<IApplicationObject *>(this),
|
|
IID_IApplicationObject);
|
|
CDispatch::Init(IID_IApplicationObject);
|
|
|
|
InterlockedIncrement((LPLONG)&g_nApplicationObjectsActive);
|
|
|
|
IF_DEBUG(APPLICATION) {
|
|
WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
|
|
}
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::~CAppln
|
|
|
|
Destructor
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
CAppln::~CAppln()
|
|
{
|
|
Assert(m_fTombstone); // must be tombstoned before destructor
|
|
Assert(m_cRefs == 0); // must have 0 ref count
|
|
|
|
#ifdef DBG_NOTIFICATION
|
|
DBGPRINTF((DBG_CONTEXT, "Deleting application %p\n", this));
|
|
#endif // DBG_NOTIFICATION
|
|
|
|
if ( m_pUnkFTM != NULL )
|
|
{
|
|
m_pUnkFTM->Release();
|
|
m_pUnkFTM = NULL;
|
|
}
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CAppln::Init
|
|
|
|
Initialize object
|
|
|
|
Parameters:
|
|
char *pszApplnKey application's metabase key
|
|
char *pszApplnPath application's directory path
|
|
CIsapiReqInfo *pIReq Isapi Req Info
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory failure
|
|
===================================================================*/
|
|
HRESULT CAppln::Init
|
|
(
|
|
TCHAR *pszApplnKey,
|
|
TCHAR *pszApplnPath,
|
|
CIsapiReqInfo *pIReq
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMBCSToWChar convStr;
|
|
|
|
InterlockedIncrement((LPLONG)&g_nApplications);
|
|
|
|
Assert(pszApplnKey);
|
|
Assert(pszApplnPath);
|
|
|
|
void *pHashKey = NULL;
|
|
DWORD dwHashKeyLength = 0;
|
|
DWORD cch;
|
|
|
|
// Debugging variables (These are placed here for possible cleanup)
|
|
IDebugApplicationNode *pVirtualServerRoot = NULL;
|
|
CFileNode *pFileNode = NULL;
|
|
|
|
|
|
// Create the FTM
|
|
if (m_pUnkFTM == NULL)
|
|
{
|
|
hr = CoCreateFreeThreadedMarshaler( (IUnknown*)this, &m_pUnkFTM );
|
|
if ( FAILED(hr) )
|
|
{
|
|
Assert( m_pUnkFTM == NULL );
|
|
return (hr);
|
|
}
|
|
}
|
|
Assert( m_pUnkFTM != NULL );
|
|
|
|
|
|
// Critical sections created together --
|
|
// they are deleted in the destructor based on m_fInited flag
|
|
|
|
ErrInitCriticalSection(&m_csInternalLock, hr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ErrInitCriticalSection(&m_csApplnLock, hr);
|
|
if (FAILED(hr))
|
|
DeleteCriticalSection(&m_csInternalLock);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "New Application Failed to acquire Critical Section, hr = %08x\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
m_fInternalLockInited = TRUE;
|
|
m_fApplnLockInited = TRUE;
|
|
|
|
// Remember (copy of) metabase key
|
|
|
|
cch = _tcslen(pszApplnKey);
|
|
m_pszMetabaseKey = new TCHAR[(cch+1) * sizeof(TCHAR)];
|
|
if (!m_pszMetabaseKey)
|
|
goto LCleanupOOM;
|
|
memcpy(m_pszMetabaseKey, pszApplnKey, (cch+1)*sizeof(TCHAR));
|
|
|
|
pHashKey = m_pszMetabaseKey;
|
|
dwHashKeyLength = cch * sizeof(TCHAR);
|
|
|
|
// Remember (copy of) appln path
|
|
cch = _tcslen(pszApplnPath);
|
|
m_pszApplnPath = new TCHAR[(cch+1) * sizeof(TCHAR)];
|
|
if (!m_pszApplnPath)
|
|
goto LCleanupOOM;
|
|
memcpy(m_pszApplnPath, pszApplnPath, (cch+1)*sizeof(TCHAR));
|
|
|
|
// Get virtual path of appln & remember what it is
|
|
TCHAR szApplnVRoot[MAX_PATH];
|
|
if (FAILED(hr = FindApplicationPath(pIReq, szApplnVRoot, sizeof szApplnVRoot)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed to FindApplicationPath(), hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
if ((m_pszApplnVRoot = new TCHAR [(_tcslen(szApplnVRoot) + 1)*sizeof(TCHAR)]) == NULL)
|
|
goto LCleanupOOM;
|
|
_tcscpy(m_pszApplnVRoot, szApplnVRoot);
|
|
|
|
// Get Session cookie names, both secure and non-secure
|
|
InitSessionCookieNames();
|
|
|
|
// Initialize link element with key
|
|
Assert(pHashKey);
|
|
Assert(dwHashKeyLength);
|
|
|
|
if (FAILED(CLinkElem::Init(pHashKey, dwHashKeyLength)))
|
|
goto LCleanupOOM;
|
|
|
|
m_cSessions = 0;
|
|
m_cRequests = 0;
|
|
|
|
// Create and init app config
|
|
m_pAppConfig = new CAppConfig();
|
|
if (!m_pAppConfig)
|
|
goto LCleanupOOM;
|
|
|
|
hr = m_pAppConfig->Init(pIReq, this);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init the AppConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Create and init application level component collection
|
|
m_pApplCompCol = new CComponentCollection;
|
|
if (!m_pApplCompCol)
|
|
goto LCleanupOOM;
|
|
|
|
hr = m_pApplCompCol->Init(csAppln,m_pAppConfig->fExecuteInMTA());
|
|
if (FAILED(hr))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init the Component Collection, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// initialize application properties collection
|
|
m_pProperties = new CApplnVariants;
|
|
if (!m_pProperties)
|
|
goto LCleanupOOM;
|
|
hr = m_pProperties->Init(this, ctProperty);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init the Application Properties, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// initialize application tagged object collection
|
|
m_pTaggedObjects = new CApplnVariants;
|
|
if (!m_pTaggedObjects)
|
|
goto LCleanupOOM;
|
|
hr = m_pTaggedObjects->Init(this, ctTagged);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init the Application Tagged Objects, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Initialize the CServicesConfig Object
|
|
|
|
hr = InitServicesConfig();
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init ServicesConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Debugging support - Create an application node
|
|
// If PDM does not exist it means debugger not installed or it's Win 95
|
|
//
|
|
if (g_pPDM)
|
|
{
|
|
// Debugging directories are shown as:
|
|
//
|
|
// <virtual web server>
|
|
// <application name>
|
|
// <path to ASP>
|
|
//
|
|
// Get a pointer to the document node containing the virtual web server.
|
|
if (FAILED(hr = GetServerDebugRoot(pIReq, &pVirtualServerRoot)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not GetServerDebugRoot(), hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Create a node for this application
|
|
if (FAILED(hr = g_pDebugApp->CreateApplicationNode(&m_pAppRoot)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not CreateApplicationNode(), hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Create a doc provider for the node
|
|
if ((pFileNode = new CFileNode) == NULL)
|
|
goto LCleanupOOM;
|
|
|
|
// Name the application
|
|
TCHAR szDebugApp[256];
|
|
TCHAR *pchEnd = strcpyEx(szDebugApp, m_pszApplnVRoot);
|
|
if (! QueryAppConfig()->fAllowDebugging()) {
|
|
#if UNICODE
|
|
CwchLoadStringOfId(
|
|
#else
|
|
CchLoadStringOfId(
|
|
#endif
|
|
IDS_DEBUGGING_DISABLED, pchEnd, DIFF(&szDebugApp[sizeof (szDebugApp)/sizeof(TCHAR)] - pchEnd));
|
|
m_fDebuggable = FALSE;
|
|
}
|
|
else
|
|
m_fDebuggable = TRUE;
|
|
Assert (_tcslen(szDebugApp) < (sizeof(szDebugApp)/sizeof(TCHAR)));
|
|
|
|
WCHAR *pswzDebugApp;
|
|
#if UNICODE
|
|
pswzDebugApp = szDebugApp;
|
|
#else
|
|
if (FAILED(hr = convStr.Init(szDebugApp))) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Cannot convert szDebugApp to UNICODE, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
pswzDebugApp = convStr.GetString();
|
|
#endif
|
|
|
|
if (FAILED(hr = pFileNode->Init(pswzDebugApp)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Cannot Init CFileNode, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
if (FAILED(hr = m_pAppRoot->SetDocumentProvider(pFileNode)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: SetDocumentProvider failed, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// Attach to the UI
|
|
if (FAILED(hr = m_pAppRoot->Attach(pVirtualServerRoot)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Attach to debugger, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
// If this application had a previous incarnation (changed global.asa
|
|
// or debugging being flipped on in midstream), then there may be some
|
|
// documents in the cache which should be added to the debugger now.
|
|
if (m_fDebuggable)
|
|
{
|
|
g_TemplateCache.AddApplicationToDebuggerUI(this);
|
|
|
|
// create the global debug activity if it hasn't been created already
|
|
if (g_pDebugActivity == NULL) {
|
|
g_pDebugActivity = new CViperActivity;
|
|
if (g_pDebugActivity == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto LCleanup;
|
|
}
|
|
if (FAILED(hr = g_pDebugActivity->Init(m_pServicesConfig))) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not create global debug activity, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
|
|
// In DEBUG mode: all requests run on the same thread
|
|
if (FAILED(hr = BindToActivity(g_pDebugActivity)))
|
|
{
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not bind application to debugging activity, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create and init session manager
|
|
m_pSessionMgr = new CSessionMgr;
|
|
if (!m_pSessionMgr)
|
|
goto LCleanupOOM;
|
|
|
|
hr = m_pSessionMgr->Init(this);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not Init session manager, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
LCleanup:
|
|
// Release interfaces
|
|
if (pFileNode)
|
|
pFileNode->Release();
|
|
|
|
if (pVirtualServerRoot)
|
|
pVirtualServerRoot->Release();
|
|
|
|
if (SUCCEEDED(hr))
|
|
m_fInited = TRUE;
|
|
|
|
|
|
return hr;
|
|
|
|
LCleanupOOM:
|
|
hr = E_OUTOFMEMORY;
|
|
DBGERROR((DBG_CONTEXT, "New Application Failed: E_OUTOFMEMORY\n"));
|
|
goto LCleanup;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::InitServicesConfig
|
|
|
|
Initializes the Application scoped CServicesConfig object
|
|
===================================================================*/
|
|
HRESULT CAppln::InitServicesConfig()
|
|
{
|
|
HRESULT hr;
|
|
IServiceThreadPoolConfig *pIThreadPool = NULL;
|
|
IServiceSynchronizationConfig *pISyncConfig = NULL;
|
|
IServicePartitionConfig *pIPartitionConfig = NULL;
|
|
IServiceSxsConfig *pISxsConfig = NULL;
|
|
|
|
hr = CoCreateInstance(CLSID_CServiceConfig,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IUnknown,
|
|
(void **)&m_pServicesConfig);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not CCI ServicesConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = m_pServicesConfig->QueryInterface(IID_IServiceThreadPoolConfig, (void **)&pIThreadPool);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not QI for IServiceThreadPoolConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
if (m_pAppConfig->fExecuteInMTA()) {
|
|
hr = ViperConfigureMTA();
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Failed to Configure MTA, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
else {
|
|
hr = ViperConfigureSTA();
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Failed to Configure STA, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
|
|
hr = pIThreadPool->SelectThreadPool(m_pAppConfig->fExecuteInMTA() ? CSC_MTAThreadPool : CSC_STAThreadPool);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Failed to SelectThreadPool, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = m_pServicesConfig->QueryInterface(IID_IServiceSynchronizationConfig, (void **)&pISyncConfig);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not QI for IServiceSynchronizationConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = pISyncConfig->ConfigureSynchronization (m_pAppConfig->fExecuteInMTA() ? CSC_NewSynchronization : CSC_IfContainerIsSynchronized);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not ConfigureSynchronization, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
if (m_pAppConfig->fUsePartition()) {
|
|
|
|
if (m_pAppConfig->PPartitionGUID()) {
|
|
|
|
hr = m_pServicesConfig->QueryInterface(IID_IServicePartitionConfig, (void **)&pIPartitionConfig);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not QI for IServicePartitionConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = pIPartitionConfig->PartitionConfig(CSC_NewPartition);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not set PartitionConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = pIPartitionConfig->PartitionID(*m_pAppConfig->PPartitionGUID());
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not set PartitionID, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
else {
|
|
LogSWCError(InvalidPartitionGUID);
|
|
hr = E_FAIL;
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - fUsePartition Set, but no PartitionID\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
|
|
if (m_pAppConfig->fSxsEnabled()) {
|
|
|
|
hr = m_pServicesConfig->QueryInterface(IID_IServiceSxsConfig, (void **)&pISxsConfig);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not QI for IServiceSxsConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = pISxsConfig->SxsConfig(CSC_NewSxs);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not set SxsConfig, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
LPWSTR pwszApplnPath;
|
|
#if UNICODE
|
|
pwszApplnPath = m_pszApplnPath;
|
|
#else
|
|
CMBCSToWChar convPath;
|
|
|
|
hr = convPath.Init(m_pszApplnPath);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not convert ApplnPath to UNICODE, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
pwszApplnPath = convPath.GetString();
|
|
#endif
|
|
hr = pISxsConfig->SxsDirectory(pwszApplnPath);
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not set SxsDirectory, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
if (m_pAppConfig->szSxsName() && *m_pAppConfig->szSxsName()) {
|
|
CMBCSToWChar convName;
|
|
|
|
hr = convName.Init(m_pAppConfig->szSxsName());
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not convert SxsName to UNICODE, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
|
|
hr = pISxsConfig->SxsName(convName.GetString());
|
|
if (FAILED(hr)) {
|
|
DBGWARN((DBG_CONTEXT, "New Application Failed: InitServicesConfig() - Could not set SxsName, hr = %#08x\n", hr));
|
|
goto LCleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
LCleanup:
|
|
|
|
if (pIThreadPool)
|
|
pIThreadPool->Release();
|
|
|
|
if (pISyncConfig)
|
|
pISyncConfig->Release();
|
|
|
|
if (pIPartitionConfig)
|
|
pIPartitionConfig->Release();
|
|
|
|
if (pISxsConfig)
|
|
pISxsConfig->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CAppln::InitSessionCookieNames
|
|
|
|
Initialize session cookie names. called during CApplin::Init. Introduced in QFE fix 28823 by markzh.
|
|
|
|
Every application will have a unique session cookie name assigned by this function. This contrast
|
|
to the pre-28823 behavior where we have one session cookie shared by all applications. (Note
|
|
we still had separate cookies if applications are running in different processes). Sharing session
|
|
cookie requires us to accept cookie values generated for another application. That was ok until
|
|
we introduced KeepSessionIDSecure - cookies generated for other applications are reset by
|
|
current application if the current session is secure.
|
|
|
|
Session-id cookie name used to be process-wide. The naming scheme was prefix+processid.
|
|
Here we generate a news one for each application. The naming scheme is expanded to prefix +
|
|
processid + appid. The appid is from a monotonous counter. Appid assignment may change
|
|
when process restarts. But that doesn't matter because the old cookie shouldn't be accepted
|
|
anyway. Instead of increasing string length of cookie name to accomodate appid, we made it
|
|
stay at prefix + 8 chars, by better utilization of each digit. Now we allow 32 different values
|
|
in each of the 8 character positions. That gives us 40 bits of information space. 24 bits are
|
|
used by processid, 15 bits are used by appid, 1 bit is used to distinguish cookies on secure
|
|
and non-secure sessions. Only 24 bits of process id (not all 32 bits) are used, because the
|
|
most significant 8 bits of process id is always 0 on current Windows implementation (see
|
|
ExpLookupHandleTableEntry).
|
|
|
|
The above method alone will give the same session-id cookie name from every computer
|
|
as long as process id is 123 and application id is 5. If a browser happened to simultaneously
|
|
visit two different severs with the same process id and application id, they will share the
|
|
same cookie name. However, their cookies should not overwrite each other because the client
|
|
should store them separately for two different servers. Regardless, we still wish to have some
|
|
randomness in cookie name and try to minimize name coincidence by injecting some data unique
|
|
to the system. But this is NOT done perfectly. The way we implement this is by adding the
|
|
system start time (as system signature) to bit 8 to bit 23 of process id. "Add" is a reversable
|
|
operator which will maintain one-to-one mapping given a fixed system signature. That will
|
|
ensure different cookie names are issued if process ids are different.
|
|
|
|
|
|
Parameters:
|
|
none
|
|
|
|
Returns:
|
|
none
|
|
===================================================================*/
|
|
void CAppln::InitSessionCookieNames()
|
|
{
|
|
DWORD pid = GetCurrentProcessId();
|
|
DWORD aid = g_ApplnMgr.NextApplicationID();
|
|
ASSERT(aid <= 0x7fff); // not likely to have so many applications
|
|
|
|
aid <<=1; // last bit for secure flag
|
|
|
|
// Avoid name conflict between computers, inject system signature.
|
|
// The API GetTickCount is used to calculate system boot time. It returns a
|
|
// DWORD which will wrap around every 49 days. But that's fine because we
|
|
// only care about some bits of the result. Those are not affected by the
|
|
// wrap around. We avoided querying perf data for precise boot up time,
|
|
// as we may run into permission problems.
|
|
|
|
time_t curtime;
|
|
time(&curtime);
|
|
|
|
DWORD tic = GetTickCount();
|
|
|
|
// uptime is what time() would return when system booted, it will:
|
|
// - stay same (within accuracy) as time passes
|
|
// - stay same when tic wraps around 4G
|
|
// - be process independent
|
|
|
|
DWORD uptime = curtime*1000 - tic;
|
|
|
|
// set signature to uptime devided by accuracy (4 seconds). max ushort= 3 days
|
|
USHORT localSignature = (USHORT)(uptime / 4096);
|
|
|
|
// blend in system signature to process id.
|
|
pid += ((DWORD)localSignature) << 8;
|
|
|
|
// Encode the Non-secure version of th cookie
|
|
EncodeCookie(m_szSessionCookieName, pid, aid, FALSE);
|
|
|
|
// Encode the Secure version of the cookie
|
|
EncodeCookie(m_szSessionCookieNameSecure, pid, aid, TRUE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
HRESULT CAppln::EncodeCookie
|
|
|
|
Dependingon the fIsSecure flag. we will either encode the cookie from numbers to a string.
|
|
|
|
Parameters:
|
|
pszCookie - pointer to the string to be returned
|
|
pid - Process ID
|
|
aid - Application ID
|
|
fIsSecure - Should this be encoded as a secure cookie.
|
|
Returns:
|
|
|
|
String filled with the Cookie
|
|
===================================================================*/
|
|
void CAppln::EncodeCookie (char *pszCookie, DWORD pid, DWORD aid, BOOL fIsSecure)
|
|
{
|
|
strcpy(pszCookie, SZ_SESSION_ID_COOKIE_PREFIX);
|
|
pszCookie += CCH_SESSION_ID_COOKIE_PREFIX;
|
|
|
|
// the number of digits is hard coded to 8 here.
|
|
// it doesn't scale by simply adjusting a macro. sorry
|
|
// set cookie value to zero - we'll "bit or" 1's into it
|
|
memset(pszCookie, 0, 8+1); // +1 for null terminator
|
|
|
|
//
|
|
// Recommended for all versions of IIS except new products.
|
|
// This limits our behavior change to secure connections only.
|
|
// It may be lifted if someone request this later. When we'll
|
|
// have more confidence on this feature change.
|
|
//
|
|
|
|
USHORT wCookieSerial[3]; // cookie name in three words format
|
|
wCookieSerial[0] = (USHORT)pid;
|
|
wCookieSerial[1] = fIsSecure ? (USHORT)aid : 0;
|
|
wCookieSerial[2] = (USHORT)(pid>>16) & 0xFF;
|
|
|
|
// redistribute bits in w0-w2 (8*5) to pszCookie (5*8)
|
|
|
|
USHORT val; // keeps the sack of bits from wCookieSerial[], then seed them into pszCookie
|
|
|
|
for (int bit=0; bit<5; bit++)
|
|
{
|
|
//
|
|
// load value from wCookieSerial[]
|
|
//
|
|
if (bit%2==0)
|
|
{
|
|
val = wCookieSerial[bit/2];
|
|
}
|
|
|
|
//
|
|
// fill in the same bit for all 8 digits
|
|
//
|
|
for (int digit=0; digit<8; digit++)
|
|
{
|
|
pszCookie[digit] |= (char)((val & 1) << bit);
|
|
val >>= 1;
|
|
}
|
|
|
|
//
|
|
// advance to next bit
|
|
//
|
|
}
|
|
|
|
//
|
|
// encode numbers to string
|
|
//
|
|
static char EncodingTable[]=
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"012345"; // 32 token characters
|
|
|
|
//
|
|
// Replace numbers by characters
|
|
//
|
|
for (int i=0; i<8; i++)
|
|
{
|
|
pszCookie[i] = EncodingTable[pszCookie[i]];
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CAppln::Restart
|
|
|
|
Restart an application. (used for when global.asa changes or
|
|
debugging enable metabase key changes)
|
|
===================================================================*/
|
|
HRESULT CAppln::Restart(BOOL fForceRestart /* = FALSE*/)
|
|
{
|
|
AddRef(); // keep addref'd while restarting
|
|
|
|
DBGPRINTF((DBG_CONTEXT, "Restarting application %S, %p\n", m_pszMetabaseKey, this));
|
|
|
|
g_ApplnMgr.Lock();
|
|
|
|
// If already restarted or
|
|
// in the tombstone state or
|
|
// restart not allowed
|
|
// shutting down -> don't restart
|
|
if (m_fGlobalChanged ||
|
|
m_fTombstone ||
|
|
(!m_pAppConfig->fEnableApplicationRestart() && !fForceRestart) ||
|
|
IsShutDownInProgress())
|
|
{
|
|
// Give back the lock and refcount
|
|
// since we don't need them
|
|
g_ApplnMgr.UnLock();
|
|
Release();
|
|
return S_OK;
|
|
}
|
|
|
|
// remove the application from the global hash
|
|
|
|
CLinkElem *pLinkElem = g_ApplnMgr.DeleteElem
|
|
(
|
|
m_pszMetabaseKey,
|
|
_tcslen(m_pszMetabaseKey) * sizeof(TCHAR)
|
|
);
|
|
Assert(pLinkElem);
|
|
Assert(static_cast<CAppln *>(pLinkElem) == this);
|
|
|
|
//
|
|
// Indicate to Delete All Sessions.
|
|
// This also doubles up as a flag to indicate that this application has been placed on the application cleanup queue.
|
|
//
|
|
m_fGlobalChanged = TRUE;
|
|
|
|
// Unlock
|
|
g_ApplnMgr.UnLock();
|
|
|
|
// Increment the count of restarting applications
|
|
InterlockedIncrement((LPLONG)&g_nApplicationsRestarting);
|
|
|
|
// Increment the count of restarted applications
|
|
InterlockedIncrement((LPLONG)&g_nApplicationsRestarted);
|
|
|
|
m_pSessionMgr->UnScheduleSessionKiller();
|
|
|
|
// cleanup the directory monitor entries
|
|
|
|
Lock(); // Place critical Section around access to m_rgpvDME
|
|
while (m_rgpvDME.Count())
|
|
{
|
|
static_cast<CDirMonitorEntry *>(m_rgpvDME[0])->Release();
|
|
m_rgpvDME.Remove(0);
|
|
}
|
|
|
|
m_rgpvDME.Clear();
|
|
UnLock();
|
|
|
|
|
|
// add this application to the CleanupManager...
|
|
g_ApplnCleanupMgr.AddAppln(this);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::ApplnCleanupProc
|
|
|
|
Called by the g_ApplnCleanupMgr thread to complete cleanup
|
|
|
|
===================================================================*/
|
|
DWORD __stdcall CAppln::ApplnCleanupProc(VOID *pArg)
|
|
{
|
|
CAppln *pAppln = (CAppln *)pArg;
|
|
|
|
DBGPRINTF((DBG_CONTEXT, "[ApplnCleanupProc] enterred for %S, %p\n", pAppln->m_pszMetabaseKey, pArg));
|
|
|
|
// Let the requests to drain while trying to delete sessions
|
|
while ((pAppln->m_cRequests || pAppln->m_cSessions))
|
|
{
|
|
if (pAppln->m_cSessions)
|
|
pAppln->m_pSessionMgr->DeleteAllSessions(FALSE);
|
|
|
|
if (pAppln->m_cSessions || pAppln->m_cRequests)
|
|
Sleep(200);
|
|
}
|
|
|
|
g_ApplnMgr.DeleteApplicationIfExpired(pAppln);
|
|
|
|
// Decrement the count of restarting applications
|
|
InterlockedDecrement((LPLONG)&g_nApplicationsRestarting);
|
|
|
|
// Wake up the thread so that other threads can run.
|
|
g_ApplnCleanupMgr.Wakeup();
|
|
|
|
//
|
|
// Corresponds to the Addref in Restart.
|
|
// Once the request has been queued to COM we can release its reference
|
|
//
|
|
pAppln->Release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::UnInit
|
|
|
|
Convert to tombstone state
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
HRESULT (S_OK)
|
|
===================================================================*/
|
|
HRESULT CAppln::UnInit()
|
|
{
|
|
Assert(!m_fTombstone); // don't do it twice
|
|
|
|
#ifdef DBG_NOTIFICATION
|
|
#if UNICODE
|
|
DBGPRINTF((DBG_CONTEXT, "Uniniting application %S, %p\n", m_pszApplnPath, this));
|
|
#else
|
|
DBGPRINTF((DBG_CONTEXT, "Uniniting application %s, %p\n", m_pszApplnPath, this));
|
|
#endif
|
|
#endif // DBG_NOTIFICATION
|
|
|
|
// Flush the global.asa from the script engine cache
|
|
if (m_pszGlobalAsa)
|
|
{
|
|
g_ScriptManager.FlushCache(m_pszGlobalAsa);
|
|
}
|
|
|
|
if (m_pGlobalTemplate)
|
|
{
|
|
// Keep template (and inc file) cache locked while releasing
|
|
// GLOBAL.ASA template so that it wouldn't step onto Flush logic
|
|
//
|
|
// NOTE: CTemplate::End potentially queues global.asa for cleanup on
|
|
// our thread! CleanupEngines() must therefore be called
|
|
// *after* this step.
|
|
//
|
|
LockTemplateAndIncFileCaches();
|
|
m_pGlobalTemplate->End();
|
|
UnLockTemplateAndIncFileCaches();
|
|
|
|
m_pGlobalTemplate = NULL;
|
|
}
|
|
|
|
//If NT, remove this app from any file/appln mappings it may be in
|
|
g_FileAppMap.Lock();
|
|
int i = m_rgpvFileAppln.Count();
|
|
while (i > 0)
|
|
{
|
|
#ifdef DBG_NOTIFICATION
|
|
DBGPRINTF((DBG_CONTEXT, "Removing application from File/App mapping\n"));
|
|
#endif // DBG_NOTIFICATION
|
|
|
|
static_cast<CFileApplnList *>(m_rgpvFileAppln[0])->RemoveApplication(this);
|
|
m_rgpvFileAppln.Remove(0);
|
|
i--;
|
|
}
|
|
g_FileAppMap.UnLock();
|
|
m_rgpvFileAppln.Clear();
|
|
|
|
Lock(); // Protecting m_rqpvDME with a critical section
|
|
//
|
|
// In case there are some DME's left around then release them.
|
|
//
|
|
while (m_rgpvDME.Count())
|
|
{
|
|
static_cast<CDirMonitorEntry *>(m_rgpvDME[0])->Release();
|
|
m_rgpvDME.Remove(0);
|
|
}
|
|
|
|
//
|
|
// Safe to free the array now.
|
|
//
|
|
m_rgpvDME.Clear();
|
|
|
|
UnLock();
|
|
|
|
// If debuggable application, clean up pending scripts
|
|
if (m_fDebuggable)
|
|
g_ApplnMgr.CleanupEngines();
|
|
|
|
|
|
// Free the properties collection
|
|
if (m_pProperties)
|
|
{
|
|
m_pProperties->UnInit();
|
|
m_pProperties->Release();
|
|
m_pProperties = NULL;
|
|
}
|
|
|
|
// Free the tagged objects collection
|
|
if (m_pTaggedObjects)
|
|
{
|
|
m_pTaggedObjects->UnInit();
|
|
m_pTaggedObjects->Release();
|
|
m_pTaggedObjects = NULL;
|
|
}
|
|
|
|
// Before we close down, debuggable templates need to be made non-debuggable
|
|
if (m_fDebuggable)
|
|
g_TemplateCache.RemoveApplicationFromDebuggerUI(this);
|
|
|
|
if (m_pAppRoot)
|
|
{
|
|
m_pAppRoot->Detach();
|
|
m_pAppRoot->Close();
|
|
m_pAppRoot->Release();
|
|
m_pAppRoot = NULL;
|
|
}
|
|
|
|
if (m_pApplCompCol)
|
|
{
|
|
delete m_pApplCompCol;
|
|
m_pApplCompCol = NULL;
|
|
}
|
|
|
|
if (m_pActivity)
|
|
{
|
|
delete m_pActivity;
|
|
m_pActivity = NULL;
|
|
}
|
|
|
|
if (m_pSessionMgr)
|
|
{
|
|
delete m_pSessionMgr;
|
|
m_pSessionMgr = NULL;
|
|
}
|
|
|
|
if (m_pAppConfig)
|
|
{
|
|
m_pAppConfig->UnInit();
|
|
|
|
// Using Reference counting on the CAppConfig object.
|
|
m_pAppConfig->Release();
|
|
|
|
m_pAppConfig = NULL;
|
|
}
|
|
|
|
if (m_pdispGlobTypeLibWrapper)
|
|
{
|
|
m_pdispGlobTypeLibWrapper->Release();
|
|
m_pdispGlobTypeLibWrapper = NULL;
|
|
}
|
|
|
|
if (m_pszGlobalAsa)
|
|
{
|
|
// If there was a change notification on global.asa
|
|
// then flush the template now.
|
|
// UNDONE: flush correct global.asa
|
|
if (m_fGlobalChanged)
|
|
g_TemplateCache.Flush(m_pszGlobalAsa, MATCH_ALL_INSTANCE_IDS);
|
|
|
|
delete [] m_pszGlobalAsa;
|
|
m_pszGlobalAsa = NULL;
|
|
}
|
|
|
|
if (m_pszMetabaseKey)
|
|
{
|
|
delete [] m_pszMetabaseKey;
|
|
m_pszMetabaseKey = NULL;
|
|
}
|
|
|
|
if (m_pszApplnPath)
|
|
{
|
|
delete [] m_pszApplnPath;
|
|
m_pszApplnPath = NULL;
|
|
}
|
|
|
|
if (m_pszApplnVRoot)
|
|
{
|
|
delete [] m_pszApplnVRoot;
|
|
m_pszApplnVRoot = NULL;
|
|
}
|
|
|
|
if (m_fInternalLockInited)
|
|
DeleteCriticalSection(&m_csInternalLock);
|
|
|
|
if (m_fApplnLockInited)
|
|
DeleteCriticalSection(&m_csApplnLock);
|
|
|
|
if (m_pServicesConfig)
|
|
{
|
|
m_pServicesConfig->Release();
|
|
m_pServicesConfig = NULL;
|
|
}
|
|
|
|
// Mark this application as Tombstone
|
|
m_fTombstone = TRUE;
|
|
|
|
InterlockedDecrement((LPLONG)&g_nApplications);
|
|
|
|
// Disconnennect from proxies (in case we are shutting down or will shortly shut down)
|
|
|
|
CoDisconnectObject(static_cast<IApplicationObject *>(this), 0);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::BindToActivity
|
|
|
|
Creates application level activity either
|
|
as a clone of the given activity or
|
|
as a brand new activity
|
|
|
|
Must be called within critical section. Does not lock itself.
|
|
|
|
Parameters:
|
|
CViperActivity *pActivity activity to clone (could be NULL)
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
HRESULT CAppln::BindToActivity
|
|
(
|
|
CViperActivity *pActivity
|
|
)
|
|
{
|
|
if (m_pActivity)
|
|
{
|
|
// multiple requests to bind to new activity are ok
|
|
if (!pActivity)
|
|
return S_OK;
|
|
// but not to clone from an existing activity
|
|
Assert(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
m_pActivity = new CViperActivity;
|
|
if (!m_pActivity)
|
|
return E_OUTOFMEMORY;
|
|
|
|
HRESULT hr;
|
|
|
|
if (pActivity)
|
|
hr = m_pActivity->InitClone(pActivity);
|
|
else
|
|
hr = m_pActivity->Init(m_pServicesConfig);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete m_pActivity;
|
|
m_pActivity = NULL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::SetGlobalAsa
|
|
|
|
Remembers GLOBAL.ASA file path for this application
|
|
|
|
Parameters:
|
|
const char *pszGlobalAsa path to (copy and) remember
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CAppln::SetGlobalAsa
|
|
(
|
|
const TCHAR *pszGlobalAsa
|
|
)
|
|
{
|
|
// remove existing
|
|
if (m_pszGlobalAsa)
|
|
{
|
|
delete [] m_pszGlobalAsa;
|
|
m_pszGlobalAsa = NULL;
|
|
}
|
|
|
|
// store new
|
|
if (pszGlobalAsa)
|
|
{
|
|
DWORD cch = _tcslen(pszGlobalAsa);
|
|
DWORD cb = (cch + 1) * sizeof(TCHAR);
|
|
|
|
m_pszGlobalAsa = new TCHAR[cch+1];
|
|
if (!m_pszGlobalAsa)
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy(m_pszGlobalAsa, pszGlobalAsa, cb);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::AddDirMonitorEntry
|
|
|
|
Remembers change notifcation monitor entries for this application
|
|
|
|
Parameters:
|
|
pDirMonitorEntry pointer to DME
|
|
|
|
Returns:
|
|
S_OK if the monitor entry was added to the list
|
|
===================================================================*/
|
|
HRESULT CAppln::AddDirMonitorEntry(CDirMonitorEntry *pDirMonitorEntry)
|
|
{
|
|
DBG_ASSERT(m_fInited);
|
|
DBG_ASSERT(pDirMonitorEntry);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Add the DME to the list
|
|
Lock(); // Protect m_rgpvDME by critical section
|
|
if (FAILED(hr = m_rgpvDME.Append(pDirMonitorEntry)))
|
|
{
|
|
pDirMonitorEntry->Release();
|
|
}
|
|
UnLock();
|
|
return hr;
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::AddFileApplnEntry
|
|
|
|
Remembers change notifcation monitor entries for this application
|
|
|
|
Parameters:
|
|
pFileAppln pointer to FileApplnEntry
|
|
|
|
Returns:
|
|
S_OK if the monitor entry was added to the list
|
|
S_FALSE if the monitor entry was alread in the list
|
|
===================================================================*/
|
|
HRESULT CAppln::AddFileApplnEntry(CFileApplnList *pFileAppln)
|
|
{
|
|
DBG_ASSERT(m_fInited);
|
|
DBG_ASSERT(pFileAppln);
|
|
|
|
HRESULT hr = S_OK;
|
|
int index;
|
|
|
|
// See if the file/application entry is alreay in the list
|
|
hr = m_rgpvFileAppln.Find(pFileAppln, &index);
|
|
if (hr == S_FALSE)
|
|
{
|
|
// Add the file/application entry to the list
|
|
hr = m_rgpvFileAppln.Append(pFileAppln);
|
|
}
|
|
else
|
|
{
|
|
// The file/application entry was already in the list
|
|
hr = S_FALSE;
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::LogSWCError
|
|
|
|
Logs an error to the NT Event log for any config errors that arise
|
|
in using COM+ Services Without Components.
|
|
|
|
Parameters:
|
|
REFIID riid
|
|
void **ppv
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
void CAppln::LogSWCError(enum eSWCERRORS error)
|
|
{
|
|
LPCSTR szStrings[4];
|
|
LPCWSTR szwStrings[4];
|
|
WORD cStrings = 0;
|
|
DWORD dwEventID = 0;
|
|
BOOL fUnicode = FALSE;
|
|
|
|
|
|
switch(error) {
|
|
|
|
case PartitionAccessDenied : {
|
|
if (m_fSWC_PartitionAccessDenied == FALSE) {
|
|
m_fSWC_PartitionAccessDenied = TRUE;
|
|
dwEventID = MSG_SWC_PARTITION_ACCESS_DENIED;
|
|
szStrings[0] = m_pAppConfig->szPartition();
|
|
cStrings = 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case InvalidPartitionGUID: {
|
|
if (m_fSWC_InvalidPartitionGUID == FALSE) {
|
|
m_fSWC_InvalidPartitionGUID = TRUE;
|
|
dwEventID = MSG_SWC_INVALID_PARTITION_GUID;
|
|
szwStrings[0] = m_pszApplnVRoot;
|
|
cStrings = 1;
|
|
fUnicode = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (dwEventID) {
|
|
|
|
if (fUnicode)
|
|
ReportAnEventW(dwEventID, (WORD) EVENTLOG_ERROR_TYPE, cStrings, szwStrings);
|
|
else
|
|
ReportAnEvent(dwEventID, (WORD) EVENTLOG_ERROR_TYPE, cStrings, szStrings);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*===================================================================
|
|
CAppln::QueryInterface
|
|
|
|
QueryInterface() -- IApplicationObject implementation.
|
|
|
|
Parameters:
|
|
REFIID riid
|
|
void **ppv
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::QueryInterface
|
|
(
|
|
REFIID riid,
|
|
void **ppv
|
|
)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (IID_IUnknown == riid ||
|
|
IID_IDispatch == riid ||
|
|
IID_IApplicationObject == riid ||
|
|
IID_IDenaliIntrinsic == riid)
|
|
{
|
|
*ppv = static_cast<IApplicationObject *>(this);
|
|
}
|
|
else if (IID_ISupportErrorInfo == riid)
|
|
{
|
|
*ppv = &m_ISuppErrImp;
|
|
}
|
|
else if (IID_IMarshal == riid)
|
|
{
|
|
Assert( m_pUnkFTM != NULL );
|
|
|
|
if ( m_pUnkFTM == NULL )
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
return m_pUnkFTM->QueryInterface( riid, ppv );
|
|
|
|
}
|
|
if (*ppv)
|
|
{
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::AddRef
|
|
|
|
AddRef() -- IUnknown implementation.
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CAppln::AddRef()
|
|
{
|
|
DWORD cRefs = InterlockedIncrement((LPLONG)&m_cRefs);
|
|
|
|
IF_DEBUG(APPLICATION) {
|
|
WriteRefTraceLog(gm_pTraceLog, cRefs, this);
|
|
}
|
|
return cRefs;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::Release
|
|
|
|
Release() -- IUnknown implementation.
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
Ref count
|
|
===================================================================*/
|
|
STDMETHODIMP_(ULONG) CAppln::Release()
|
|
{
|
|
|
|
DWORD cRefs = InterlockedDecrement((LPLONG)&m_cRefs);
|
|
|
|
IF_DEBUG(APPLICATION) {
|
|
WriteRefTraceLog(gm_pTraceLog, cRefs, this);
|
|
}
|
|
|
|
if (cRefs)
|
|
return cRefs;
|
|
|
|
delete this;
|
|
|
|
InterlockedDecrement((LPLONG)&g_nApplicationObjectsActive);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::CheckForTombstone
|
|
|
|
Tombstone stub for IApplicationObject methods. If the object is
|
|
tombstone, does ExceptionId and fails.
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT E_FAIL if Tombstone
|
|
S_OK if not
|
|
===================================================================*/
|
|
HRESULT CAppln::CheckForTombstone()
|
|
{
|
|
if (!m_fTombstone)
|
|
return S_OK;
|
|
|
|
ExceptionId
|
|
(
|
|
IID_IApplicationObject,
|
|
IDE_APPLICATION,
|
|
IDE_INTRINSIC_OUT_OF_SCOPE
|
|
);
|
|
return E_FAIL;
|
|
}
|
|
/*===================================================================
|
|
CAppln::Lock
|
|
|
|
IApplicationObject method.
|
|
|
|
Will allow the user to lock the application intrinsic for the
|
|
purpose of adding/deleting values.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
HRESULT S_OK on success
|
|
E_FAIL otherwise
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::Lock()
|
|
{
|
|
if (FAILED(CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
Assert(m_fApplnLockInited);
|
|
|
|
DWORD dwId = GetCurrentThreadId();
|
|
|
|
// If this thread already has the lock, increment lock ref count
|
|
|
|
if (m_dwLockThreadID == dwId)
|
|
{
|
|
m_cLockRefCount++;
|
|
}
|
|
else
|
|
{
|
|
EnterCriticalSection(&m_csApplnLock);
|
|
m_cLockRefCount = 1;
|
|
m_dwLockThreadID = dwId;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::UnLock
|
|
|
|
IApplicationObject method.
|
|
|
|
Will allow the user to unlock the application intrinsic only
|
|
if it has been locked by this user.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
HRESULT S_OK
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::UnLock()
|
|
{
|
|
if (FAILED(CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (m_dwLockThreadID == GetCurrentThreadId())
|
|
{
|
|
if (--m_cLockRefCount == 0)
|
|
{
|
|
// Unlock the application
|
|
m_dwLockThreadID = INVALID_THREADID;
|
|
LeaveCriticalSection(&m_csApplnLock);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::UnLockAfterRequest
|
|
|
|
Remove any application locks left by the user script
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
HRESULT S_OK
|
|
===================================================================*/
|
|
HRESULT CAppln::UnLockAfterRequest()
|
|
{
|
|
Assert(!m_fTombstone);
|
|
|
|
if (m_cLockRefCount > 0 && m_dwLockThreadID == GetCurrentThreadId())
|
|
{
|
|
m_cLockRefCount = 0;
|
|
m_dwLockThreadID = INVALID_THREADID;
|
|
LeaveCriticalSection(&m_csApplnLock);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::get_Value
|
|
|
|
IApplicationObject method.
|
|
|
|
Will allow the user to retreive a application state variable,
|
|
the variable will come as a named pair, bstr is the name and
|
|
var is the value or object to be returned for that name
|
|
|
|
Parameters:
|
|
BSTR FAR * bstrName Name of the variable to get
|
|
VARIANT * pVar Value/object to get for the variable
|
|
|
|
Returns:
|
|
HRESULT S_OK on success
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::get_Value
|
|
(
|
|
BSTR bstrName,
|
|
VARIANT *pVar
|
|
)
|
|
{
|
|
if (FAILED(CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (bstrName == NULL)
|
|
{
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
return E_FAIL;
|
|
}
|
|
|
|
VariantInit(pVar); // default variant empty
|
|
|
|
WCHAR *pwszName;
|
|
STACK_BUFFER(rgbName, 42);
|
|
WSTR_STACK_DUP(bstrName, &rgbName, &pwszName);
|
|
|
|
if (pwszName == NULL)
|
|
return S_OK; // no name - no value - no error
|
|
//_wcsupr(pwszName);
|
|
|
|
Assert(m_pApplCompCol);
|
|
|
|
HRESULT hr = S_OK;
|
|
CComponentObject *pObj = NULL;
|
|
|
|
// Lock the application
|
|
Lock();
|
|
|
|
hr = m_pApplCompCol->GetProperty(pwszName, &pObj);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(pObj);
|
|
hr = pObj->GetVariant(pVar);
|
|
}
|
|
|
|
// UnLock the application
|
|
UnLock();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::putref_Value
|
|
|
|
IApplicationObject method.
|
|
|
|
Will allow the user to assign a application state variable to be saved
|
|
the variable will come as a named pair, bstr is the name and
|
|
var is the value or object to be stored for that name
|
|
|
|
Parameters:
|
|
BSTR bstrName Name of the variable to set
|
|
VARIANT Var Value/object to set for the variable
|
|
|
|
Returns:
|
|
HRESULT S_OK on success
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::putref_Value
|
|
(
|
|
BSTR bstrName,
|
|
VARIANT Var
|
|
)
|
|
{
|
|
if (FAILED(CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (FIsIntrinsic(&Var))
|
|
{
|
|
ExceptionId(IID_IApplicationObject, IDE_APPLICATION,
|
|
IDE_APPLICATION_CANT_STORE_INTRINSIC);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (bstrName == NULL)
|
|
{
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
Assert(m_pApplCompCol);
|
|
|
|
// Prepare property name
|
|
WCHAR *pwszName;
|
|
STACK_BUFFER(rgbName, 42);
|
|
WSTR_STACK_DUP(bstrName, &rgbName, &pwszName);
|
|
|
|
if (pwszName == NULL)
|
|
{
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
return E_FAIL;
|
|
}
|
|
//_wcsupr(pwszName);
|
|
|
|
// Lock the application
|
|
Lock();
|
|
|
|
hr = m_pApplCompCol->AddProperty(pwszName, &Var);
|
|
|
|
// Unlock the application
|
|
UnLock();
|
|
|
|
if (hr == RPC_E_WRONG_THREAD)
|
|
{
|
|
// We use RPC_E_WRONG_THREAD to indicate bad model object
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_APPLICATION_CANT_STORE_OBJECT);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::put_Value
|
|
|
|
IApplicationObject method.
|
|
|
|
Implement property put by dereferencing variants before
|
|
calling putref.
|
|
|
|
Parameters:
|
|
BSTR FAR * bstrName Name of the variable to set
|
|
VARIANT Var Value/object to set for the variable
|
|
|
|
Returns:
|
|
HRESULT S_OK on success
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::put_Value
|
|
(
|
|
BSTR bstrName,
|
|
VARIANT Var
|
|
)
|
|
{
|
|
if (FAILED(CheckForTombstone()))
|
|
return E_FAIL;
|
|
|
|
if (bstrName == NULL)
|
|
{
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
return E_FAIL;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
Assert(m_pApplCompCol);
|
|
|
|
// Prepare property name
|
|
WCHAR *pwszName;
|
|
STACK_BUFFER(rgbName, 42);
|
|
WSTR_STACK_DUP(bstrName, &rgbName, &pwszName);
|
|
|
|
if (pwszName == NULL)
|
|
{
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_EXPECTING_STR);
|
|
return E_FAIL;
|
|
}
|
|
//_wcsupr(pwszName);
|
|
|
|
// Lock the application
|
|
Lock();
|
|
|
|
VARIANT varResolved;
|
|
hr = VariantResolveDispatch(&varResolved, &Var,
|
|
IID_IApplicationObject,
|
|
IDE_APPLICATION);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = m_pApplCompCol->AddProperty(pwszName, &varResolved);
|
|
VariantClear(&varResolved);
|
|
}
|
|
|
|
// Unlock the application
|
|
UnLock();
|
|
|
|
if (hr == RPC_E_WRONG_THREAD)
|
|
{
|
|
// We use RPC_E_WRONG_THREAD to indicate bad model object
|
|
ExceptionId(IID_IApplicationObject,
|
|
IDE_APPLICATION, IDE_APPLICATION_CANT_STORE_OBJECT);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::get_Contents
|
|
|
|
Return the application contents dictionary
|
|
===================================================================*/
|
|
|
|
STDMETHODIMP CAppln::get_Contents(IVariantDictionary **ppDictReturn)
|
|
{
|
|
if (FAILED(CheckForTombstone()) || !m_pProperties)
|
|
return E_FAIL;
|
|
|
|
return m_pProperties->QueryInterface(IID_IVariantDictionary, reinterpret_cast<void **>(ppDictReturn));
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::get_StaticObjects
|
|
|
|
Return the application static objects dictionary
|
|
===================================================================*/
|
|
STDMETHODIMP CAppln::get_StaticObjects(IVariantDictionary **ppDictReturn)
|
|
{
|
|
if (FAILED(CheckForTombstone()) || !m_pTaggedObjects)
|
|
return E_FAIL;
|
|
|
|
return m_pTaggedObjects->QueryInterface(IID_IVariantDictionary, reinterpret_cast<void **>(ppDictReturn));
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::UpdateConfig
|
|
|
|
Updates configuration from metabase if needed
|
|
===================================================================*/
|
|
HRESULT CAppln::UpdateConfig(CIsapiReqInfo *pIReq, BOOL *pfRestart, BOOL *pfFlushAll)
|
|
{
|
|
BOOL fRestart = FALSE;
|
|
BOOL fFlushAll = FALSE;
|
|
|
|
if (m_pAppConfig->fNeedUpdate())
|
|
{
|
|
InternalLock();
|
|
|
|
if (m_pAppConfig->fNeedUpdate()) // still need update?
|
|
{
|
|
|
|
char lastPartitionGUID[64];
|
|
lastPartitionGUID[0] = '\0';
|
|
|
|
BOOL fAllowedDebugging = m_pAppConfig->fAllowDebugging();
|
|
BOOL fAllowedClientDebug = m_pAppConfig->fAllowClientDebug();
|
|
BOOL fAllowedRestart = m_pAppConfig->fEnableApplicationRestart();
|
|
BOOL fParentPathsEnabled = m_pAppConfig->fEnableParentPaths();
|
|
UINT uLastCodePage = m_pAppConfig->uCodePage();
|
|
LCID uLastLCID = m_pAppConfig->uLCID();
|
|
BOOL fPrevSxsEnabled = m_pAppConfig->fSxsEnabled();
|
|
BOOL fPrevUsePartition = m_pAppConfig->fUsePartition();
|
|
BOOL fPrevUseTracker = m_pAppConfig->fTrackerEnabled();
|
|
BOOL fPrevKeepSessionIDSecure = m_pAppConfig->fKeepSessionIDSecure();
|
|
BOOL fPrevCalcLineNumber = m_pAppConfig->fCalcLineNumber();
|
|
if (m_pAppConfig->fUsePartition() && m_pAppConfig->szPartition()) {
|
|
strncpy(lastPartitionGUID, m_pAppConfig->szPartition(), sizeof(lastPartitionGUID));
|
|
lastPartitionGUID[sizeof(lastPartitionGUID)-1] = '\0';
|
|
}
|
|
|
|
BOOL fRestartEnabledUpdated = m_pAppConfig->fRestartEnabledUpdated();
|
|
char szLastDefaultEngine[64];
|
|
strncpy(szLastDefaultEngine, m_pAppConfig->szScriptLanguage(), sizeof szLastDefaultEngine);
|
|
szLastDefaultEngine[sizeof(szLastDefaultEngine) - 1] = '\0';
|
|
|
|
m_pAppConfig->Update(pIReq);
|
|
|
|
BOOL fAllowDebugging = m_pAppConfig->fAllowDebugging();
|
|
BOOL fAllowClientDebug = m_pAppConfig->fAllowClientDebug();
|
|
BOOL fAllowRestart = m_pAppConfig->fEnableApplicationRestart();
|
|
BOOL fEnableParentPaths = m_pAppConfig->fEnableParentPaths();
|
|
UINT uCodePage = m_pAppConfig->uCodePage();
|
|
LCID uLCID = m_pAppConfig->uLCID();
|
|
BOOL fCurSxsEnabled = m_pAppConfig->fSxsEnabled();
|
|
BOOL fCurUsePartition = m_pAppConfig->fUsePartition();
|
|
BOOL fCurUseTracker = m_pAppConfig->fTrackerEnabled();
|
|
BOOL fCurKeepSessionIDSecure = m_pAppConfig->fKeepSessionIDSecure();
|
|
BOOL fCurCalcLineNumber = m_pAppConfig->fCalcLineNumber();
|
|
|
|
const char *szNewDefaultEngine = m_pAppConfig->szScriptLanguage();
|
|
|
|
fFlushAll = strcmpi(szLastDefaultEngine, szNewDefaultEngine) != 0
|
|
|| (fParentPathsEnabled != fEnableParentPaths)
|
|
|| (uLastCodePage != uCodePage)
|
|
|| (uLastLCID != uLCID);
|
|
|
|
fRestart = (fAllowDebugging != fAllowedDebugging) ||
|
|
(fAllowClientDebug != fAllowedClientDebug) ||
|
|
((fAllowRestart != fAllowedRestart) && fAllowRestart) ||
|
|
((fAllowRestart == fAllowedRestart) && fRestartEnabledUpdated) ||
|
|
(fCurSxsEnabled != fPrevSxsEnabled) ||
|
|
(fCurUsePartition != fPrevUsePartition) ||
|
|
(fCurUseTracker != fPrevUseTracker) ||
|
|
(fCurKeepSessionIDSecure != fPrevKeepSessionIDSecure) ||
|
|
(fCurCalcLineNumber != fPrevCalcLineNumber) ||
|
|
(fCurUsePartition
|
|
&& fPrevUsePartition
|
|
&& ((m_pAppConfig->szPartition() == NULL)
|
|
|| (strcmpi(lastPartitionGUID, m_pAppConfig->szPartition()) != 0))) ||
|
|
fFlushAll;
|
|
}
|
|
InternalUnLock();
|
|
}
|
|
|
|
if (pfRestart)
|
|
*pfRestart = fRestart;
|
|
|
|
if (pfFlushAll)
|
|
*pfFlushAll = fFlushAll;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CAppln::FPathMonitored()
|
|
|
|
Checks the list of DMEs in application to see if the specified path
|
|
is already being monitored.
|
|
|
|
===================================================================*/
|
|
CASPDirMonitorEntry *CAppln::FPathMonitored(LPCTSTR pszPath)
|
|
{
|
|
int i;
|
|
|
|
Lock(); // Protect m_rqpvDME by a critical section
|
|
int cDMEs = m_rgpvDME.Count();
|
|
for (i=0; i < cDMEs; i++) {
|
|
CASPDirMonitorEntry *pDME = static_cast<CASPDirMonitorEntry *>(m_rgpvDME[i]);
|
|
if (pDME == NULL)
|
|
break;
|
|
if (pDME->FPathMonitored(pszPath))
|
|
{
|
|
UnLock();
|
|
return pDME;
|
|
}
|
|
}
|
|
|
|
UnLock();
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef DBG
|
|
/*===================================================================
|
|
CAppln::AssertValid
|
|
|
|
Test to make sure that the CAppln object is currently correctly
|
|
formed and assert if it is not.
|
|
|
|
Returns:
|
|
Nothing
|
|
|
|
Side effects:
|
|
None.
|
|
===================================================================*/
|
|
void CAppln::AssertValid() const
|
|
{
|
|
Assert(m_fInited);
|
|
|
|
Assert(m_pSessionMgr);
|
|
|
|
Assert(m_pApplCompCol);
|
|
m_pApplCompCol->AssertValid();
|
|
}
|
|
#endif // DBG
|
|
|
|
|
|
/*===================================================================
|
|
C A p p l n M g r
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CApplnMgr::CApplnMgr
|
|
|
|
Application Manager constructor.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
CApplnMgr::CApplnMgr()
|
|
: m_fInited(FALSE),
|
|
m_fHashTableInited(FALSE),
|
|
m_fCriticalSectionInited(FALSE),
|
|
m_cntApp(0),
|
|
m_pMetabase(NULL),
|
|
m_pMetabaseSink(NULL),
|
|
m_dwMDSinkCookie(0)
|
|
{
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnMgr::~CApplnMgr
|
|
|
|
Application Manager destructor.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
CApplnMgr::~CApplnMgr()
|
|
{
|
|
if (!m_fInited)
|
|
UnInit();
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnMgr::Init
|
|
|
|
Initializes the Appln Manager.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::Init( void )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Assert(!m_fInited);
|
|
|
|
// Init hash table
|
|
|
|
hr = CHashTable::Init(NUM_APPLMGR_HASHING_BUCKETS);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
m_fHashTableInited = TRUE;
|
|
|
|
// Init critical section
|
|
|
|
ErrInitCriticalSection(&m_csLock, hr);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
m_fCriticalSectionInited = TRUE;
|
|
|
|
m_fInited = TRUE;
|
|
|
|
return g_ApplnCleanupMgr.Init();
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnMgr::UnInit
|
|
|
|
UnInitializes the Appln Manager.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::UnInit( void )
|
|
{
|
|
//
|
|
// release the metabase change notification. Ignore return value as it is always S_OK
|
|
//
|
|
UnInitMBListener();
|
|
|
|
//
|
|
// Wait for COM to release all Sinks
|
|
//
|
|
while (!g_fProceedWithShutdownAppln)
|
|
Sleep (100);
|
|
|
|
|
|
if (m_fHashTableInited)
|
|
{
|
|
CHashTable::UnInit();
|
|
m_fHashTableInited = FALSE;
|
|
}
|
|
|
|
if (m_fCriticalSectionInited)
|
|
{
|
|
DeleteCriticalSection(&m_csLock);
|
|
m_fCriticalSectionInited = FALSE;
|
|
}
|
|
|
|
m_fInited = FALSE;
|
|
return g_ApplnCleanupMgr.UnInit();
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnMgr::InitMBListener
|
|
|
|
Registers the CMDAppConfigSink object to listen to sink notifications. Performs an Advise on the ABO
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory
|
|
===================================================================*/
|
|
|
|
HRESULT CApplnMgr::InitMBListener( )
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
IConnectionPointContainer *pConnPointContainer = NULL;
|
|
IConnectionPoint *pConnPoint = NULL;
|
|
IClassFactory *pcsfFactory = NULL;
|
|
HANDLE hCurrentUser = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Get a pointer to the Metabase
|
|
//
|
|
|
|
AspDoRevertHack (&hCurrentUser);
|
|
|
|
hr = GetMetabaseIF(&m_pMetabase);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
AspUndoRevertHack (&hCurrentUser);
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Register to get notifications on the Sink
|
|
//
|
|
m_pMetabaseSink = new CMDAppConfigSink(this);
|
|
if (!m_pMetabaseSink)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto LExit;
|
|
}
|
|
|
|
m_dwMDSinkCookie = 0;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//Advise Metabase about SinkNotify().
|
|
hr = m_pMetabase->QueryInterface(IID_IConnectionPointContainer, (void **)&pConnPointContainer);
|
|
|
|
if (pConnPointContainer != NULL)
|
|
{
|
|
//Find the requested Connection Point. This AddRef's the return pointer.
|
|
hr = pConnPointContainer->FindConnectionPoint(IID_IMSAdminBaseSink, &pConnPoint);
|
|
pConnPointContainer->Release();
|
|
|
|
if (pConnPoint != NULL)
|
|
{
|
|
hr = pConnPoint->Advise((IUnknown *)m_pMetabaseSink, &m_dwMDSinkCookie);
|
|
pConnPoint->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
LExit:
|
|
if (FAILED(hr))
|
|
{
|
|
m_pMetabase->Release();
|
|
m_pMetabase = NULL;
|
|
}
|
|
|
|
AspUndoRevertHack(&hCurrentUser);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnMgr::UnInitMBListener
|
|
|
|
Unregisters the Application manager from receiving sink notifications. Performs an UnAdvise on the ABO
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::UnInitMBListener( )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IConnectionPointContainer *pConnPointContainer = NULL;
|
|
IConnectionPoint *pConnPoint = NULL;
|
|
CHAR szErr[256];
|
|
HANDLE hCurrentUser = INVALID_HANDLE_VALUE;
|
|
|
|
AspDoRevertHack (&hCurrentUser);
|
|
|
|
if (m_pMetabase) {
|
|
|
|
//Advise Metabase about SinkNotify().
|
|
hr = m_pMetabase->QueryInterface(IID_IConnectionPointContainer, (void **)&pConnPointContainer);
|
|
if (pConnPointContainer != NULL)
|
|
{
|
|
//Find the requested Connection Point. This AddRef's the return pointer.
|
|
hr = pConnPointContainer->FindConnectionPoint(IID_IMSAdminBaseSink, &pConnPoint);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "FindConnectionPoint failed. hr = %08x\n", hr));
|
|
}
|
|
|
|
pConnPointContainer->Release();
|
|
if (pConnPoint != NULL)
|
|
{
|
|
hr = pConnPoint->Unadvise(m_dwMDSinkCookie);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "UnAdvise App Config Change Notify failed. hr = %08x\n", hr));
|
|
}
|
|
hr = S_OK; // benign failure if Advise was not called (happens with unknown script lang)
|
|
pConnPoint->Release();
|
|
m_dwMDSinkCookie = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "QueryInterface failed. hr = %08x\n", hr));
|
|
}
|
|
m_pMetabase->Release();
|
|
m_pMetabase = NULL;
|
|
}
|
|
|
|
if (m_pMetabaseSink) {
|
|
m_pMetabaseSink->Release();
|
|
m_pMetabaseSink = NULL;
|
|
}
|
|
|
|
AspUndoRevertHack (&hCurrentUser);
|
|
|
|
//
|
|
// Always return S_OK. In the new world, the metabase may have gone away
|
|
// because of a WAS recycle. So, it can happen that even the QI's above will fail.
|
|
//
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnMgr::NotifyAllMBListeners
|
|
|
|
When the RPC thread calls the CMDAppConfigSink objects SinkNotify method. This method is called
|
|
which will iterate through all the applications and call each applications appconfig object to update thier
|
|
config information
|
|
|
|
Parameters:
|
|
dwMDNumElements - number of elements in the change list.
|
|
|
|
pcoChangeList - The list of changes
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::NotifyAllMBListeners( DWORD dwMDNumElements, MD_CHANGE_OBJECT_W __RPC_FAR pcoChangeList [ ] )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Lock();
|
|
|
|
CLinkElem *pLink = CHashTable::Head();
|
|
|
|
while (pLink)
|
|
{
|
|
CAppln *pAppln = static_cast<CAppln *>(pLink);
|
|
pLink = pLink->m_pNext;
|
|
|
|
if (pAppln->m_fDeleteInProgress)
|
|
continue;
|
|
|
|
hr = pAppln->QueryAppConfig()->SinkNotify(dwMDNumElements, pcoChangeList);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
UnLock();
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CApplnMgr::AddAppln
|
|
|
|
Adds a CAppln element to link list / hash table.
|
|
User has to check if Appln already exists before calling this.
|
|
Critical sectioning is in CHitObj::BrowserRequestInit().
|
|
|
|
Parameters:
|
|
char *pszApplnKey Application metabase key
|
|
char *pszApplnPath Application directory path
|
|
CIsapiReqInfo *pIReq
|
|
|
|
CAppln **ppAppln [out] Application created
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::AddAppln
|
|
(
|
|
TCHAR *pszApplnKey,
|
|
TCHAR *pszApplnPath,
|
|
CIsapiReqInfo *pIReq,
|
|
CAppln **ppAppln
|
|
)
|
|
{
|
|
*ppAppln = NULL; // return NULL if failed
|
|
|
|
// Create CAppln object
|
|
|
|
CAppln *pAppln = new CAppln;
|
|
|
|
if (!pAppln)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Init CAppln object
|
|
|
|
HRESULT hr;
|
|
|
|
hr = pAppln->Init
|
|
(
|
|
pszApplnKey,
|
|
pszApplnPath,
|
|
pIReq
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
pAppln->UnInit();
|
|
pAppln->Release();
|
|
return hr;
|
|
}
|
|
|
|
// Add to hash table
|
|
|
|
if (!CHashTable::AddElem(pAppln))
|
|
{
|
|
pAppln->UnInit();
|
|
pAppln->Release();
|
|
return E_FAIL;
|
|
}
|
|
|
|
*ppAppln = pAppln;
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnMgr::FindAppln
|
|
|
|
Finds CAppln in hash table
|
|
Critical sectioning must be done outside
|
|
|
|
Parameters:
|
|
char *pszApplnKey Application metabase key
|
|
CAppln **ppAppln [out] Application found
|
|
|
|
Returns:
|
|
S_OK if found
|
|
S_FALSE if not found
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::FindAppln
|
|
(
|
|
TCHAR *pszApplnKey,
|
|
CAppln **ppAppln
|
|
)
|
|
{
|
|
CLinkElem *pLinkElem = CHashTable::FindElem
|
|
(
|
|
pszApplnKey,
|
|
_tcslen(pszApplnKey)*sizeof(TCHAR)
|
|
);
|
|
|
|
if (!pLinkElem)
|
|
{
|
|
*ppAppln = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
*ppAppln = static_cast<CAppln *>(pLinkElem);
|
|
return S_OK;
|
|
}
|
|
/*===================================================================
|
|
CApplnMgr::AddEngine
|
|
|
|
When a change notification occurs for a file being debugged,
|
|
we need to delete its associated scripting engine. The naive
|
|
approach of Releasing the engine during notification won't work
|
|
because the engine is on the wrong thread. Instead of marshaling
|
|
to the thread (which raises possibilities of deadlock or starving
|
|
the notification thread if debugging is happening on the debug
|
|
thread), the engines are added to a queue in the application.
|
|
When a request is serviced for debugging (which is now in the
|
|
correct thread context), the application object first flushes
|
|
this list by releasing the engines
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::AddEngine(CActiveScriptEngine *pEngine)
|
|
{
|
|
CScriptEngineCleanupElem *pScriptElem = new CScriptEngineCleanupElem(pEngine);
|
|
if (pScriptElem == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
pScriptElem->AppendTo(m_listEngineCleanup);
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnMgr::CleanupEngines()
|
|
|
|
Call Release all engine cleanup list.
|
|
===================================================================*/
|
|
void CApplnMgr::CleanupEngines()
|
|
{
|
|
while (! m_listEngineCleanup.FIsEmpty())
|
|
delete m_listEngineCleanup.PNext();
|
|
}
|
|
|
|
|
|
/*===================================================================
|
|
CApplnMgr::DeleteApplicationIfExpired
|
|
|
|
Removes CAppln object if exprired
|
|
Critical sectioning must be done outside
|
|
|
|
Parameters:
|
|
CAppln *pAppln application to delete
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::DeleteApplicationIfExpired
|
|
(
|
|
CAppln *pAppln
|
|
)
|
|
{
|
|
if (!pAppln->m_fGlobalChanged)
|
|
return S_OK;
|
|
|
|
if (pAppln->m_cSessions || pAppln->m_cRequests)
|
|
return S_OK;
|
|
|
|
if (pAppln->m_fDeleteInProgress)
|
|
return S_OK;
|
|
|
|
pAppln->m_fDeleteInProgress = TRUE;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// Queue it up for deletion
|
|
CHitObj *pHitObj = new CHitObj;
|
|
if (!pHitObj)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pHitObj->ApplicationCleanupInit(pAppln);
|
|
|
|
// Ask Viper to queue this request
|
|
hr = pHitObj->PostViperAsyncCall();
|
|
}
|
|
|
|
// cleanup
|
|
if (FAILED(hr) && pHitObj) {
|
|
pAppln->UnInit();
|
|
pAppln->Release();
|
|
delete pHitObj;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnMgr::DeleteAllApplications
|
|
|
|
Removes CAppln objects from the application manager link list
|
|
and hash table.
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::DeleteAllApplications()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Lock();
|
|
|
|
CLinkElem *pLink = CHashTable::Head();
|
|
CHashTable::ReInit();
|
|
|
|
while (pLink)
|
|
{
|
|
CAppln *pAppln = static_cast<CAppln *>(pLink);
|
|
pLink = pLink->m_pNext;
|
|
|
|
if (pAppln->m_fDeleteInProgress)
|
|
continue;
|
|
|
|
pAppln->m_fDeleteInProgress = TRUE;
|
|
|
|
|
|
// Queue it up for deletion
|
|
CHitObj *pHitObj = new CHitObj;
|
|
if (!pHitObj)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
// If NT, Unregister for notifications
|
|
while ((pAppln->m_rgpvDME).Count())
|
|
{
|
|
static_cast<CDirMonitorEntry *>(pAppln->m_rgpvDME[0])->Release();
|
|
(pAppln->m_rgpvDME).Remove(0);
|
|
}
|
|
pAppln->m_rgpvDME.Clear();
|
|
|
|
|
|
pHitObj->ApplicationCleanupInit(pAppln);
|
|
|
|
// Ask Viper to queue this request
|
|
hr = pHitObj->PostViperAsyncCall();
|
|
if (FAILED(hr))
|
|
{
|
|
pAppln->UnInit();
|
|
pAppln->Release();
|
|
|
|
delete pHitObj;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
UnLock();
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnMgr::RestartAllChagnedApplications
|
|
|
|
Restarts CAppln objects from the application manager link list
|
|
We walk the list recording which applications are dependent
|
|
on files that have changed since they were compiled. Once we
|
|
have the list, we restart each of the applications.
|
|
|
|
This is a fall back when we may have missed a change notification,
|
|
for instance when we had insufficient buffer to record all the changes
|
|
that occured.
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnMgr::RestartApplications(BOOL fRestartAllApplications)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Lock();
|
|
|
|
CLinkElem *pLink = CHashTable::Head();
|
|
|
|
// Find out which applications need restarting
|
|
|
|
while (pLink)
|
|
{
|
|
CAppln *pAppln = static_cast<CAppln *>(pLink);
|
|
pLink = pLink->m_pNext;
|
|
if (!pAppln->FTombstone() && (fRestartAllApplications || (pAppln->m_pGlobalTemplate != NULL && pAppln->m_pGlobalTemplate->FTemplateObsolete())))
|
|
{
|
|
pAppln->Restart();
|
|
}
|
|
}
|
|
|
|
UnLock();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
C A p p l n C l e a n u p M g r
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CApplnMgr::CApplnCleanupMgr
|
|
|
|
Application Cleanup Manager constructor.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
CApplnCleanupMgr::CApplnCleanupMgr()
|
|
: m_fInited(FALSE),
|
|
m_fCriticalSectionInited(FALSE),
|
|
m_hThreadAlive(NULL),
|
|
m_cCleanupThreads(0),
|
|
m_hAppToCleanup(INVALID_HANDLE_VALUE)
|
|
{
|
|
m_List.m_pPrev = &m_List;
|
|
m_List.m_pNext = &m_List;
|
|
|
|
// Clean out the thread count
|
|
ZeroMemory(&m_hCleanupThreads, sizeof (m_hCleanupThreads));
|
|
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnCleanupMgr::~CApplnCleanupMgr
|
|
|
|
Application Cleanup Manager destructor.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
CApplnCleanupMgr::~CApplnCleanupMgr()
|
|
{
|
|
// safety measure to prevent UnInit() from calling Sleep()
|
|
UnInit();
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnCleanupMgr::Init
|
|
|
|
Initializes the Appln Cleanup Manager.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
E_OUTOFMEMORY Out of memory
|
|
===================================================================*/
|
|
HRESULT CApplnCleanupMgr::Init( void )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
Assert(!m_fInited);
|
|
|
|
// Create delete app event
|
|
|
|
m_hAppToCleanup = IIS_CREATE_EVENT(
|
|
"CApplnCleanupMgr::m_hAppToCleanup",
|
|
this,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
|
|
if (!m_hAppToCleanup)
|
|
return E_FAIL;
|
|
|
|
// Init critical section
|
|
|
|
ErrInitCriticalSection(&m_csLock, hr);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
m_fCriticalSectionInited = TRUE;
|
|
|
|
m_hThreadAlive = CreateThread(NULL, 0, CApplnCleanupMgr::ApplnCleanupThread, NULL , CREATE_SUSPENDED , NULL);
|
|
|
|
if (!m_hThreadAlive)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
m_fInited = TRUE;
|
|
|
|
if (ResumeThread (m_hThreadAlive) == (DWORD) -1)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnCleanupMgr::UnInit
|
|
|
|
UnInitializes the Appln Cleanup Manager.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
===================================================================*/
|
|
HRESULT CApplnCleanupMgr::UnInit( void )
|
|
{
|
|
HRESULT hr = S_OK;
|
|
// set fInited to FALSE here so that the cleanup thread
|
|
// can safely detect that we're shutting down.
|
|
|
|
m_fInited = FALSE;
|
|
|
|
if (m_hAppToCleanup != INVALID_HANDLE_VALUE) {
|
|
// Set the event one last time so that the thread
|
|
// wakes up, sees that shutdown is occurring and
|
|
// exits.
|
|
SetEvent(m_hAppToCleanup);
|
|
CloseHandle(m_hAppToCleanup);
|
|
m_hAppToCleanup = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// we'll wait for the thread to finish its work
|
|
if (m_hThreadAlive)
|
|
{
|
|
if (WaitForSingleObject(m_hThreadAlive, INFINITE) == WAIT_FAILED)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
CloseHandle (m_hThreadAlive);
|
|
|
|
m_hThreadAlive = NULL;
|
|
}
|
|
|
|
if (m_fCriticalSectionInited) {
|
|
DeleteCriticalSection(&m_csLock);
|
|
m_fCriticalSectionInited = FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnCleanupMgr::AddAppln
|
|
|
|
Adds a CAppln element to link list / hash table.
|
|
|
|
|
|
Parameters:
|
|
CAppln *pAppln Application to cleanup
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CApplnCleanupMgr::AddAppln
|
|
(
|
|
CAppln *pAppln
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
#if UNICODE
|
|
DBGPRINTF((DBG_CONTEXT, "[CApplnCleanupMgr] Adding App (%S)\n",pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)));
|
|
#else
|
|
DBGPRINTF((DBG_CONTEXT, "[CApplnCleanupMgr] Adding App (%s)\n",pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)));
|
|
#endif
|
|
|
|
Lock();
|
|
|
|
AddElem(pAppln);
|
|
|
|
UnLock();
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
Wakeup();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnCleanupMgr::ApplnCleanupThread
|
|
|
|
The thread that does the work to cleanup applications
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
DWORD __stdcall CApplnCleanupMgr::ApplnCleanupThread(VOID *pArg)
|
|
{
|
|
g_ApplnCleanupMgr.ApplnCleanupDoWork();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnCleanupMgr::ApplnCleanupDoWork
|
|
|
|
Proc that actually does the work
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
void CApplnCleanupMgr::ApplnCleanupDoWork()
|
|
{
|
|
CAppln *pAppln = NULL;
|
|
DWORD nThreadIndex = 0;
|
|
HANDLE hThread = NULL;
|
|
|
|
// this thread will be in a constant loop checking for work
|
|
|
|
while(1)
|
|
{
|
|
if (m_fInited)
|
|
WaitForSingleObject(m_hAppToCleanup, INFINITE);
|
|
|
|
if (Head() == NULL && m_fInited)
|
|
continue;
|
|
|
|
// hold the lock while in this loop. This shouldn't hold it
|
|
// for long as there are no long running operations in this loop.
|
|
// If a thread can't be created and the application cleanup
|
|
// must occur on this thread, then the lock is released.
|
|
//
|
|
// Rely on Head() returning NULL to break out of this loop in case of shutdown.
|
|
// In the shutdown case we will not wait
|
|
//
|
|
|
|
Lock();
|
|
|
|
pAppln = static_cast<CAppln *>(Head());
|
|
|
|
if (!pAppln && !m_fInited)
|
|
{
|
|
UnLock();
|
|
break;
|
|
}
|
|
|
|
RemoveElem(Head());
|
|
|
|
UnLock();
|
|
|
|
//
|
|
// This loop will execute while there is work and there aren't too many
|
|
// threads active or we're in shutdown. The theory here is that in the
|
|
// non-shutdown case, let's not spin up more than 4 threads at a time to
|
|
// do the cleanup. If in shutdown, create as many threads as necessary.
|
|
// if no threads started yet, use the first slot
|
|
//
|
|
if ((m_cCleanupThreads < 4) || (IsShutDownInProgress() && (m_cCleanupThreads < MAX_CLEANUP_THREADS)))
|
|
{
|
|
//
|
|
// just get the next index
|
|
//
|
|
nThreadIndex = m_cCleanupThreads;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// At full capacity. So wait till one of the threads returns / terminated
|
|
//
|
|
nThreadIndex = WaitForMultipleObjects( m_cCleanupThreads,
|
|
m_hCleanupThreads,
|
|
FALSE, // wait for any event
|
|
INFINITE); // return immediately
|
|
Assert(nThreadIndex != WAIT_TIMEOUT);
|
|
}
|
|
|
|
DBGPRINTF((DBG_CONTEXT, "[CApplnCleanupMgr] Cleanup Thread working on (%S)\n",pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)));
|
|
|
|
hThread = CreateThread(NULL, 0, CAppln::ApplnCleanupProc, pAppln, 0, NULL);
|
|
|
|
// failed to create a thread to do the work. Cleanup the app right here.
|
|
// Unlock the cleanup manager while we are doing this.
|
|
|
|
if (hThread == NULL)
|
|
{
|
|
pAppln->ApplnCleanupProc(pAppln);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// close the previous handle if we are reusing an entry
|
|
//
|
|
if (nThreadIndex < m_cCleanupThreads)
|
|
{
|
|
//
|
|
// we are reusing a slot from a terminated thread
|
|
//
|
|
CloseHandle(m_hCleanupThreads[ nThreadIndex ]);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we are using a new slot
|
|
//
|
|
Assert(nThreadIndex == m_cCleanupThreads);
|
|
|
|
m_cCleanupThreads++;
|
|
}
|
|
|
|
m_hCleanupThreads[ nThreadIndex ] = hThread;
|
|
}
|
|
|
|
}
|
|
|
|
if (m_cCleanupThreads)
|
|
{
|
|
WaitForMultipleObjects(
|
|
m_cCleanupThreads,
|
|
m_hCleanupThreads,
|
|
TRUE, // wait for ALL event
|
|
INFINITE); // wait for as long as it takes.
|
|
|
|
while( m_cCleanupThreads )
|
|
{
|
|
CloseHandle(m_hCleanupThreads[ --m_cCleanupThreads ]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define WSTR_NULL L"\0"
|
|
|
|
/*===================================================================
|
|
C A p p l n I t e r a t o r
|
|
===================================================================*/
|
|
|
|
/*===================================================================
|
|
CApplnIterator::CApplnIterator
|
|
|
|
Constructor
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
|
|
CApplnIterator::CApplnIterator()
|
|
: m_pApplnMgr(NULL), m_pCurr(NULL), m_fEnded(FALSE)
|
|
{
|
|
}
|
|
|
|
/*===================================================================
|
|
CApplnIterator::~CApplnIterator
|
|
|
|
Destructor.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
NONE
|
|
===================================================================*/
|
|
|
|
CApplnIterator::~CApplnIterator( void )
|
|
{
|
|
if (m_pApplnMgr != NULL)
|
|
Stop();
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnIterator::Start
|
|
|
|
Starts iterator on the Appln Manager.
|
|
|
|
Parameters:
|
|
CApplnMgr * pApplnMgr Appln Manager
|
|
(if NULL g_ApplnManager is assumed)
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
===================================================================*/
|
|
|
|
HRESULT CApplnIterator::Start
|
|
(
|
|
CApplnMgr *pApplnMgr
|
|
)
|
|
{
|
|
m_pApplnMgr = pApplnMgr ? m_pApplnMgr : &g_ApplnMgr;
|
|
|
|
m_pApplnMgr->Lock();
|
|
|
|
m_pCurr = NULL;
|
|
m_fEnded = FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnIterator::Stop
|
|
|
|
Stops iterator on the Appln Manager.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
S_OK Success
|
|
E_FAIL Failure
|
|
===================================================================*/
|
|
|
|
HRESULT CApplnIterator::Stop()
|
|
{
|
|
if (m_pApplnMgr)
|
|
{
|
|
m_pApplnMgr->UnLock();
|
|
m_pApplnMgr = NULL;
|
|
}
|
|
|
|
m_pCurr = NULL;
|
|
m_fEnded = FALSE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
HRESULT CApplnIterator::Next
|
|
|
|
Iterates to the next Appln.
|
|
|
|
Parameters:
|
|
NONE
|
|
|
|
Returns:
|
|
Appln * or NULL
|
|
===================================================================*/
|
|
|
|
CAppln *CApplnIterator::Next( void )
|
|
{
|
|
if (m_pApplnMgr == NULL || m_fEnded)
|
|
return NULL; // didn't start or already ended
|
|
|
|
CLinkElem *pT = m_pCurr ? m_pCurr->m_pNext : m_pApplnMgr->Head();
|
|
if (pT)
|
|
{
|
|
m_pCurr = static_cast<CAppln *>(pT);
|
|
return m_pCurr;
|
|
}
|
|
|
|
m_fEnded = TRUE;
|
|
return NULL;
|
|
}
|