You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1521 lines
46 KiB
1521 lines
46 KiB
/*===================================================================
|
|
Microsoft Denali
|
|
|
|
Microsoft Confidential.
|
|
Copyright 1996 Microsoft Corporation. All Rights Reserved.
|
|
|
|
Component: Executor
|
|
|
|
Owner: DGottner
|
|
|
|
File: executor.cpp
|
|
|
|
This file contains the executor, whose job is to co-ordinate the
|
|
execution of Denali scripts.
|
|
===================================================================*/
|
|
|
|
#include "denpre.h"
|
|
#pragma hdrstop
|
|
|
|
#include "exec.h"
|
|
#include "response.h"
|
|
#include "request.h"
|
|
#include "perfdata.h"
|
|
#include "memchk.h"
|
|
#include <iismsg.h>
|
|
|
|
|
|
// Local declarations
|
|
HRESULT ExecuteGlobal(CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo);
|
|
HRESULT ExecuteRequest(CTemplate *pTemplate, CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo);
|
|
HRESULT ReInitIntrinsics(CHitObj *pHitObj, const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo, BOOL fPostGlobal);
|
|
HRESULT AllocAndLoadEngines(CHitObj *pHitObj, CTemplate *pTemplate, ActiveEngineInfo *pEngineInfo,
|
|
CScriptingNamespace *pScriptingNamespace, BOOL fGlobalAsa);
|
|
VOID DeAllocAndFreeEngines(ActiveEngineInfo *pEngineInfo, CAppln *pAppln);
|
|
CScriptEngine *GetScriptEngine(int iScriptEngine, void *pvData);
|
|
HRESULT CallScriptFunctionOfEngine(ActiveEngineInfo &engineInfo, short iScriptBlock, wchar_t *strFunction, CASPObjectContext *pASPObjectContext = NULL);
|
|
HRESULT CallScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction);
|
|
HRESULT TestScriptFunction(ActiveEngineInfo &engineInfo, wchar_t *strFunction);
|
|
|
|
/*===================================================================
|
|
Execute
|
|
|
|
Execute a request:
|
|
First determine if Global needs to be called
|
|
then invoke actual requested template
|
|
|
|
Parameters:
|
|
pTemplate - pointer to loaded template (could be NULL)
|
|
pHitObj - pointer to the hit object
|
|
intrinsics - pointers to the intrinsic IUnknown pointers.
|
|
fChild - flag: TRUE when child request (Server.Execute())
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT Execute
|
|
(
|
|
CTemplate *pTemplate,
|
|
CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
BOOL fChild
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ActiveEngineInfo engineInfo;
|
|
BOOL fRanGlobal = FALSE;
|
|
|
|
// The hit obj must be valid
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
|
|
// Check for valid Session codepage. We do it here, rather than in CSession::Init in
|
|
// order to avoid generic "New Session Failed" message.
|
|
if (pHitObj->GetCodePage() != CP_ACP && !IsValidCodePage(pHitObj->GetCodePage()))
|
|
{
|
|
HandleErrorMissingFilename(IDE_BAD_CODEPAGE_IN_MB, pHitObj);
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Give the engine list to the hitobject
|
|
pHitObj->SetActiveEngineInfo(&engineInfo);
|
|
|
|
/*
|
|
* If there is a Global.ASA, call it
|
|
*/
|
|
if (pHitObj->GlobalAspPath() && !fChild)
|
|
{
|
|
// Clear out the engine info
|
|
engineInfo.cEngines = 0;
|
|
engineInfo.cActiveEngines = 0;
|
|
engineInfo.rgActiveEngines = NULL;
|
|
|
|
// Init the intrinsics
|
|
hr = ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, /* fPostGlobal*/ FALSE);
|
|
if (FAILED(hr))
|
|
return(hr);
|
|
|
|
hr = ExecuteGlobal(pHitObj, intrinsics, &engineInfo);
|
|
|
|
if (intrinsics.PResponse() && intrinsics.PResponse()->FResponseAborted())
|
|
{
|
|
hr = S_OK;
|
|
goto LExit;
|
|
}
|
|
|
|
if (E_SOURCE_FILE_IS_EMPTY == hr)
|
|
// bug 977: silently ignore empty global.asa file
|
|
hr = S_OK;
|
|
else if (FAILED(hr))
|
|
{
|
|
// Bug 481: If global.asa fails due to Response.End (or Response.Redirect),
|
|
// then halt execution of the calling script. If the
|
|
// script fails due to Response.End, then return OK status
|
|
//
|
|
if (hr == DISP_E_EXCEPTION)
|
|
hr = S_OK;
|
|
|
|
// In any case, blow out of here
|
|
goto LExit;
|
|
}
|
|
|
|
// Running Global.asa added the scripting namespace to the hitobj. This will cause us problems
|
|
// later, remove it.
|
|
pHitObj->RemoveScriptingNamespace();
|
|
|
|
fRanGlobal = TRUE;
|
|
}
|
|
|
|
/*
|
|
* If this is not a browser request, then we are done
|
|
* For non-browser requests, we do want to run Global.asa (if any), but there is no real template to run.
|
|
*/
|
|
if (!pHitObj->FIsBrowserRequest())
|
|
{
|
|
hr = S_OK;
|
|
goto LExit;
|
|
}
|
|
|
|
// Clear out (or re-clear out) the engine info
|
|
engineInfo.cEngines = 0;
|
|
engineInfo.cActiveEngines = 0;
|
|
engineInfo.rgActiveEngines = NULL;
|
|
|
|
// Init or Re-Init the intrinsics
|
|
ReInitIntrinsics(pHitObj, intrinsics, &engineInfo, fRanGlobal || fChild);
|
|
|
|
if (!fChild)
|
|
{
|
|
// For non-child requests hand new Template to Response object
|
|
// (for child requests already done)
|
|
intrinsics.PResponse()->ReInitTemplate(pTemplate, pHitObj->PSzNewSessionCookie());
|
|
}
|
|
else
|
|
{
|
|
// For child requests hand new engine info to the response object
|
|
intrinsics.PResponse()->SwapScriptEngineInfo(&engineInfo);
|
|
}
|
|
|
|
// Run the main template
|
|
if (pTemplate->FScriptless() && !pHitObj->PAppln()->FDebuggable())
|
|
{
|
|
// special case scriptless pages
|
|
hr = intrinsics.PResponse()->WriteBlock(0);
|
|
}
|
|
else
|
|
{
|
|
hr = ExecuteRequest(pTemplate, pHitObj, intrinsics, &engineInfo);
|
|
}
|
|
|
|
LExit:
|
|
intrinsics.PResponse()->SwapScriptEngineInfo(NULL);
|
|
pHitObj->SetActiveEngineInfo(NULL);
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
ExecRequest
|
|
|
|
Execute a request for an actual template (not Global.asa)
|
|
|
|
execute a request by
|
|
- getting the script name
|
|
- loading the script into memory
|
|
- interpreting the opcodes
|
|
|
|
Parameters:
|
|
pTemplate - pointer to loaded template
|
|
pHitObj - pointer to the hit object
|
|
intrinsics - pointers to the intrinsic IUnknown pointers.
|
|
pEngineInfo - pointers to engine info
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT ExecuteRequest(CTemplate *pTemplate,
|
|
CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fAborted = FALSE;
|
|
BOOLB fDebuggerNotifiedOnStart = FALSE;
|
|
BOOL fServiceDomainEnterred = FALSE;
|
|
CASPObjectContext *pASPObjectContext = NULL;
|
|
CASPObjectContext *pPoppedASPObjectContext = NULL;
|
|
|
|
#ifndef PERF_DISABLE
|
|
BOOLB fPerfTransPending = FALSE;
|
|
#endif
|
|
|
|
// The template must be valid
|
|
Assert(pTemplate);
|
|
|
|
// The hit obj must be valid
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
|
|
// This function should never be called on a non-browser request
|
|
Assert(pHitObj->FIsBrowserRequest());
|
|
|
|
// Remember template's type library wrapper with the HitObj
|
|
if (pTemplate->PTypeLibWrapper())
|
|
pHitObj->SetTypeLibWrapper(pTemplate->PTypeLibWrapper());
|
|
|
|
if (pTemplate->FTransacted()) {
|
|
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_TRANSTOTAL();
|
|
g_PerfData.Incr_TRANSPENDING();
|
|
fPerfTransPending = TRUE;
|
|
#endif
|
|
pASPObjectContext = new CASPObjectContext();
|
|
|
|
if (!pASPObjectContext) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto LExit;
|
|
}
|
|
|
|
pPoppedASPObjectContext = pHitObj->SetASPObjectContext(pASPObjectContext);
|
|
}
|
|
|
|
if (pTemplate->PServicesConfig()) {
|
|
hr = CoEnterServiceDomain(pTemplate->PServicesConfig());
|
|
if (FAILED(hr)) {
|
|
goto LExit;
|
|
}
|
|
fServiceDomainEnterred = TRUE;
|
|
}
|
|
|
|
// load script engines
|
|
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */FALSE);
|
|
if (FAILED(hr)) {
|
|
pHitObj->SetCompilationFailed();
|
|
goto LExit;
|
|
}
|
|
|
|
// If debugging, notify debugger ONPAGESTART
|
|
// BUG 138773: Notify debugger AFTER scripts load
|
|
// (script must be in running state when AttachTo() is called because debugger may want a code context)
|
|
//
|
|
if (pHitObj->PAppln()->FDebuggable()) {
|
|
pTemplate->AttachTo(pHitObj->PAppln());
|
|
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
|
|
fDebuggerNotifiedOnStart = TRUE;
|
|
}
|
|
|
|
// bug 1009: if no script engines, do not attempt to do anything
|
|
if(0 == pTemplate->CountScriptEngines())
|
|
goto LExit;
|
|
|
|
// run the script by calling primary script engine's global code
|
|
hr = CallScriptFunctionOfEngine(*pEngineInfo, // engine-info
|
|
0, // primary script engine
|
|
NULL, // call the engine's global code
|
|
pHitObj->PASPObjectContext());
|
|
|
|
if (fServiceDomainEnterred) {
|
|
fServiceDomainEnterred = FALSE;
|
|
CoLeaveServiceDomain(static_cast<ITransactionStatus *>(pHitObj->PASPObjectContext()));
|
|
}
|
|
|
|
|
|
if (pTemplate->FTransacted()) {
|
|
fAborted = pHitObj->PASPObjectContext()->FAborted();
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
/*
|
|
* The cryptically named CONTEXT_E_OLDREF error in this case means that
|
|
* we are trying to run a transacted web page, but DTC isnt running.
|
|
* CONTEXT_E_TMNOTAVAILABLE means the same thing. God knows why
|
|
*/
|
|
if (hr == CONTEXT_E_OLDREF || hr == CONTEXT_E_TMNOTAVAILABLE) {
|
|
HandleErrorMissingFilename(IDE_EXECUTOR_DTC_NOT_RUNNING, pHitObj);
|
|
}
|
|
|
|
// Regardless of the error, exit
|
|
goto LExit;
|
|
}
|
|
|
|
/*
|
|
* If this is a transacted web page, then run either the OnTransactionCommit
|
|
* or OnTransactionAbort method in the script, if any.
|
|
*
|
|
* If the script writer did an explicit SetAbort, or a component run by the script
|
|
* did a SetAbort, then we run OnTransactionAbort, otherwise run OnTransactionCommit
|
|
*/
|
|
if (pTemplate->FTransacted()) {
|
|
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_TRANSPERSEC();
|
|
#endif
|
|
|
|
if (fAborted) {
|
|
|
|
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionAbort");
|
|
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_TRANSABORTED();
|
|
#endif
|
|
}
|
|
else {
|
|
hr = CallScriptFunction(*pEngineInfo, L"OnTransactionCommit");
|
|
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_TRANSCOMMIT();
|
|
#endif
|
|
}
|
|
|
|
// Ignore UNKNOWNNAME -- this means the author didnt write the method, which is fine
|
|
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
|
|
hr = S_OK;
|
|
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
}
|
|
|
|
LExit:
|
|
|
|
//
|
|
// Return the engine(s) to cache. Be sure to do this before leaving the services domain
|
|
// after we have deallocated the freed the engines or else the context will leak Variant objects
|
|
// as the reset script code will not make it to the various variant allocated during the running of the script.
|
|
//
|
|
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln());
|
|
|
|
if (fServiceDomainEnterred) {
|
|
CoLeaveServiceDomain(NULL);
|
|
}
|
|
|
|
#ifndef PERF_DISABLE
|
|
if (fPerfTransPending)
|
|
g_PerfData.Decr_TRANSPENDING();
|
|
#endif
|
|
|
|
// Uninit the scripting namespace
|
|
(VOID)intrinsics.PScriptingNamespace()->UnInit();
|
|
|
|
// If debugging, notify debugger ONPAGEDONE
|
|
if (fDebuggerNotifiedOnStart) {
|
|
Assert(pHitObj->PAppln()->FDebuggable());
|
|
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
|
|
}
|
|
|
|
if (pPoppedASPObjectContext)
|
|
pHitObj->SetASPObjectContext(pPoppedASPObjectContext);
|
|
|
|
if (pASPObjectContext)
|
|
pASPObjectContext->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
ExecuteGlobal
|
|
|
|
UNDONE: handle script engine the same manner as mainline script engines
|
|
with respect to debugging.
|
|
|
|
Execute code in Global.ASA as part of application or session start or end.
|
|
|
|
Parameters:
|
|
pHitObj - pointer to the hit object
|
|
intrinsics - pointers to the intrinsic IUnknown pointers.
|
|
pEngineInfo - pointers to engine info
|
|
pfDeleteSession - true if global.asa failed, and therefore the caller should
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT ExecuteGlobal
|
|
(
|
|
CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CTemplate *pTemplate = NULL;
|
|
WORD iEng;
|
|
BOOLB fDebuggerNotifiedOnStart = FALSE;
|
|
|
|
BOOL fUnHideRequestAndResponse = FALSE;
|
|
|
|
BOOL fOnStartAppln = FALSE;
|
|
BOOL fOnEndAppln = FALSE;
|
|
BOOL fOnEndSession = FALSE;
|
|
BOOL fGlobalAsaInCache;
|
|
BOOL fApplnStarted = FALSE;
|
|
BOOL fServiceDomainEnterred = FALSE;
|
|
CASPObjectContext *pASPObjectContext = NULL;
|
|
CASPObjectContext *pPoppedASPObjectContext = NULL;
|
|
|
|
UINT savedCodePage = CP_ACP;
|
|
UINT loadedCodePage = CP_ACP;
|
|
|
|
// The hit obj must be there, be valid, & have a global.asa name
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
Assert(pHitObj->GlobalAspPath() != NULL && *(pHitObj->GlobalAspPath()) != '\0');
|
|
|
|
// Other arg's must be right
|
|
Assert(pEngineInfo != NULL);
|
|
|
|
// save the current code page in case LoadTemplate changes it...
|
|
|
|
savedCodePage = pHitObj->GetCodePage();
|
|
|
|
// Load the script - cache will AddRef
|
|
// bug 1051: load template before possibly removing response object (in switch block, below),
|
|
// so error reporting to browser will work
|
|
hr = LoadTemplate(pHitObj->GlobalAspPath(), pHitObj, &pTemplate, intrinsics, /* fGlobalAsa */ TRUE, &fGlobalAsaInCache);
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// get the codepage again so that we can later compare to see if the initial code
|
|
// page should be reverted
|
|
|
|
loadedCodePage = pHitObj->GetCodePage();
|
|
|
|
Assert(pTemplate != NULL);
|
|
|
|
// Remember GLOBAL.ASA's type library wrapper with the application
|
|
// on the first request
|
|
|
|
if (pHitObj->FStartApplication() && pTemplate->PTypeLibWrapper())
|
|
{
|
|
pHitObj->PAppln()->SetGlobTypeLibWrapper(pTemplate->PTypeLibWrapper());
|
|
}
|
|
|
|
if (pTemplate->FTransacted()) {
|
|
|
|
pASPObjectContext = new CASPObjectContext();
|
|
|
|
if (!pASPObjectContext) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto LExit;
|
|
}
|
|
|
|
pPoppedASPObjectContext = pHitObj->SetASPObjectContext(pASPObjectContext);
|
|
}
|
|
|
|
if (pTemplate->PServicesConfig()) {
|
|
hr = CoEnterServiceDomain(pTemplate->PServicesConfig());
|
|
if (FAILED(hr)) {
|
|
goto LExit;
|
|
}
|
|
fServiceDomainEnterred = TRUE;
|
|
}
|
|
|
|
Assert(pHitObj->FIsValidRequestType());
|
|
|
|
// Figure out which events to trigger
|
|
if (pHitObj->FIsBrowserRequest())
|
|
{
|
|
fOnStartAppln = pHitObj->FStartApplication();
|
|
|
|
if (fOnStartAppln)
|
|
{
|
|
// Hide response and request intrinsics from namespace
|
|
pHitObj->HideRequestAndResponseIntrinsics();
|
|
|
|
// Flag that intrinsics need to be un-hidden back in.
|
|
fUnHideRequestAndResponse = TRUE;
|
|
}
|
|
}
|
|
else if (pHitObj->FIsSessionCleanupRequest())
|
|
{
|
|
fOnEndSession = TRUE;
|
|
}
|
|
else if (pHitObj->FIsApplnCleanupRequest())
|
|
{
|
|
fOnEndAppln = TRUE;
|
|
}
|
|
|
|
// If debugging, notify debugger ONPAGESTART
|
|
if (pHitObj->PAppln()->FDebuggable())
|
|
{
|
|
if (SUCCEEDED(pTemplate->NotifyDebuggerOnPageEvent(TRUE)))
|
|
fDebuggerNotifiedOnStart = TRUE;
|
|
}
|
|
|
|
hr = AllocAndLoadEngines(pHitObj, pTemplate, pEngineInfo, intrinsics.PScriptingNamespace(), /* fGlobalAsa */TRUE);
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// BUG 93991: Defer registration of new document with debugger until after script engines have
|
|
// been loaded
|
|
//
|
|
if (!fGlobalAsaInCache && pHitObj->PAppln()->FDebuggable())
|
|
pTemplate->AttachTo(pHitObj->PAppln());
|
|
|
|
// bug 975: if no script engines, do not attempt to call event functions
|
|
if(0 == pTemplate->CountScriptEngines())
|
|
goto LExit;
|
|
|
|
/*
|
|
* Call event functions as required
|
|
* bug 459: event functions may be in any script engine
|
|
*/
|
|
|
|
// First run Application_OnStart
|
|
if (fOnStartAppln)
|
|
{
|
|
pHitObj->SetEventState(eEventAppOnStart);
|
|
|
|
hr = CallScriptFunction(*pEngineInfo, L"Application_OnStart");
|
|
|
|
if (SUCCEEDED(hr) || hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND ||
|
|
intrinsics.PResponse()->FResponseAborted())
|
|
{
|
|
if (fUnHideRequestAndResponse)
|
|
{
|
|
pHitObj->UnHideRequestAndResponseIntrinsics();
|
|
fUnHideRequestAndResponse = FALSE;
|
|
}
|
|
|
|
fApplnStarted = TRUE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
goto LExit;
|
|
}
|
|
}
|
|
|
|
if (pHitObj->FStartSession())
|
|
{
|
|
// If application on start was run, add Response and Request names to script engines
|
|
if (fOnStartAppln)
|
|
{
|
|
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
|
|
{
|
|
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_RESPONSE, FALSE)))
|
|
goto LExit;
|
|
|
|
if (FAILED(hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddAdditionalObject(WSZ_OBJ_REQUEST, FALSE)))
|
|
goto LExit;
|
|
}
|
|
}
|
|
|
|
pHitObj->SetEventState(eEventSesOnStart);
|
|
hr = CallScriptFunction(*pEngineInfo, L"Session_OnStart");
|
|
|
|
if (FAILED(hr) && hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND &&
|
|
!intrinsics.PResponse()->FResponseAborted())
|
|
{
|
|
// Mark session as on-start-failed - to be deleted soon
|
|
pHitObj->SessionOnStartFailed();
|
|
}
|
|
else
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Mark as on-start-invoked -- need to wait for timeout
|
|
pHitObj->SessionOnStartInvoked();
|
|
}
|
|
|
|
// Check if Session_OnEnd Present
|
|
if (SUCCEEDED(TestScriptFunction(*pEngineInfo, L"Session_OnEnd")))
|
|
{
|
|
// Mark as on-end-present -- need to execute OnEnd later
|
|
pHitObj->SessionOnEndPresent();
|
|
}
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
goto LExit;
|
|
}
|
|
|
|
BOOL fImpersonationSet = FALSE;
|
|
BOOL fExecuteOnEnd = TRUE;
|
|
|
|
// no need to even attempt to run Session_OnEnd if it isn't present
|
|
|
|
if (fOnEndSession && FAILED(TestScriptFunction(*pEngineInfo, L"Session_OnEnd")))
|
|
fOnEndSession = FALSE;
|
|
|
|
// same for application_onend
|
|
|
|
if (fOnEndAppln && FAILED(TestScriptFunction(*pEngineInfo, L"Application_OnEnd")))
|
|
fOnEndAppln = FALSE;
|
|
|
|
if (fOnEndSession || fOnEndAppln) {
|
|
|
|
// Is the OnEnd routine supposed to be executed as the Anonymous user?
|
|
|
|
if (pHitObj->PAppln()->QueryAppConfig()->fRunOnEndAsAnon()) {
|
|
|
|
// if so, but we don't have a valid token, then we can't
|
|
|
|
if (pHitObj->PAppln()->QueryAppConfig()->AnonToken() == INVALID_HANDLE_VALUE) {
|
|
|
|
MSG_Error(MSG_APPL_ERROR_GETTING_ANON_TOKEN,
|
|
pHitObj->PAppln()->GetMetabaseKey());
|
|
|
|
fExecuteOnEnd = FALSE;
|
|
}
|
|
|
|
else {
|
|
|
|
fImpersonationSet = ImpersonateLoggedOnUser(pHitObj->PAppln()->QueryAppConfig()->AnonToken());
|
|
|
|
if (fImpersonationSet == FALSE) {
|
|
|
|
MSG_Error(MSG_APPL_ERROR_IMPERSONATING_ANON_USER,
|
|
pHitObj->PAppln()->GetMetabaseKey());
|
|
|
|
fExecuteOnEnd = FALSE;
|
|
}
|
|
else {
|
|
|
|
fImpersonationSet = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fOnEndSession && fExecuteOnEnd) {
|
|
|
|
pHitObj->SetEventState(eEventSesOnEnd);
|
|
hr = CallScriptFunction(*pEngineInfo, L"Session_OnEnd");
|
|
|
|
// We are failing silently here, since there is no corrective action we could take
|
|
}
|
|
|
|
|
|
if (fOnEndAppln && fExecuteOnEnd) {
|
|
pHitObj->SetEventState(eEventAppOnEnd);
|
|
hr = CallScriptFunction(*pEngineInfo, L"Application_OnEnd");
|
|
// We are failing silently here, since there is no corrective action we could take
|
|
}
|
|
|
|
// need to undo impersonation if it was set when executing the OnEnd
|
|
// routines. Note that a RevertToSelf is the right thing to do here
|
|
// as there should not have been any impersonation on the thread in
|
|
// the OnEnd case.
|
|
|
|
if (fImpersonationSet)
|
|
RevertToSelf();
|
|
|
|
LExit:
|
|
|
|
// restore the codepage in the hit object if it changed after loading
|
|
// and the session's codepage wasn't explicitly set.
|
|
|
|
if ((loadedCodePage != savedCodePage)
|
|
&& (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet()))
|
|
pHitObj->SetCodePage(savedCodePage);
|
|
|
|
if (fUnHideRequestAndResponse)
|
|
{
|
|
pHitObj->UnHideRequestAndResponseIntrinsics();
|
|
}
|
|
|
|
if (FAILED(hr) && (hr != E_SOURCE_FILE_IS_EMPTY) && pHitObj->FStartApplication() && !fApplnStarted)
|
|
{
|
|
pHitObj->ApplnOnStartFailed();
|
|
}
|
|
|
|
pHitObj->SetEventState(eEventNone);
|
|
|
|
// Uninit the scripting namespace
|
|
(VOID)intrinsics.PScriptingNamespace()->UnInit();
|
|
|
|
// Release the template
|
|
if (pTemplate)
|
|
{
|
|
|
|
// bug 975: if no script engines, do not do this
|
|
if(pTemplate->CountScriptEngines() > 0)
|
|
// Return the engine(s) to cache
|
|
DeAllocAndFreeEngines(pEngineInfo, pHitObj->PAppln());
|
|
|
|
// If debugging, notify debugger ONPAGEDONE
|
|
if (fDebuggerNotifiedOnStart)
|
|
{
|
|
Assert(pHitObj->PAppln()->FDebuggable());
|
|
pTemplate->NotifyDebuggerOnPageEvent(FALSE);
|
|
}
|
|
|
|
pTemplate->Release();
|
|
}
|
|
|
|
if (fServiceDomainEnterred)
|
|
CoLeaveServiceDomain(NULL);
|
|
|
|
// It is OK if the event function was not found in global.asa
|
|
if (hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (pPoppedASPObjectContext)
|
|
pHitObj->SetASPObjectContext(pPoppedASPObjectContext);
|
|
|
|
if (pASPObjectContext)
|
|
pASPObjectContext->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CIntrinsicObjects::Prepare
|
|
|
|
Prepare intrinsics for the request processing
|
|
|
|
Parameters:
|
|
pSession session holding the instrinsics (can be NULL)
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CIntrinsicObjects::Prepare
|
|
(
|
|
CSession *pSession
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pSession)
|
|
{
|
|
// get request, response, server from session
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pRequest = pSession->PRequest();
|
|
if (m_pRequest)
|
|
m_pRequest->AddRef();
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pResponse = pSession->PResponse();
|
|
if (m_pResponse)
|
|
m_pResponse->AddRef();
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pServer = pSession->PServer();
|
|
if (m_pServer)
|
|
m_pServer->AddRef();
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// create new request, response, server
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pRequest = new CRequest;
|
|
if (!m_pRequest)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pResponse = new CResponse;
|
|
if (!m_pResponse)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pServer = new CServer;
|
|
if (!m_pServer)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
// init request, response, server
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(m_pRequest);
|
|
hr = m_pRequest->Init();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(m_pResponse);
|
|
hr = m_pResponse->Init();
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(m_pServer);
|
|
hr = m_pServer->Init();
|
|
}
|
|
|
|
// create the scripting namespace
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pScriptingNamespace = new CScriptingNamespace;
|
|
if (!m_pScriptingNamespace)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
// cleanup on error
|
|
|
|
if (FAILED(hr))
|
|
Cleanup();
|
|
|
|
m_fIsChild = FALSE;
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CIntrinsicObjects::PrepareChild
|
|
|
|
Prepare intrinsics structure for a child request
|
|
|
|
Parameters:
|
|
pResponse parent intrinsic
|
|
pRequest parent intrinsic
|
|
pServer parent intrinsic
|
|
|
|
Returns:
|
|
HRESULT
|
|
===================================================================*/
|
|
HRESULT CIntrinsicObjects::PrepareChild
|
|
(
|
|
CResponse *pResponse,
|
|
CRequest *pRequest,
|
|
CServer *pServer
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pResponse || !pRequest || !pServer)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pResponse = pResponse;
|
|
m_pResponse->AddRef();
|
|
|
|
m_pRequest = pRequest;
|
|
m_pRequest->AddRef();
|
|
|
|
m_pServer = pServer;
|
|
m_pServer->AddRef();
|
|
|
|
m_fIsChild = TRUE;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_pScriptingNamespace = new CScriptingNamespace;
|
|
if (!m_pScriptingNamespace)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
Cleanup();
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CIntrinsicObjects::Cleanup
|
|
|
|
Cleanup the intrinsics after the request processing
|
|
|
|
Parameters:
|
|
|
|
Returns:
|
|
S_OK
|
|
===================================================================*/
|
|
HRESULT CIntrinsicObjects::Cleanup()
|
|
{
|
|
if (m_pRequest)
|
|
{
|
|
if (!m_fIsChild)
|
|
m_pRequest->UnInit();
|
|
m_pRequest->Release();
|
|
m_pRequest = NULL;
|
|
}
|
|
|
|
if (m_pResponse)
|
|
{
|
|
if (!m_fIsChild)
|
|
m_pResponse->UnInit();
|
|
m_pResponse->Release();
|
|
m_pResponse = NULL;
|
|
}
|
|
|
|
if (m_pServer)
|
|
{
|
|
if (!m_fIsChild)
|
|
m_pServer->UnInit();
|
|
m_pServer->Release();
|
|
m_pServer = NULL;
|
|
}
|
|
|
|
if (m_pScriptingNamespace)
|
|
{
|
|
m_pScriptingNamespace->Release();
|
|
m_pScriptingNamespace = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*===================================================================
|
|
ReInitIntrinsics
|
|
|
|
Call re-init on each of the intrinsics that require it
|
|
to run a new page.
|
|
|
|
Parameters:
|
|
pHitObj - pointer to the hit object
|
|
intrinsics - pointers to the intrinsic IUnknown pointers.
|
|
pEngineInfo - some engine info
|
|
fPostGlobal - Is this a reinit after running global.asa?
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT ReInitIntrinsics
|
|
(
|
|
CHitObj *pHitObj,
|
|
const CIntrinsicObjects &intrinsics,
|
|
ActiveEngineInfo *pEngineInfo,
|
|
BOOL fPostGlobal
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
Assert(pEngineInfo != NULL);
|
|
|
|
// Hand the new CIsapiReqInfo to the Server object
|
|
// Note on bug 682: We do always need to re-init CServer because it takes the phitobj
|
|
if (FAILED(hr = intrinsics.PServer()->ReInit(pHitObj->PIReq(), pHitObj)))
|
|
goto LExit;
|
|
|
|
if (FAILED(hr = intrinsics.PScriptingNamespace()->Init()))
|
|
goto LExit;
|
|
|
|
/*
|
|
* Bug 682 & 671 (better fix to 452 & 512)
|
|
* Dont re-init the Request & response objects after running a Global.Asa
|
|
* because, the running of global.asa may have set cookies into request (bug 671), that reinit
|
|
* would wipe, and the global.asa may have output headers (or other stuff) which impacts the response
|
|
* object (bug 512) that we dont want to reset.
|
|
*/
|
|
if (!fPostGlobal)
|
|
{
|
|
if (FAILED(hr = intrinsics.PRequest()->ReInit(pHitObj->PIReq(), pHitObj)))
|
|
goto LExit;
|
|
|
|
if (FAILED(hr = intrinsics.PResponse()->ReInit(
|
|
pHitObj->PIReq(),
|
|
pHitObj->PSzNewSessionCookie(),
|
|
intrinsics.PRequest(),
|
|
GetScriptEngine,
|
|
pEngineInfo,
|
|
pHitObj
|
|
)))
|
|
goto LExit;
|
|
}
|
|
|
|
LExit:
|
|
return(hr);
|
|
}
|
|
|
|
/*===================================================================
|
|
LoadTemplate
|
|
|
|
Load a template, cleanup and give appropriate errors on failure.
|
|
|
|
Parameters:
|
|
szFile - the file to load a template for
|
|
pHitObj - pointer to the hit object
|
|
ppTemplate - The returned loaded template
|
|
fGlobalAsa - is this for Global.asa?
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT LoadTemplate
|
|
(
|
|
const TCHAR *szFile,
|
|
CHitObj *pHitObj,
|
|
CTemplate **ppTemplate,
|
|
const CIntrinsicObjects &intrinsics,
|
|
BOOL fGlobalAsa,
|
|
BOOL *pfTemplateInCache)
|
|
{
|
|
HRESULT hr;
|
|
DWORD nRetryCount=0;
|
|
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
Assert(ppTemplate != NULL);
|
|
|
|
//
|
|
// Verify that we are recieving a valid filename. If not there is something seriously broken.
|
|
// We cannot have a browser request without a URL?
|
|
//
|
|
Assert(szFile);
|
|
|
|
if (_tcslen(szFile) == 0)
|
|
{
|
|
// Could not obtain the PathTranslated corresponding to this file.
|
|
|
|
// Report a Server 500 Error
|
|
pHitObj->ReportServerError(IDE_500_SERVER_ERROR);
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
Retry:
|
|
// Load the script - cache will AddRef
|
|
if (FAILED(hr = g_TemplateCache.Load(
|
|
fGlobalAsa,
|
|
szFile,
|
|
pHitObj->DWInstanceID(),
|
|
pHitObj,
|
|
ppTemplate,
|
|
pfTemplateInCache)))
|
|
{
|
|
|
|
// handle the inpage I/O Error.
|
|
|
|
// if inpage I/O error is returned, retry the Load up to 5 times...
|
|
|
|
if (hr == STATUS_IN_PAGE_ERROR /*0xc0000006*/) {
|
|
if (nRetryCount++ < 5) {
|
|
|
|
if (*ppTemplate) {
|
|
(*ppTemplate)->Release();
|
|
*ppTemplate = NULL;
|
|
}
|
|
|
|
goto Retry;
|
|
}
|
|
else {
|
|
|
|
// if retried more than 5 times, give up and return error
|
|
|
|
hr = E_COULDNT_OPEN_SOURCE_FILE;
|
|
}
|
|
}
|
|
|
|
// CONSIDER moving this cleanup into Template.Load
|
|
if (hr == E_COULDNT_OPEN_SOURCE_FILE)
|
|
{
|
|
// Load error string from string table
|
|
// BUG 731: added if to retrieve the correct header
|
|
|
|
WCHAR szwErr[128];
|
|
|
|
CwchLoadStringOfId(IDH_404_OBJECT_NOT_FOUND, szwErr, 128);
|
|
intrinsics.PResponse()->put_Status( szwErr );
|
|
HandleSysError(404, 0, IDE_404_OBJECT_NOT_FOUND, NULL, NULL, pHitObj);
|
|
#ifndef PERF_DISABLE
|
|
g_PerfData.Incr_REQNOTFOUND();
|
|
#endif
|
|
}
|
|
// fix for bug 371
|
|
if (*ppTemplate)
|
|
{
|
|
(*ppTemplate)->Release();
|
|
*ppTemplate = NULL;
|
|
}
|
|
|
|
if (hr == E_OUTOFMEMORY)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Loading template returned E_OUTOFMEMORY. Flushing template & Script Cache.\n"));
|
|
g_TemplateCache.FlushAll();
|
|
g_ScriptManager.FlushAll();
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
/*===================================================================
|
|
AllocAndLoadEngines
|
|
|
|
Allocate and load all the engines we need
|
|
|
|
Parameters:
|
|
pHitObj - The hit object
|
|
pTemplate - The template we're gonna run
|
|
pEngineInfo - Engine info to fill in
|
|
pScriptingNamespace - scripting namespace
|
|
fGlobalAsa - Are we loading engines to run global.asa?
|
|
|
|
Returns:
|
|
S_OK on success
|
|
===================================================================*/
|
|
HRESULT AllocAndLoadEngines
|
|
(
|
|
CHitObj *pHitObj,
|
|
CTemplate *pTemplate,
|
|
ActiveEngineInfo *pEngineInfo,
|
|
CScriptingNamespace *pScriptingNamespace,
|
|
BOOL fGlobalAsa
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iObj;
|
|
WORD iEng;
|
|
WORD iScriptBlock;
|
|
WORD cEngines = pTemplate->CountScriptEngines();
|
|
|
|
Assert(pHitObj != NULL);
|
|
pHitObj->AssertValid();
|
|
Assert(pTemplate != NULL);
|
|
Assert(pEngineInfo != NULL);
|
|
Assert(pScriptingNamespace != NULL);
|
|
|
|
// Load objects from template into hit object
|
|
for (iObj = pTemplate->Count(tcompObjectInfo) - 1; iObj >= 0; --iObj)
|
|
{
|
|
CHAR *szObjectName = NULL;
|
|
CLSID clsid;
|
|
CompScope scope;
|
|
CompModel model;
|
|
CMBCSToWChar convStr;
|
|
|
|
// get object-info from template and add to hitobj's list of objects
|
|
hr = pTemplate->GetObjectInfo(iObj, &szObjectName, &clsid, &scope, &model);
|
|
if(FAILED(hr))
|
|
goto LExit;
|
|
|
|
hr = convStr.Init(szObjectName);
|
|
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// ignore error ?
|
|
pHitObj->AddComponent(ctTagged, clsid, scope, model, convStr.GetString());
|
|
}
|
|
|
|
// bug 975: if no script engines, exit now
|
|
if(cEngines == 0)
|
|
goto LExit;
|
|
|
|
// Allocate space for script engines
|
|
//
|
|
// NOTE: There is a timing problem here in that the response object needs to
|
|
// be instantiated before we instantiate the script engines, but the
|
|
// response object needs to be able to access the list of active script
|
|
// engines, because it may need to halt execution. To accomplish this,
|
|
// the response object is passed a pointer to the "EngineInfo" structure
|
|
// as a pointer, and then we modify the contents of the pointer right under
|
|
// its nose. We pass an accessor function via pointer so that response just
|
|
// sees a void pointer.
|
|
//
|
|
|
|
if (cEngines == 1)
|
|
{
|
|
// don't do allocations in case of one engine
|
|
pEngineInfo->rgActiveEngines = & (pEngineInfo->siOneActiveEngine);
|
|
}
|
|
else
|
|
{
|
|
pEngineInfo->rgActiveEngines = new ScriptingInfo[cEngines];
|
|
if (pEngineInfo->rgActiveEngines == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto LExit;
|
|
}
|
|
}
|
|
|
|
pEngineInfo->cEngines = cEngines;
|
|
pEngineInfo->cActiveEngines = 0; // number of SUCCESSFULLY instantiated engines
|
|
|
|
// Load all of the script engines in advance.
|
|
for (iScriptBlock = 0; iScriptBlock < cEngines; ++iScriptBlock)
|
|
{
|
|
LPCOLESTR wstrScript;
|
|
SCRIPTSTATE nScriptState;
|
|
|
|
ScriptingInfo *pScriptInfo = &pEngineInfo->rgActiveEngines[iScriptBlock];
|
|
pTemplate->GetScriptBlock(
|
|
iScriptBlock,
|
|
&pScriptInfo->szScriptEngine,
|
|
&pScriptInfo->pProgLangId,
|
|
&wstrScript);
|
|
|
|
// Populate information required for the line mapping callback.
|
|
//
|
|
pScriptInfo->LineMapInfo.iScriptBlock = iScriptBlock;
|
|
pScriptInfo->LineMapInfo.pTemplate = pTemplate;
|
|
|
|
// acquire a script engine by:
|
|
//
|
|
// getting an engine from the template object (if it has one)
|
|
// else from the script manager.
|
|
//
|
|
// If we are in debug mode, the templates tend to be greedy and hold
|
|
// onto script engines. (See notes in scrptmgr.h)
|
|
//
|
|
pScriptInfo->pScriptEngine = NULL;
|
|
|
|
if (pHitObj->PAppln()->FDebuggable())
|
|
{
|
|
pScriptInfo->pScriptEngine = pTemplate->GetActiveScript(iScriptBlock);
|
|
|
|
if (pScriptInfo->pScriptEngine)
|
|
{
|
|
// If we got one, we don't need to re-init the engine
|
|
nScriptState = SCRIPTSTATE_INITIALIZED;
|
|
hr = static_cast<CActiveScriptEngine *>(pScriptInfo->pScriptEngine)->ReuseEngine(pHitObj, NULL, iScriptBlock, pHitObj->DWInstanceID());
|
|
}
|
|
}
|
|
|
|
if (pScriptInfo->pScriptEngine == NULL)
|
|
{
|
|
hr = g_ScriptManager.GetEngine(LOCALE_SYSTEM_DEFAULT,
|
|
*(pScriptInfo->pProgLangId),
|
|
pTemplate->GetSourceFileName(),
|
|
pHitObj,
|
|
&pScriptInfo->pScriptEngine,
|
|
&nScriptState,
|
|
pTemplate,
|
|
iScriptBlock);
|
|
}
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
|
|
// BUG 252: Keep track of how many engines we actually instantiate
|
|
++pEngineInfo->cActiveEngines;
|
|
|
|
if (nScriptState == SCRIPTSTATE_UNINITIALIZED || fGlobalAsa)
|
|
{
|
|
if (FAILED(hr = pScriptInfo->pScriptEngine->AddObjects(!fGlobalAsa)))
|
|
goto LExit;
|
|
}
|
|
|
|
if (nScriptState == SCRIPTSTATE_UNINITIALIZED)
|
|
{
|
|
if (FAILED(hr = pScriptInfo->pScriptEngine->AddScriptlet(wstrScript)))
|
|
goto LExit;
|
|
}
|
|
|
|
// Add the engine to the scripting namespace
|
|
if (FAILED(hr = pScriptingNamespace->AddEngineToNamespace(
|
|
(CActiveScriptEngine *)pScriptInfo->pScriptEngine)))
|
|
goto LExit;
|
|
|
|
// Update locale & code page (in case they are different om this page)
|
|
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoLocale);
|
|
pScriptInfo->pScriptEngine->UpdateLocaleInfo(hostinfoCodePage);
|
|
}
|
|
|
|
// Add the scripting namespace to each script engine. Because all engines might not
|
|
// implement "lazy instantiation", this code requires all
|
|
// engines are pre-instantiated (which means we can't do it in the above loop.)
|
|
// Add the scripting namespace to the hitobj first
|
|
pHitObj->AddScriptingNamespace(pScriptingNamespace);
|
|
|
|
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
|
|
pEngineInfo->rgActiveEngines[iEng].pScriptEngine->AddScriptingNamespace();
|
|
|
|
/*
|
|
* Bring all engines, except the "primary engine" (engine 0) when the template
|
|
* isn't global.asa, to runnable state in the order in which script for the
|
|
* given language was found in the script file.
|
|
*/
|
|
for (iEng = fGlobalAsa ? 0 : 1; iEng < pEngineInfo->cActiveEngines; ++iEng)
|
|
{
|
|
hr = pEngineInfo->rgActiveEngines[iEng].pScriptEngine->MakeEngineRunnable();
|
|
if (FAILED(hr))
|
|
goto LExit;
|
|
}
|
|
|
|
LExit:
|
|
return(hr);
|
|
}
|
|
|
|
/*===================================================================
|
|
DeAllocAndFreeEngines
|
|
|
|
Deallocate and free any loaded engines
|
|
|
|
Parameters:
|
|
pEngineInfo - Engine info to release
|
|
|
|
Returns:
|
|
Nothing
|
|
===================================================================*/
|
|
VOID DeAllocAndFreeEngines
|
|
(
|
|
ActiveEngineInfo *pEngineInfo,
|
|
CAppln *pAppln
|
|
)
|
|
{
|
|
WORD iEng;
|
|
|
|
Assert(pEngineInfo != NULL);
|
|
|
|
if (pEngineInfo->cActiveEngines > 0) {
|
|
if (pEngineInfo->rgActiveEngines == NULL) {
|
|
Assert(pEngineInfo->rgActiveEngines);
|
|
}
|
|
else {
|
|
for (iEng = 0; iEng < pEngineInfo->cActiveEngines; ++iEng)
|
|
g_ScriptManager.ReturnEngineToCache(&pEngineInfo->rgActiveEngines[iEng].pScriptEngine, pAppln);
|
|
pEngineInfo->cActiveEngines = 0;
|
|
}
|
|
}
|
|
if (pEngineInfo->cEngines > 1)
|
|
{
|
|
delete pEngineInfo->rgActiveEngines;
|
|
}
|
|
|
|
pEngineInfo->cEngines = 0;
|
|
pEngineInfo->rgActiveEngines = NULL;
|
|
}
|
|
|
|
/*===================================================================
|
|
GetScriptEngine
|
|
|
|
Get a script engine based on index. Return NULL if the index
|
|
is not in range. (this is a callback)
|
|
|
|
The AllocAndLoadEngines function will create an array of ScriptingInfo
|
|
structures that are defined here. It contains all the infomation
|
|
needed to 1. set up the MapScript2SourceLine callback,
|
|
2. merge namespaces, 3. set up this callback.
|
|
|
|
Parameters:
|
|
iScriptEngine - the script engine to retrieve
|
|
pvData - instance data for the function
|
|
|
|
Returns:
|
|
The requested script engine or NULL if not such engine
|
|
|
|
Side effects:
|
|
None
|
|
===================================================================*/
|
|
CScriptEngine *GetScriptEngine
|
|
(
|
|
INT iScriptEngine,
|
|
VOID *pvData
|
|
)
|
|
{
|
|
ActiveEngineInfo *pInfo = static_cast<ActiveEngineInfo *>(pvData);
|
|
if (unsigned(iScriptEngine) >= unsigned(pInfo->cActiveEngines))
|
|
{
|
|
// Note: the caller has no idea how many script engines there are.
|
|
// if the caller asks for an engine out of range, return NULL so they
|
|
// know they have asked for more than there are
|
|
return NULL;
|
|
}
|
|
|
|
return(pInfo->rgActiveEngines[iScriptEngine].pScriptEngine);
|
|
}
|
|
|
|
/*===================================================================
|
|
CallScriptFunctionOfEngine
|
|
|
|
Calls a script engine to execute one of its functions
|
|
|
|
Returns:
|
|
S_OK on success
|
|
|
|
Side effects:
|
|
None
|
|
===================================================================*/
|
|
HRESULT CallScriptFunctionOfEngine
|
|
(
|
|
ActiveEngineInfo &engineInfo,
|
|
short iScriptBlock,
|
|
wchar_t *strFunction,
|
|
CASPObjectContext *pASPObjectContext /* = NULL */
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(engineInfo.rgActiveEngines != NULL);
|
|
Assert (iScriptBlock <= engineInfo.cEngines);
|
|
|
|
CScriptEngine *pScriptEngine = (CScriptEngine *)engineInfo.rgActiveEngines[iScriptBlock].pScriptEngine;
|
|
Assert(pScriptEngine != NULL);
|
|
|
|
|
|
hr = pScriptEngine->Call(strFunction);
|
|
|
|
// housekeeping for the transacted case...
|
|
|
|
if (pASPObjectContext != NULL) {
|
|
|
|
// If the script timed out or there was an unhandled error, then autoabort
|
|
if (SUCCEEDED(hr) && (pScriptEngine->FScriptTimedOut() || pScriptEngine->FScriptHadError()))
|
|
{
|
|
hr = pASPObjectContext->SetAbort();
|
|
}
|
|
|
|
// If the script author did not do an explicit SetComplete or SetAbort
|
|
// then do a SetComplete here so Viper will return the transaction
|
|
// completion status to the caller
|
|
if (SUCCEEDED(hr) && !pASPObjectContext->FAborted())
|
|
{
|
|
hr = pASPObjectContext->SetComplete();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
CallScriptFunction
|
|
|
|
Calls each script engine in turn to execute a script function;
|
|
exits when an engine succeeds or we run out of engines.
|
|
|
|
Returns:
|
|
S_OK on success
|
|
|
|
Side effects:
|
|
None
|
|
===================================================================*/
|
|
HRESULT CallScriptFunction
|
|
(
|
|
ActiveEngineInfo &engineInfo,
|
|
wchar_t *strFunction
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
int i;
|
|
|
|
for (i = 0; i < engineInfo.cActiveEngines; i++)
|
|
{
|
|
// if execution succeeds, bail
|
|
if (SUCCEEDED(hr = CallScriptFunctionOfEngine(engineInfo, (SHORT)i, strFunction)))
|
|
goto LExit;
|
|
|
|
// if execution fails with exception other then unknown name, bail
|
|
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
|
|
goto LExit;
|
|
}
|
|
|
|
LExit:
|
|
return hr;
|
|
}
|
|
|
|
/*===================================================================
|
|
TestScriptFunction
|
|
|
|
Tests each script engine in turn to test [the existance of] a script
|
|
function; exits when an engine succeeds or we run out of engines.
|
|
|
|
Parameters
|
|
ActiveEngineInfo &engineInfo
|
|
wchar_t *strFunction functions name
|
|
|
|
|
|
Returns:
|
|
S_OK if exists
|
|
|
|
Side effects:
|
|
None
|
|
===================================================================*/
|
|
HRESULT TestScriptFunction
|
|
(
|
|
ActiveEngineInfo &engineInfo,
|
|
wchar_t *strFunction
|
|
)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
for (int i = 0; i < engineInfo.cActiveEngines; i++)
|
|
{
|
|
hr = engineInfo.rgActiveEngines[i].pScriptEngine->
|
|
CheckEntryPoint(strFunction);
|
|
|
|
// if execution succeeds, bail
|
|
if (SUCCEEDED(hr))
|
|
break;
|
|
|
|
// if fails with result other then unknown name, bail
|
|
if (hr != DISP_E_UNKNOWNNAME && hr != DISP_E_MEMBERNOTFOUND)
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|