/*=================================================================== 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; LONG g_nAppRestartThreads = 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), m_pServicesConfig(NULL) { // COM stuff m_ISuppErrImp.Init(static_cast(this), static_cast(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; } // 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: // // // // // // 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; } } } // 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::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; } 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 (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() && 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; } } 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; } /*=================================================================== 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(pLinkElem) == this); // Unlock g_ApplnMgr.UnLock(); // 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()) { Lock(); // Place critical Section around access to m_rgpvDME while ((m_rgpvDME).Count()) { static_cast(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 (!IsShutDownInProgress() && (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); // Decrement the count of restart threads InterlockedDecrement(&g_nAppRestartThreads); // tell the cleanup manager to check for more work g_ApplnCleanupMgr.Wakeup(); 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 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(m_rgpvFileAppln[0])->RemoveApplication(this); m_rgpvFileAppln.Remove(0); i--; } g_FileAppMap.UnLock(); m_rgpvFileAppln.Clear(); Lock(); // Protecting m_rqpvDME with a critical section 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) { /* * 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); } if (m_pServicesConfig) m_pServicesConfig->Release(); // 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(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::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(this); } else if (IID_ISupportErrorInfo == riid) { *ppv = &m_ISuppErrImp; } else if (IID_IMarshal == riid) { *ppv = static_cast(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(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(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 fPrevSxsEnabled = m_pAppConfig->fSxsEnabled(); BOOL fPrevUsePartition = m_pAppConfig->fUsePartition(); BOOL fPrevUseTracker = m_pAppConfig->fTrackerEnabled(); 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(); 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) || 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(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) { } /*=================================================================== 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 ) { 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(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(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(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; Lock(); CLinkElem *pLink = CHashTable::Head(); // Find out which applications need restarting while (pLink) { CAppln *pAppln = static_cast(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_fThreadAlive(FALSE), m_hAppToCleanup(INVALID_HANDLE_VALUE) { m_List.m_pPrev = &m_List; m_List.m_pNext = &m_List; } /*=================================================================== 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 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_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(); 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() { m_fThreadAlive = TRUE; // this thread will be in a constant loop checking for work while(1) { // 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. Lock(); // 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. while(Head() && ((g_nAppRestartThreads < 4) || (IsShutDownInProgress()))) { CAppln *pAppln = static_cast(Head()); RemoveElem(Head()); #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 HANDLE 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) { UnLock(); pAppln->ApplnCleanupProc(this); Lock(); } else { InterlockedIncrement(&g_nAppRestartThreads); CloseHandle(hThread); } } UnLock(); WaitForSingleObject(m_hAppToCleanup, INFINITE); // check the work queue under lock here to prevent the // 'if' condition below from kicking in and preventing // the remaining cleanup during shutdown Lock(); if (Head()) { UnLock(); continue; } UnLock(); // check to see if shutdown is occurring... if (m_fInited == FALSE) { Assert(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(pT); return m_pCurr; } m_fEnded = TRUE; return NULL; }