windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/cmp/asp51/applmgr.cpp
2020-09-26 16:20:57 +08:00

2920 lines
71 KiB
C++

/*===================================================================
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"
PTRACE_LOG CAppln::gm_pTraceLog = NULL;
CApplnMgr g_ApplnMgr;
CApplnCleanupMgr g_ApplnCleanupMgr;
DWORD g_nApplications = 0;
DWORD g_nApplicationsRestarting = 0;
DWORD g_nApplicationsRestarted = 0;
#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()
{
if (InterlockedDecrement((LPLONG)&m_cRefs) > 0)
return m_cRefs;
delete this;
return 0;
}
/*===================================================================
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
)
{
*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);
}
VariantClear(&varKeyCopy);
return S_OK;
}
/*===================================================================
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_fNotificationAdded(FALSE),
m_fUseImpersonationHandle(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_dwLockThreadID(INVALID_THREADID),
m_cLockRefCount(0),
m_hUserImpersonation(NULL),
m_pdispGlobTypeLibWrapper(NULL)
{
// COM stuff
m_ISuppErrImp.Init(static_cast<IApplicationObject *>(this),
static_cast<IApplicationObject *>(this),
IID_IApplicationObject);
CDispatch::Init(IID_IApplicationObject);
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
}
/*===================================================================
HRESULT CAppln::Init
Initialize object
Parameters:
char *pszApplnKey application's metabase key
char *pszApplnPath application's directory path
CIsapiReqInfo *pIReq Isapi Req Info
HANDLE hUserImpersonation impersonation handle
Returns:
S_OK Success
E_FAIL Failure
E_OUTOFMEMORY Out of memory failure
===================================================================*/
HRESULT CAppln::Init
(
TCHAR *pszApplnKey,
TCHAR *pszApplnPath,
CIsapiReqInfo *pIReq,
HANDLE hUserImpersonation
)
{
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;
// 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;
}
// 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[256];
if (FAILED(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);
// Initialize link element with key
Assert(pHashKey);
Assert(dwHashKeyLength);
if (FAILED(CLinkElem::Init(pHashKey, dwHashKeyLength)))
goto LCleanupOOM;
// Setup impersonation
m_fNotificationAdded = FALSE;
if (FIsWinNT() && pszApplnPath &&
pszApplnPath[0] ==_T('\\') && pszApplnPath[1] == _T('\\'))
{
m_fUseImpersonationHandle = DuplicateToken
(
hUserImpersonation,
SecurityImpersonation,
&m_hUserImpersonation
);
}
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);
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;
}
// 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);
// 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;
}
}
}
// For Win95: all requests run on the same thread
if (!FIsWinNT())
{
if (FAILED(hr = BindToActivity()))
{
DBGWARN((DBG_CONTEXT, "New Application Failed: Could not bind application to Win95 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::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
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;
}
// Indicate to Delete All Sessions
m_fGlobalChanged = TRUE;
// 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
if (FIsWinNT())
{
while ((m_rgpvDME).Count())
{
static_cast<CDirMonitorEntry *>(m_rgpvDME[0])->Release();
(m_rgpvDME).Remove(0);
}
m_rgpvDME.Clear();
}
// 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);
// Unlock
g_ApplnMgr.UnLock();
// add this application to the CleanupManager...
#if 0
g_ApplnCleanupMgr.AddAppln(this);
#else
ApplnCleanupProc();
#endif
return S_OK;
}
/*===================================================================
CAppln::ApplnCleanupProc
Called by the g_ApplnCleanupMgr thread to complete cleanup
===================================================================*/
HRESULT CAppln::ApplnCleanupProc()
{
// Let the requests to drain while trying to delete sessions
while (!IsShutDownInProgress() && (m_cRequests || m_cSessions))
{
if (m_cSessions)
m_pSessionMgr->DeleteAllSessions(FALSE);
if (m_cSessions || m_cRequests)
Sleep(200);
}
// Re-lock
g_ApplnMgr.Lock();
g_ApplnMgr.DeleteApplicationIfExpired(this);
// Decrement the count of restarting applications
InterlockedDecrement((LPLONG)&g_nApplicationsRestarting);
g_ApplnMgr.UnLock();
Release();
return S_OK;
}
/*===================================================================
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
if (FIsWinNT())
{
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();
m_rgpvDME.Clear();
// 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)
{
/*
* BUG 89144: Uninit AppConfig but do it from the MTA
* When AppConfig is inited, it is done on a WAM thread. WAM
* threads are MTA threads. At that time we register an event
* sink to get Metabase change notifications. Now, during shutdown,
* we are running on an ASP worker thread, which is an STA thread.
* That means we will get an RPC_E_WRONGTHREAD error shutting down. The
* fix is to make the uninit call happen on an MTA thread.
*/
HRESULT hr;
HRESULT AppConfigUnInit(void *pV1, void *pV2);
HANDLE hCompletionEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
hr = CallMTACallback
(
AppConfigUnInit,
m_pAppConfig,
hCompletionEvent
);
Assert(SUCCEEDED(hr));
WaitForSingleObject(hCompletionEvent, INFINITE);
CloseHandle(hCompletionEvent);
delete m_pAppConfig;
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 (FIsWinNT() && m_fUseImpersonationHandle)
{
CloseHandle(m_hUserImpersonation);
m_hUserImpersonation = NULL;
}
if (m_fInited)
{
DeleteCriticalSection(&m_csInternalLock);
DeleteCriticalSection(&m_csApplnLock);
}
// 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(QueryAppConfig()->fExecuteInMTA());
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
if (FAILED(hr = m_rgpvDME.Append(pDirMonitorEntry)))
{
pDirMonitorEntry->Release();
}
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::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)
{
*ppv = static_cast<IMarshal *>(this);
}
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;
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_fInited);
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?
{
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 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();
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) ||
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 cDMEs = m_rgpvDME.Count();
int i;
for (i=0; i < cDMEs; i++) {
CASPDirMonitorEntry *pDME = static_cast<CASPDirMonitorEntry *>(m_rgpvDME[i]);
if (pDME == NULL)
break;
if (pDME->FPathMonitored(pszPath))
return pDME;
}
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_hDeleteApplnEvent(INVALID_HANDLE_VALUE)
{
}
/*===================================================================
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);
// Create delete app event
m_hDeleteApplnEvent = IIS_CREATE_EVENT(
"CApplnMgr::m_hDeleteApplnEvent",
this,
FALSE,
FALSE
);
if (!m_hDeleteApplnEvent)
return E_FAIL;
// 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 )
{
if (m_hDeleteApplnEvent != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hDeleteApplnEvent);
m_hDeleteApplnEvent = INVALID_HANDLE_VALUE;
}
if (m_fHashTableInited)
{
CHashTable::UnInit();
m_fHashTableInited = FALSE;
}
if (m_fCriticalSectionInited)
{
DeleteCriticalSection(&m_csLock);
m_fCriticalSectionInited = FALSE;
}
m_fInited = FALSE;
return g_ApplnCleanupMgr.UnInit();
}
/*===================================================================
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
HANDLE hUserImpersonation impersonation handle
CAppln **ppAppln [out] Application created
Returns:
HRESULT
===================================================================*/
HRESULT CApplnMgr::AddAppln
(
TCHAR *pszApplnKey,
TCHAR *pszApplnPath,
CIsapiReqInfo *pIReq,
HANDLE hUserImpersonation,
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,
hUserImpersonation
);
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)
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
if (FIsWinNT())
{
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))
{
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;
CPtrArray *prgpapplnRestartList = new CPtrArray();
if (prgpapplnRestartList == NULL)
return E_OUTOFMEMORY;
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->AddRef();
prgpapplnRestartList->Append(pAppln);
}
}
UnLock();
// Restart the applicaitons
// Don't want to CloseHandle on this, _beginthread implicitly calls _endthread
// which closes the handle implicitly.
uintptr_t hThread = _beginthread(RestartAppsThreadProc, 0, prgpapplnRestartList);
if (hThread == -1)
{
delete prgpapplnRestartList;
return E_OUTOFMEMORY;
}
return hr;
}
void __cdecl RestartAppsThreadProc(VOID *arg)
{
CPtrArray *prgpapplnRestartList = (CPtrArray *)arg;
for (int i = 0; i < prgpapplnRestartList->Count(); i++)
{
CAppln *pAppln = (CAppln *)((*prgpapplnRestartList)[i]);
pAppln->Restart();
pAppln->Release();
}
delete prgpapplnRestartList;
return;
}
/*===================================================================
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_fHashTableInited(FALSE),
m_fCriticalSectionInited(FALSE),
m_fThreadAlive(FALSE),
m_hAppToCleanup(INVALID_HANDLE_VALUE)
{
}
/*===================================================================
CApplnCleanupMgr::~CApplnCleanupMgr
Application Cleanup Manager destructor.
Parameters:
NONE
Returns:
NONE
===================================================================*/
CApplnCleanupMgr::~CApplnCleanupMgr()
{
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 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;
HANDLE hThread = CreateThread(NULL, 0, CApplnCleanupMgr::ApplnCleanupThread, 0, 0, NULL);
if (!hThread) {
return E_FAIL;
}
CloseHandle(hThread);
m_fInited = TRUE;
return S_OK;
}
/*===================================================================
HRESULT CApplnCleanupMgr::UnInit
UnInitializes the Appln Cleanup Manager.
Parameters:
NONE
Returns:
S_OK Success
E_FAIL Failure
===================================================================*/
HRESULT CApplnCleanupMgr::UnInit( void )
{
// 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
while(m_fThreadAlive) {
Sleep(200);
}
if (m_fHashTableInited) {
CHashTable::UnInit();
m_fHashTableInited = FALSE;
}
if (m_fCriticalSectionInited) {
DeleteCriticalSection(&m_csLock);
m_fCriticalSectionInited = FALSE;
}
return S_OK;
}
/*===================================================================
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();
// Add to hash table
if (!CHashTable::AddElem(pAppln)) {
hr = E_FAIL;
}
UnLock();
if (SUCCEEDED(hr)) {
SetEvent(m_hAppToCleanup);
}
return hr;
}
/*===================================================================
CApplnCleanupMgr::ApplnCleanupProc
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()
{
m_fThreadAlive = TRUE;
while(1) {
Lock();
CLinkElem *pLink = CHashTable::Head();
CHashTable::ReInit();
UnLock();
while(pLink) {
CAppln *pAppln = static_cast<CAppln *>(pLink);
#if UNICODE
DBGPRINTF((DBG_CONTEXT, "[CApplnCleanupMgr] Cleanup Thread working on (%S)\n",pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)));
#else
DBGPRINTF((DBG_CONTEXT, "[CApplnCleanupMgr] Cleanup Thread working on (%s)\n",pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)));
#endif
pLink = pLink->m_pNext;
pAppln->ApplnCleanupProc();
}
// no need waiting if there is more work to do...
if (CHashTable::Head() != NULL) {
continue;
}
WaitForSingleObject(m_hAppToCleanup, INFINITE);
// check to see if shutdown is occurring...
if (m_fInited == FALSE) {
Assert(CHashTable::Head() == NULL);
m_fThreadAlive = FALSE;
return;
}
}
return;
}
#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;
}