/*===================================================================
Microsoft Denali

Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.

Component: Script Manager

File: ScrptMgr.cpp

Owner: AndrewS

This file contains the implementation of the Scrip Manager,
ie: siting an ActiveX Scripting engine (in our case VBScript) for Denali.

===================================================================*/
#include "denpre.h"
#pragma hdrstop

#include "dbgcxt.h"
#include "SMHash.h"
#include "perfdata.h"
#include "debugger.h"
#include "wraptlib.h"

// ATQ Scheduler
#include "issched.hxx"

#include "MemChk.h"

CScriptManager g_ScriptManager;
IWrapTypeLibs *g_pWrapTypelibs = NULL;

#define RESPONSE_END_ERRORCODE ERROR_OPERATION_ABORTED

HRESULT GetProgLangIdOfName(LPCSTR szProgLangName, PROGLANG_ID *pProgLangId);

//*****************************************************************************
// The following macros are used to catch exceptions thrown from the external
// scripting engines.
//
// Use of TRY/CATCH blocks around calls to the script engine is controlled by
// the DBG compile #define.  If DBG is 1, then the TRY/CATCH blocks are NOT
// used so that checked builds break into the debugger and we can examine why
// the error occurred.  If DBG is 0, then the TRY/CATCH blocks are used and
// exceptions are captured and logged to the browser (if possible) and the NT
// Event log.
//
// The TRYCATCH macros are:
//
//  TRYCATCH(_s, _IFStr)
//      _s      - statement to execute inside of TRY/CATCH block.
//      _IFStr  - string containing the name of interface invoked
//  TRYCATCH_HR(_s, _hr, _IFStr)
//      _s      - statement to execute inside of TRY/CATCH block.
//      _hr     - HRESULT to store return from _s
//      _IFStr  - string containing the name of interface invoked
//  TRYCATCH_NOHITOBJ(_s, _IFStr)
//      Same as TRYCATCH() except there is no Hitobj in the "this" object
//  TRYCATCH_HR_NOHITOBJ(_s, _hr, _IFStr)
//      Same as TRYCATCH_HR() except there is no Hitobj in the "this" object
//
//  NOTES:
//  The macros also expect that there is a local variable defined in the function
//  the macros is used of type char * named _pFuncName.
//  
//  A minimal test capability is included to allow for random errors to be throw.
//  The test code is compiled in based on the TEST_TRYCATCH #define.
//
//*****************************************************************************

//*****************************************************************************
// TEST_TRYCATCH definitions
//*****************************************************************************
#define TEST_TRYCATCH 0

#if TEST_TRYCATCH
#define  THROW_INTERVAL 57

int g_TryCatchCount = 0;

#define TEST_THROW_ERROR  g_TryCatchCount++; if ((g_TryCatchCount % THROW_INTERVAL) == 0) {THROW(0x80070000+g_TryCatchCount);}
#else
#define TEST_THROW_ERROR  
#endif

//*****************************************************************************
// The following is the heart of the TRYCATCH macros.  The definitions here are
// based on the definition of DBG.  Again, note that when DBG is off that the
// TRYCATCH defines are removed.
//*****************************************************************************

#if DBG == 0

#define START_TRYCATCH do { TRY
#define END_TRYCATCH(_hr, _hitobj, _IFStr) \
    CATCH(nException) \
        HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF, _hitobj,TRUE,nException,_IFStr,_pFuncName); \
        _hr = nException; \
    END_TRY } while(0)
#else
#define START_TRYCATCH do {
#define END_TRYCATCH(_hr, _hitobj, _IFStr) } while (0)
#endif

//*****************************************************************************
// Definition of TRYCATCH_INT which is used by all of the TRYCATCH macros
// described above.
//*****************************************************************************

#define TRYCATCH_INT(_s, _hr, _hitobj, _IFStr) \
    START_TRYCATCH \
    TEST_THROW_ERROR \
    _hr = _s; \
    END_TRYCATCH(_hr, _hitobj, _IFStr)

//*****************************************************************************
// Here are the actual definitions of the TRYCATCH macros described above.
//*****************************************************************************

#define TRYCATCH(_s, _IFStr) \
    do { \
        HRESULT     _tempHR; \
        TRYCATCH_INT(_s, _tempHR, m_pHitObj, _IFStr); \
    } while (0)
#define TRYCATCH_HR(_s, _hr, _IFStr) TRYCATCH_INT(_s, _hr, m_pHitObj, _IFStr)
#define TRYCATCH_NOHITOBJ(_s, _IFStr) \
    do { \
        HRESULT     _tempHR; \
        TRYCATCH_INT(_s, _tempHR, NULL, _IFStr); \
    } while (0)
#define TRYCATCH_HR_NOHITOBJ(_s, _hr, _IFStr) TRYCATCH_INT(_s, _hr, NULL, _IFStr)

/*===================================================================
CActiveScriptEngine::CActiveScriptEngine

Constructor for CActiveScriptEngine object

Returns:
	Nothing

Side effects:
	None.
===================================================================*/
CActiveScriptEngine::CActiveScriptEngine()
	: m_cRef(1), m_fInited(FALSE), m_fZombie(FALSE), m_fScriptLoaded(FALSE), 
	  m_fObjectsLoaded(FALSE), m_fTemplateNameAllocated(FALSE),
	  m_pAS(NULL), m_pASP(NULL), m_pDisp(NULL), m_pHIUpdate(NULL), m_lcid(LOCALE_SYSTEM_DEFAULT),
	  m_pHitObj(NULL), m_szTemplateName(NULL), m_fScriptAborted(FALSE), m_fScriptTimedOut(FALSE), 
	  m_fScriptHadError(FALSE), m_fCorrupted(FALSE), m_fBeingDebugged(FALSE), m_pTemplate(NULL),
	  m_dwInstanceID(0xBADF00D), m_dwSourceContext(0xBADF00D)
	{
	}

/*===================================================================
CActiveScriptEngine::~CActiveScriptEngine

Destructor for CActiveScriptEngine object

Returns:
	Nothing

Side effects:
	None.
===================================================================*/
CActiveScriptEngine::~CActiveScriptEngine()
	{

	if (m_fTemplateNameAllocated)
    	delete[] m_szTemplateName;

	if (m_pTemplate)
		m_pTemplate->Release();
	}

/*===================================================================
CActiveScriptEngine::FinalRelease

Call this when we are done with the object - Like release but
it removes all of the interfaces we got, so that the ref.
count can vanish when last external user is done with the engine

Returns:
	Nothing

Side effects:
	None.
===================================================================*/
ULONG CActiveScriptEngine::FinalRelease()
	{
    static const char *_pFuncName = "CActiveScriptEngine::FinalRelease()";

	if (m_pDisp)
		{
		TRYCATCH(m_pDisp->Release(),"IScriptDispatch::Release()");
		m_pDisp = NULL;
		}

	if (m_pASP)
		{
		TRYCATCH(m_pASP->Release(),"IActiveScriptParse::Release()");
		m_pASP = NULL;
		}

	if (m_pHIUpdate)
		{
		TRYCATCH(m_pHIUpdate->Release(),"IHostInfoUpdate::Release()");
		m_pHIUpdate = NULL;
		}

	if (m_pAS)
		{
		HRESULT hr;
		
		// First "close" the engine
		TRYCATCH_HR(m_pAS->Close(), hr, "IActiveScript::Close()");
		Assert(SUCCEEDED(hr));

		// Then we can release it
		TRYCATCH(m_pAS->Release(), "IActiveScript::Release()");

		m_pAS = NULL;
		}

	ULONG cRefs = Release();
	Assert (cRefs == 0);
	return cRefs;
	}

/*===================================================================
CActiveScriptEngine::Init

Init the script site object.  This must only be called once.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::Init
(
PROGLANG_ID proglang_id,
LPCTSTR szTemplateName,
LCID lcid,
CHitObj *pHitObj,
CTemplate *pTemplate,
DWORD dwSourceContext
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::Init()";
	HRESULT hr;
	UINT cTrys = 0;
	
	if (m_fInited)
		{
		Assert(FALSE);
		return(ERROR_ALREADY_INITIALIZED);
		}

	// Note: need to init these first, because we will need them if AS calls back into us during init.
	m_lcid = lcid;
	m_proglang_id = proglang_id;
	m_pHitObj = pHitObj;
	m_dwSourceContext = dwSourceContext;
	m_dwInstanceID = pHitObj->DWInstanceID();
	m_pTemplate = pTemplate;
	m_pTemplate->AddRef();

lRetry:
	// Create an instance of the script engine for the given language
	hr = CoCreateInstance(proglang_id, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void**)&m_pAS);
	if (FAILED(hr))
		{
		/*
		 * If some control (or other component) does a CoUninitialize on our thread, we will
		 * never be able to create another object.  In this case, we will get back CO_E_NOTINITIALIZED.
		 * Try (once) to re-initialize and then create the object
		 */
		if (hr == CO_E_NOTINITIALIZED && cTrys++ == 0)
			{
			MSG_Error(IDS_COUNINITIALIZE);
			hr = CoInitialize(NULL);
            if (SUCCEEDED(hr))
			    goto lRetry;
			}
		goto LFail;
		}

    // Remember template name
    hr = StoreTemplateName(szTemplateName);
	if (FAILED(hr))
		goto LFail;
    
	// Tell ActiveScripting that this is our script site
    TRYCATCH_HR(m_pAS->SetScriptSite((IActiveScriptSite *)this), hr, "IActiveScript::SetScriptSite()");
	if (FAILED(hr))
		{
		goto LFail;
		}

	// Tell ActiveScripting which exceptions we want caught
	IActiveScriptProperty *pScriptProperty;
    TRYCATCH_HR(m_pAS->QueryInterface(IID_IActiveScriptProperty, reinterpret_cast<void **>(&pScriptProperty)), hr, "IActiveScript::QueryInterface()");
    if (SUCCEEDED(hr))
    	{
    	static const int rgnExceptionsToCatch[] =
							{
							STATUS_GUARD_PAGE_VIOLATION      ,
							STATUS_DATATYPE_MISALIGNMENT     ,
							STATUS_ACCESS_VIOLATION          ,
							STATUS_INVALID_HANDLE            ,
							STATUS_NO_MEMORY                 ,
							STATUS_ILLEGAL_INSTRUCTION       ,
							STATUS_INVALID_DISPOSITION       , // what's this?  Do we need to catch it?
							STATUS_ARRAY_BOUNDS_EXCEEDED     ,
							STATUS_FLOAT_DENORMAL_OPERAND    ,
							STATUS_FLOAT_DIVIDE_BY_ZERO      ,
							STATUS_FLOAT_INVALID_OPERATION   ,
							STATUS_FLOAT_OVERFLOW            ,
							STATUS_FLOAT_STACK_CHECK         ,
							STATUS_INTEGER_DIVIDE_BY_ZERO    ,
							STATUS_INTEGER_OVERFLOW          ,
							STATUS_PRIVILEGED_INSTRUCTION    ,
							STATUS_STACK_OVERFLOW
							};

    	VARIANT varBoolTrue;
    	VARIANT varExceptionType;

    	V_VT(&varExceptionType) = VT_I4;
    	V_VT(&varBoolTrue) = VT_BOOL;
    	V_BOOL(&varBoolTrue) = -1;

		for (int i = 0; i < (sizeof rgnExceptionsToCatch) / sizeof(int); ++i)
			{
			V_I4(&varExceptionType) = rgnExceptionsToCatch[i];
			TRYCATCH(pScriptProperty->SetProperty(SCRIPTPROP_CATCHEXCEPTION, &varExceptionType, &varBoolTrue), "IActiveScriptProperty::SetProperty");
			}

		pScriptProperty->Release();
		}

	// Get ActiveScriptParse interface
	hr = GetASP();
	if (FAILED(hr))
		goto LFail;

	// Tell the script parser to init itself
    TRYCATCH_HR(m_pASP->InitNew(), hr, "IActiveScriptParse::InitNew()");
	if (FAILED(hr))
		goto LFail;

	// Get IDisp interface
	hr = GetIDisp();
	if (FAILED(hr))
		goto LFail;

	// Get IHostInfoUpdate interface
	hr = GetIHostInfoUpdate();
	if (FAILED(hr))
		goto LFail;

	m_fInited = TRUE;

LFail:
	if (FAILED(hr))
		{
		if (m_pAS)
			{
			TRYCATCH(m_pAS->Release(),"IActiveScript::Release()");
			m_pAS = NULL;
			}
		if (m_pASP)
			{
            TRYCATCH(m_pASP->Release(),"IActiveScriptParse::Release()");
			m_pASP = NULL;
			}
		if (m_pDisp)
			{
			TRYCATCH(m_pDisp->Release(),"IScriptDispatch::Release()");
			m_pDisp = NULL;
			}
		if (m_pTemplate)
			{
			m_pTemplate->Release();
			m_pTemplate = NULL;
			}
		if (m_pHIUpdate)
		    {
		    TRYCATCH(m_pHIUpdate->Release(),"IHostInfoUpdate::Release()");
		    m_pHIUpdate = NULL;
		    }
		}

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::StoreTemplateName

Stores template name inside CActiveScriptEngine. Allocates
buffer or uses internal one if the name fits.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Might allocate memory for long template names
===================================================================*/
HRESULT CActiveScriptEngine::StoreTemplateName
(
LPCTSTR szTemplateName
)
    {
    DWORD cch = _tcslen(szTemplateName);

    if (((cch+1)*sizeof(TCHAR)) <= sizeof(m_szTemplateNameBuf))
        {
        m_szTemplateName = m_szTemplateNameBuf;
        m_fTemplateNameAllocated = FALSE;
        }
    else
        {
    	m_szTemplateName = new TCHAR[cch+1];
    	if (!m_szTemplateName)
            return E_OUTOFMEMORY;
        m_fTemplateNameAllocated = TRUE;
        }
        
	_tcscpy(m_szTemplateName, szTemplateName);
    return S_OK;
    }

/*===================================================================
CActiveScriptEngine::GetASP

Get an ActiveScriptParser interface from ActiveScripting

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Fills in member variables
===================================================================*/
HRESULT CActiveScriptEngine::GetASP
(
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::GetASP()";
	HRESULT hr;

	Assert(m_pASP == NULL);
	
	m_pASP = NULL;
			
	// Get OLE Scripting parser interface, if any
    TRYCATCH_HR(m_pAS->QueryInterface(IID_IActiveScriptParse, (void **)&m_pASP), hr, "IActiveScript::QueryInterface()");

	if (m_pASP == NULL && SUCCEEDED(hr))
		hr = E_FAIL;
	if (FAILED(hr))
		{
		goto LFail;
		}

LFail:
	if (FAILED(hr))
		{
		if (m_pASP)
			{
            TRYCATCH(m_pASP->Release(),"IActiveScriptParse::Release()");
			m_pASP = NULL;
			}
		}

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::GetIDisp

Get an IDispatch interface from ActiveScripting

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Fills in member variables
===================================================================*/
HRESULT CActiveScriptEngine::GetIDisp
(
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::GetIDisp()";

	HRESULT hr;

	Assert(m_pDisp == NULL);
	
	m_pDisp = NULL;
			
	// Get an IDispatch interface to be able to call functions in the script
    
    TRYCATCH_HR(m_pAS->GetScriptDispatch(NULL, &m_pDisp),hr,"IActiveScript::GetScriptDispatch()");

	if (m_pDisp == NULL && SUCCEEDED(hr))
		hr = E_FAIL;
	if (FAILED(hr))
		{
		goto LFail;
		}

LFail:
	if (FAILED(hr))
		{
		if (m_pDisp)
			{
			TRYCATCH(m_pDisp->Release(),"IScriptDispatch::Release()");
			m_pDisp = NULL;
			}
		}

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::GetIHostInfoUpdate

Get an IHostInfoUpdate interface from ActiveScripting.
This interface is used to advise the scripting engine that
we have new information about the host (change in LCID for example)
If we can't find the interface, we exit succesfully anyway.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Fills in member variables
===================================================================*/
HRESULT CActiveScriptEngine::GetIHostInfoUpdate
(
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::GetIHostInfoUpdate()";
	HRESULT hr = S_OK;

	Assert(m_pHIUpdate == NULL);
	m_pHIUpdate = NULL;
			
	// Get an IHostInfoUpdate interface to be able to call functions in the script
    TRYCATCH_HR(m_pAS->QueryInterface(IID_IHostInfoUpdate, (void **) &m_pHIUpdate),
                hr,
                "IActiveScript::QueryInterface()");

	Assert(SUCCEEDED(hr) || hr == E_NOINTERFACE);

	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::ResetToUninitialized

When we want to reuse and engine, we reset it to an uninited state
before putting it on the FSQ

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::ResetToUninitialized()
{
    static const char *_pFuncName = "CActiveScriptEngine::ResetToUninitialized()";
	HRESULT hr = S_OK;
	
	// Reset our flags
	m_fScriptAborted = FALSE;
	m_fScriptTimedOut = FALSE;
	m_fScriptHadError = FALSE;
	m_fBeingDebugged = FALSE;

	// Release interfaces, they will need to be re-gotten when
	// the engine is reused
	if (m_pASP) {
        TRYCATCH(m_pASP->Release(),"IActiveScriptParse::Release()");
		m_pASP = NULL;
    }

	if (m_pDisp) {
		TRYCATCH(m_pDisp->Release(),"IScriptDispatch::Release()");
		m_pDisp = NULL;
    }

	if(m_pHIUpdate) {
		TRYCATCH(m_pHIUpdate->Release(),"IHostInfoUpdate::Release()");
		m_pHIUpdate = NULL;
    }

	// Hitobj will no longer be valid
	m_pHitObj = NULL;

	// Set the script state to Uninited
	if (m_pAS) {
        TRYCATCH_HR(ResetScript(), hr, "IActiveScript::SetScriptState()");
    }

	return(hr);
}

/*===================================================================
CActiveScriptEngine::ReuseEngine

Reusing an engine from the FSQ.  Reset stuff

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Sets member variables.
===================================================================*/
HRESULT CActiveScriptEngine::ReuseEngine
(
CHitObj *pHitObj,
CTemplate *pTemplate,
DWORD dwSourceContext,
DWORD dwInstanceID
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::ReuseEngine()";
	HRESULT hr = S_OK;

	/* NOTE: we must reset the hitobj & other members BEFORE calling
	 * any Active Scripting methods (esp. SetScriptSite)  This is
	 * because SetScriptSite queries us for the debug application, which
	 * relies on the hitobj being set.
	 */

	// reset the hitobj
	m_pHitObj = pHitObj;

	// Reset the debug document
	if (pTemplate)
		{
		if (m_pTemplate)
			m_pTemplate->Release();

		m_dwSourceContext = dwSourceContext;
		m_dwInstanceID = dwInstanceID;
		m_pTemplate = pTemplate;
		m_pTemplate->AddRef();
		}

	// If the engine is in the UNITIALIZED state ONLY then tell ActiveScripting
	// that this is our script site.  (Scripts in the debug cache are already initialized)
	SCRIPTSTATE nScriptState;
    TRYCATCH_HR(m_pAS->GetScriptState(&nScriptState), hr, "IActiveScript::GetScriptState()");
	if (FAILED(hr))
		goto LFail;

	if (nScriptState == SCRIPTSTATE_UNINITIALIZED)
		{
        TRYCATCH_HR(m_pAS->SetScriptSite(static_cast<IActiveScriptSite *>(this)),hr, "IActiveScript::SetScriptState()");
		if (FAILED(hr))
			goto LFail;
		}

	// Get ActiveScriptParse interface
	hr = GetASP();
	if (FAILED(hr))
		goto LFail;

	// Get IDisp interface
	hr = GetIDisp();
	if (FAILED(hr))
		goto LFail;

	// Get IHostInfoUpdate interface
	hr = GetIHostInfoUpdate();
	if (FAILED(hr))
		goto LFail;

	AssertValid();
LFail:
	return(hr);
	}

/*===================================================================
CActiveScriptEngine::MakeClone

We are cloning a running script engine.  Fill this new ActiveScriptEngine
with the cloned ActiveScript.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::MakeClone
(
PROGLANG_ID proglang_id,
LPCTSTR szTemplateName,
LCID lcid,
CHitObj *pHitObj,
CTemplate *pTemplate,
DWORD dwSourceContext,
DWORD dwInstanceID,
IActiveScript *pAS			// The cloned script engine
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::MakeClone()";
	HRESULT hr;
	
	if (m_fInited)
		{
		Assert(FALSE);
		return(ERROR_ALREADY_INITIALIZED);
		}

	// Note: need to init these first, because we will need them if AS calls back into us during init.
	m_lcid = lcid;
	m_proglang_id = proglang_id;
	m_pHitObj = pHitObj;

	m_pAS = pAS;

	StoreTemplateName(szTemplateName);

	if (m_pTemplate)
		m_pTemplate->Release();

	m_dwSourceContext = dwSourceContext;
	m_dwInstanceID = dwInstanceID;
	m_pTemplate = pTemplate;
	m_pTemplate->AddRef();

	// We are not yet inited fully but SetScriptSite may call back into us so we must flag inited now.
	m_fInited = TRUE;

	// Tell ActiveScripting that this is our script site
    TRYCATCH_HR(m_pAS->SetScriptSite((IActiveScriptSite *)this), hr, "IActiveScript::SetScriptSite()");

	if (FAILED(hr))
		{
		goto LFail;
		}

	// Get ActiveScriptParse interface
	hr = GetASP();
	if (FAILED(hr))
		goto LFail;

	// Get IDisp interface
	hr = GetIDisp();
	if (FAILED(hr))
		goto LFail;

	// Get IHostInfoUpdate interface
	hr = GetIHostInfoUpdate();
	if (FAILED(hr))
		goto LFail;

	// Because we are a clone of an already loaded engine, we have script and objects loaded.
	m_fScriptLoaded = TRUE;
	m_fObjectsLoaded = TRUE;

	// We should be valid now.
	AssertValid();
	
LFail:
	if (FAILED(hr))
		{
		m_fInited = FALSE;
		
		if (m_pAS)
			{
			// dont release the passed in script engine on failure
			m_pAS = NULL;
			}
		if (m_pASP)
			{
			TRYCATCH(m_pASP->Release(),"IActiveScriptParse::Release()");
			m_pASP = NULL;
			}
		if (m_pDisp)
			{
			TRYCATCH(m_pDisp->Release(),"IScriptDispatch::Release()");
			m_pDisp = NULL;
			}
		if (m_pTemplate)
			{
			m_pTemplate->Release();
			m_pTemplate = NULL;
			}
		if (m_pHIUpdate)
		    {
		    TRYCATCH(m_pHIUpdate->Release(),"IHostInfoUpdate::Release()");
		    m_pHIUpdate = NULL;
		    }
		}

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::InterruptScript

Stop the script from running

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Stops the script from running
===================================================================*/
HRESULT CActiveScriptEngine::InterruptScript
(
BOOL fAbnormal				// = TRUE
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::InterruptScript()";
	HRESULT hr;
	EXCEPINFO excepinfo;

	AssertValid();

	// Fill in the excepinfo.  This will be passed to OnScriptError
	memset(&excepinfo, 0x0, sizeof(EXCEPINFO));
	if (fAbnormal)
		{
		m_fScriptTimedOut = TRUE;
		excepinfo.wCode = ERROR_SERVICE_REQUEST_TIMEOUT;
		}
	else
		{
		m_fScriptAborted = TRUE;
		excepinfo.wCode = RESPONSE_END_ERRORCODE;		// Error code to ourselves - means Response.End was invoked
		}
	
        TRYCATCH_HR(m_pAS->InterruptScriptThread(SCRIPTTHREADID_BASE,		// The thread in which the engine was instantiated
		           						  &excepinfo,
								          0),
                    hr,
                    "IActiveScript::InterruptScriptThread()");
	return(hr);
	}

/*===================================================================
CActiveScriptEngine::UpdateLocaleInfo

Advise the script engine that we want to update the lcid or
code page

Returns:
	HRESULT.  S_OK on success.
===================================================================*/
HRESULT CActiveScriptEngine::UpdateLocaleInfo
(
hostinfo hiNew
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::UpdateLocaleInfo()";
	HRESULT hr = S_OK;
	
	// If no IUpdateHost ineterface is available 
	// just skip the call to UpdateInfo;
	if (m_pHIUpdate)
		TRYCATCH_HR(m_pHIUpdate->UpdateInfo(hiNew), hr, "IHostInfoUpdate::UpdateInfo()");

	return hr;
	}

#ifdef DBG
/*===================================================================
CActiveScriptEngine::AssertValid

Test to make sure that the CActiveScriptEngine object is currently correctly formed
and assert if it is not.

Returns:

Side effects:
	None.
===================================================================*/
VOID CActiveScriptEngine::AssertValid() const
	{
	Assert(m_fInited);
	Assert(m_pAS != NULL);
	Assert(m_pTemplate != NULL);
	}
#endif // DBG

/*
 *
 *
 *
 * I U n k n o w n   M e t h o d s
 *
 *
 *
 *
 */ 

/*===================================================================
CActiveScriptEngine::QueryInterface
CActiveScriptEngine::AddRef
CActiveScriptEngine::Release

IUnknown members for CActiveScriptEngine object.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::QueryInterface
(
REFIID riid,
PVOID *ppvObj
)
	{
	if (ppvObj == NULL)
		{
		Assert(FALSE);
		return E_POINTER;
		} 

	*ppvObj = NULL;

	if (IsEqualIID(riid, IID_IUnknown))
	    {
	    // this IS NOT derived directly from IUnknown
	    // typecast this to something that IS
		*ppvObj = static_cast<IActiveScriptSite *>(this);
        }
	else if (IsEqualIID(riid, IID_IActiveScriptSite))
	    {
		*ppvObj = static_cast<IActiveScriptSite *>(this);
        }
	else if (IsEqualIID(riid, IID_IActiveScriptSiteDebug))
	    {
		*ppvObj = static_cast<IActiveScriptSiteDebug *>(this);
        }
	else if (IsEqualIID(riid, IID_IHostInfoProvider))
	    {
		*ppvObj = static_cast<IHostInfoProvider *>(this);
        }
        
	if (*ppvObj != NULL)
		{
		AddRef();
		return(S_OK);
		}
	
	return(E_NOINTERFACE);
	}

STDMETHODIMP_(ULONG) CActiveScriptEngine::AddRef()
	{
	++m_cRef;

	return(m_cRef);
	}

STDMETHODIMP_(ULONG) CActiveScriptEngine::Release()
	{
	if (--m_cRef)
		return(m_cRef);

	delete this;
	return(0);
	}

/*
 *
 *
 *
 * I A c t i v e S c r i p t S i t e   M e t h o d s
 *
 *
 *
 *
 */ 

/*===================================================================
CActiveScriptEngine::GetLCID

Provide the local id for the script to the script engine.

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::GetLCID
(
LCID *plcid
)
	{
	// It is OK to call this before we are fully inited.
	//AssertValid();

	*plcid = ((CActiveScriptEngine *)this)->m_lcid;
	
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::GetItemInfo

Provide requested info for a named item to the script engine.  May be
asked for IUnknown, ITypeInfo or both.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::GetItemInfo
(
LPCOLESTR pcszName,
DWORD dwReturnMask,IUnknown **ppiunkItem,
ITypeInfo **ppti
)
	{
	HRESULT hr;
	AssertValid();
	
	// Assume none
	if (ppti)
		*ppti = NULL;
	if (ppiunkItem)
		*ppiunkItem = NULL;

    CHitObj *pHitObj = m_pHitObj;
    if (pHitObj == NULL)
        {
        // could happen when debugging and re-initializing
        // the scripting engine when storing it in the template
        // in this case GetItemInfo() is called for TYPELIB stuff
        ViperGetHitObjFromContext(&pHitObj);
    	if (pHitObj == NULL)
	        return TYPE_E_ELEMENTNOTFOUND;
        }

    // Calculate name length once
    
    DWORD cbName = CbWStr((LPWSTR)pcszName);
    
	// Special case for intrinsics
	
	IUnknown *punkIntrinsic = NULL;
	hr = pHitObj->GetIntrinsic((LPWSTR)pcszName, cbName, &punkIntrinsic);
	
	if (hr == S_OK)
	    {
    	if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
	    	{
    		Assert(ppiunkItem);
    		Assert(punkIntrinsic);
    		punkIntrinsic->AddRef();
    		*ppiunkItem = punkIntrinsic;
		    }
		return S_OK;
	    }
	else if (hr == S_FALSE)
	    {
	    // Missing intrinsic case
	    return TYPE_E_ELEMENTNOTFOUND;
	    }

	// It's not an intrinsic -- try component collection
	
	CComponentObject *pObj = NULL;
	hr = pHitObj->GetComponent(csUnknown, (LPWSTR)pcszName, cbName, &pObj);

	if (hr == S_OK) // object found
	    {
    	if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
	    	{
    		Assert(ppiunkItem != NULL);
	    	hr = pObj->GetAddRefdIUnknown(ppiunkItem);
		    }

		if (SUCCEEDED(hr))
		    return S_OK;
	    }

	// Could'n find -- output an error

	HandleItemNotFound(pcszName);
    	
	return hr;
	}

/*===================================================================
CActiveScriptEngine::HandleItemNotFound

Error handling due to item not found in GetItemInfo().

Parameters:
    pcszName        name of the item not found

Returns:
===================================================================*/
void CActiveScriptEngine::HandleItemNotFound
(
LPCOLESTR pcszName
)
    {
    HRESULT         hr = TYPE_E_ELEMENTNOTFOUND;
    
	CHAR 	        *szErrT		= NULL;
	CHAR	        szEngineT[255];
	CHAR	        szErr[255];
	TCHAR	        *szFileNameT = NULL;
    CHAR            *szFileName = NULL;
	CHAR 	        *szLineNum  = NULL;
	CHAR	        *szEngine	= NULL;
	CHAR	        *szErrCode	= NULL;
	CHAR	        *szLongDes	= NULL;
	ULONG 	        ulLineError = 0;
	DWORD	        dwMask = 0x3;
	BOOLB	        fGuessedLine = FALSE;
	UINT	        ErrId = IDE_OOM;
    CWCharToMBCS    convName;

	m_pTemplate->GetScriptSourceInfo(m_dwSourceContext, ulLineError, &szFileNameT, NULL, &ulLineError, NULL, &fGuessedLine);
	//Make a copy for error handling
#if UNICODE
	szFileName = StringDupUTF8(szFileNameT);
#else
    szFileName = StringDupA(szFileNameT);
#endif

	//get line num
	if (ulLineError)
		{
		szLineNum = (CHAR *)malloc(sizeof(CHAR)*10);
		if (szLineNum)
			_ltoa(ulLineError, szLineNum, 10);
		else
			{
			hr = E_OUTOFMEMORY;
			goto lCleanUp;
			}
		}
	//get engine
	CchLoadStringOfId(IDS_ENGINE, szEngineT, 255);
	szEngine = StringDupA(szEngineT);

	//get informative string

    if (FAILED(hr = convName.Init((LPWSTR)pcszName))) {
        goto lCleanUp;
    }
		
	// Error string is: "Failed to create object 'objname'.  Error code (code)."
	ErrId = IDE_SCRIPT_CANT_LOAD_OBJ;
	LoadErrResString(ErrId, &dwMask, NULL, NULL, szErr);
	if (szErr)
		{
		INT	cch = strlen(szErr);
		szErrT = (CHAR *)malloc((CHAR)(cch + strlen(convName.GetString()) + 1));
		if (!szErrT)
			{
			hr = E_OUTOFMEMORY;
			goto lCleanUp;
			}

		sprintf(szErrT, szErr, convName.GetString());
		szErrCode = SzScodeToErrorCode(hr);
		}
		
lCleanUp:		
	
	//szErrT is the long description		
	HandleError(ErrId, szFileName, szLineNum, szEngine, szErrCode, szErrT, NULL, m_pHitObj);
    }

/*===================================================================
CActiveScriptEngine::GetDocVersionString

Return a string uniquely identifying the current document version
from Denali's point of view.

I dont think we need this. It is mostly interesting if
the scripting engine is persisting scripts so that it can decide
if a script needs a recompile.  Since the scripting engine is
not persisting anything for us, we dont need to do anything here.

Returns:
	HRESULT.  Always returns E_NOTIMPL.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::GetDocVersionString
(
BSTR *pbstrVersion
)
	{
	return(E_NOTIMPL);
	}

/*===================================================================
CActiveScriptEngine::RequestItems

If this is called, it means that the Script engine wants us to call
IActiveScript::AddNameItem() for each named item associated with the 
script.

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::RequestItems
(
BOOL fPersistNames			// = TRUE
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::RequestItems()";
	HRESULT hr = S_OK;

	AssertValid();
	Assert (m_pHitObj != NULL);

	DWORD grf = SCRIPTITEM_ISVISIBLE;
	if (fPersistNames)
		grf |= SCRIPTITEM_ISPERSISTENT;

	/*
	 * Intrinsics
	 */

    START_TRYCATCH

	if (m_pHitObj->FIsBrowserRequest())
	    {
        hr = m_pAS->AddNamedItem(WSZ_OBJ_RESPONSE, grf);
        Assert(SUCCEEDED(hr));

        hr = m_pAS->AddNamedItem(WSZ_OBJ_REQUEST, grf);
        Assert(SUCCEEDED(hr));
        }
	
    hr = m_pAS->AddNamedItem(WSZ_OBJ_SERVER, grf);
	Assert(SUCCEEDED(hr));

	if (m_pHitObj->FHasSession())
	    {
        hr = m_pAS->AddNamedItem(WSZ_OBJ_SESSION, grf);
	    Assert(SUCCEEDED(hr));
	    }

    hr = m_pAS->AddNamedItem(WSZ_OBJ_APPLICATION, grf);
	Assert(SUCCEEDED(hr));
	
    hr = m_pAS->AddNamedItem(WSZ_OBJ_OBJECTCONTEXT, grf);
	Assert(SUCCEEDED(hr));

	/*
	 * Components from different collections
	 */

	CComponentIterator CompIter(m_pHitObj);
    LPWSTR strObjName;
        
    while (strObjName = CompIter.WStrNextComponentName())
        {
   		hr = m_pAS->AddNamedItem(strObjName, grf);
        if (FAILED(hr))
   			break;
        }
	
	Assert(SUCCEEDED(hr));

	/*
	 * Type library wrappers. (Has to be last in order to be called
	 * only when everything else fails.
	 */

	// Special flag value for typelib wrappers
	grf |= SCRIPTITEM_GLOBALMEMBERS;

	if (m_pHitObj->PTypeLibWrapper())
	    {
        hr = m_pAS->AddNamedItem(WSZ_OBJ_ASPPAGETLB, grf);
    	Assert(SUCCEEDED(hr));
	    }

    // GLOBAL.ASA typelib wrapper is added always
    // because each page does not pick up changes to
    // GLOBAL.ASA and there's no way to figure out
    // when TYPELIBs get added to GLOBAL.ASA
    hr = m_pAS->AddNamedItem(WSZ_OBJ_ASPGLOBALTLB, grf);
  	Assert(SUCCEEDED(hr));

    END_TRYCATCH(hr, m_pHitObj, "IActiveScript::AddNamedItem");

	// We are required to return OK
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::RequestTypeLibs 

If this is called, it means that the Script engine wants us to call
IActiveScript::AddTypeLib() for each typelib associated with the 
script.  It is unclear to me that this will ever be called in our case.

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::RequestTypeLibs()
	{
	AssertValid();

	// We have no typelibs for the namespace	
	
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::OnEnterScript

Host callback to indicate that the script has started executing

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnEnterScript()
	{
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::OnLeaveScript

Host callback to indicate that the script has stopped executing

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnLeaveScript()
	{
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::GetHostInfo

Host callback to for furnishing LCID and code page info

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::GetHostInfo(hostinfo hostinfoRequest, void **ppvInfo)
	{

	Assert(hostinfoRequest == hostinfoLocale || hostinfoRequest == hostinfoCodePage);

	HRESULT hr = S_OK;
	
	if (hostinfoRequest == hostinfoLocale)
		{
		// Allocate an LCID and set it to the current
		// value for the HitObj
		*ppvInfo = CoTaskMemAlloc(sizeof(UINT));
		if (!*ppvInfo)
			hr = E_OUTOFMEMORY;
		else
			(*(UINT *)*ppvInfo) = m_pHitObj->GetLCID();
		}
	else if (hostinfoRequest == hostinfoCodePage)
		{
		// Allocate an code page and set it to the current
		// value for the HitObj
		*ppvInfo = CoTaskMemAlloc(sizeof(UINT));
		if (!*ppvInfo)
			hr = E_OUTOFMEMORY;
		else
			(*(UINT *)*ppvInfo) = m_pHitObj->GetCodePage();
		}
	else
		hr = E_FAIL;
		
	return(hr);
	}

/*===================================================================
CActiveScriptEngine::OnScriptTerminate

Host callback to indicate that the script has completed.

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnScriptTerminate
(
const VARIANT *pvarResult,
const EXCEPINFO *pexcepinfo
)
	{
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::OnStateChange

Host callback to indicate that the script has changed state (e.g. from
Uninitialized to Loaded.)

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnStateChange
(
SCRIPTSTATE ssScriptState
)
	{
	return(S_OK);
	}

/*===================================================================
CActiveScriptEngine::OnScriptError

Host callback to indicate that an error has occured in the script.
We should handle the error.  We will return E_FAIL to indicate that we 
want the script to terminate.

Returns:
	HRESULT.  E_FAIL -- Terminate executing the script.

Side effects:
	None.
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnScriptError
(
IActiveScriptError __RPC_FAR *pscripterror
)
	{
	Assert(pscripterror);
	AssertValid();

	// Bug 153: If we terminate the script due to Response.End, dont show an error
	// NOTE: ActiveXScripting was failing to pass us our excepinfo.  Use member flags
	// ALSO: ActiveXScripting has fixed the problem of failing to pass us our excepinfo, but the
	// way we are doing this with flags works just fine.  
	if (m_fScriptAborted)
		{
		goto LRet;
		}
	
	if (m_fScriptTimedOut)
		{
            //Load Default Engine from resource
        char  szEngine[128];
        DWORD cch;
        cch = CchLoadStringOfId(IDS_ENGINE, szEngine, 128);
        szEngine[cch] = '\0';
        CHAR    *szFileName;
#if UNICODE
        szFileName = StringDupUTF8(m_pHitObj->PSzCurrTemplateVirtPath());
#else
        szFileName = StringDupA(m_pHitObj->PSzCurrTemplateVirtPath());
#endif

	    HandleError(IDE_SCRIPT_TIMEOUT, 
                    szFileName, 
                    NULL, 
                    StringDupA(szEngine), 
                    NULL, 
                    NULL, 
                    NULL, 
                    m_pHitObj,
                    NULL);

		if(m_pHitObj)
			{
			m_pHitObj->SetExecStatus(eExecTimedOut);
			}
		goto LRet;
		}

	// OnScriptErrorDebug calls OnScriptError.  use this test to be sure we don't log error
	// twice if we are called twice.  (won't happen with present debugging implementation,
	// but externals may change.)
	if (m_fScriptHadError)
		{
		goto LRet;
		}

	m_fScriptHadError = TRUE;				// Note that the script had an error so we can abort transactions (if any)
		
	if (pscripterror)
		{
		// If there was an error in the script, first see if we should pop up the debugger
		// (ONLY bring up Script Debugger; VID will do the right thing on its own)
		//
		// NEW CHANGE: always bring error description to browser, since VID does not
		// give sufficient description.
		//
		if (FCaesars() && m_pHitObj->PAppln()->FDebuggable())
			DebugError(pscripterror, m_pTemplate, m_dwSourceContext, g_pDebugApp);

		HandleError(pscripterror, m_pTemplate, m_dwSourceContext, NULL, m_pHitObj);
		}

LRet:
	// Bug 99718 return S_OK to tell the script engine that we handled the error ok.
	// Returning E_FAIL would not stop the scripting engine, this was a doc error.
	return(S_OK);
	}


/*
 *
 *
 *
 * I A c t i v e S c r i p t S i t e D e b u g   M e t h o d s
 *
 *
 *
 *
 */ 

/*===================================================================
CActiveScriptEngine::OnScriptErrorDebug

Callback for debugger to query host on what to do on exception.

NOTE: Theoretically, we would set *pfCallOnScriptErrorWhenContinuing
      to TRUE and not call OnScriptError, and set *pfEnterDebugger
      to TRUE or FALSE based on whether debugging is enabled and
      whether user wants to debug.

      However, in practice, *pfCallOnScriptErrorWhenContinuing is
      not honored (OnScriptError is NOT called in any case), and
      the VID team wants us to pretend like we don't implement
      this interface.  However, we always need our "OnScriptError"
      code to execute, so we call our OnScriptError function
      explicitly, then fail

Returns:
	HRESULT.  always returns E_NOTIMPL

Side effects:
	calls OnScriptError
===================================================================*/
STDMETHODIMP CActiveScriptEngine::OnScriptErrorDebug
(
IActiveScriptErrorDebug *pscripterror,
BOOL *pfEnterDebugger,
BOOL *pfCallOnScriptErrorWhenContinuing
)
	{
	OnScriptError(pscripterror);
	return E_NOTIMPL;
	} 

/*===================================================================
CActiveScriptEngine::GetDocumentContextFromPosition

Create a document context (file + offset + length) from an offset in the
script.

Returns:
	HRESULT.  S_OK on success.
===================================================================*/
HRESULT CActiveScriptEngine::GetDocumentContextFromPosition
(
/* [in] */ DWORD_PTR dwSourceContext,
/* [in] */ ULONG cchTargetOffset,
/* [in] */ ULONG cchText,
/* [out] */ IDebugDocumentContext **ppDocumentContext)
	{
    static const char *_pFuncName = "CActiveScriptEngine::GetDocumentContextFromPosition()";
	TCHAR *szSourceFile;
	ULONG cchSourceOffset;
	ULONG cchSourceText;
	IActiveScriptDebug *pASD;

	// Convert offset in script engine to source location, and get debugging interfaces
	m_pTemplate->GetSourceOffset(m_dwSourceContext, cchTargetOffset, &szSourceFile, &cchSourceOffset, &cchSourceText);
    
    HRESULT  hr;

    TRYCATCH_HR(m_pAS->QueryInterface(IID_IActiveScriptDebug, reinterpret_cast<void **>(&pASD)), 
                                      hr,
                                      "IActiveScript::QueryInterface()");

    if (FAILED(hr))
        return(E_FAIL);

	// If this is in the main file, create a document context based on the CTemplate compiled source
	if (_tcscmp(szSourceFile, m_pTemplate->GetSourceFileName()) == 0)
		{
		if (
			(*ppDocumentContext = new CTemplateDocumentContext(m_pTemplate, cchSourceOffset, cchSourceText, pASD, m_dwSourceContext, cchTargetOffset))
			 == NULL
		   )
			{
			pASD->Release();
			return E_OUTOFMEMORY;
			}
		}

	// source refers to an include file, so create a documet context based on cached CIncFile dependency graph
	else
		{
		CIncFile *pIncFile;
		if (FAILED(g_IncFileMap.GetIncFile(szSourceFile, &pIncFile)))
			return E_FAIL;

		if (
			(*ppDocumentContext = new CIncFileDocumentContext(pIncFile, cchSourceOffset, cchSourceText))
			 == NULL
		   )
			{
			TRYCATCH(pASD->Release(),"IActiveScriptDebug::Release()");
			pIncFile->Release();
			return E_OUTOFMEMORY;
			}

		pIncFile->Release();
		}

	TRYCATCH(pASD->Release(),"IActiveScriptDebug::Release()");
	m_fBeingDebugged = TRUE;
	return S_OK;
	}

/*===================================================================
CActiveScriptEngine::GetApplication

Return a pointer to the application that the script resides in.

Returns:
	HRESULT.  Always succeeds.
===================================================================*/
HRESULT CActiveScriptEngine::GetApplication
(
/* [out] */ IDebugApplication **ppDebugApp
)
	{
	Assert (m_pTemplate != NULL);
	if (m_pTemplate->FDebuggable())
		{
		Assert (g_pDebugApp);

		*ppDebugApp = g_pDebugApp;
		(*ppDebugApp)->AddRef();

		return S_OK;
		}
	else
		{
		*ppDebugApp = NULL;
		return E_FAIL;
		}
	}

/*===================================================================
CActiveScriptEngine::GetRootApplicationNode

Return a pointer to the top level node (for browsing)

Returns:
	HRESULT.  Always succeeds.
===================================================================*/
HRESULT CActiveScriptEngine::GetRootApplicationNode
(
/* [out] */ IDebugApplicationNode **ppRootNode
)
	{
	Assert (m_pTemplate != NULL);
	if (m_pTemplate->FDebuggable())
		{
		Assert (g_pDebugAppRoot);

		*ppRootNode = g_pDebugAppRoot;
		(*ppRootNode)->AddRef();
		return S_OK;
		}
	else
		{
		*ppRootNode = NULL;
		return E_FAIL;
		}
	}

/*
 *
 *
 *
 * C S c r i p t E n g i n e   M e t h o d s
 *
 *
 *
 *
 */ 

/*===================================================================
CActiveScriptEngine::AddScriptlet

Add a piece of code to the script engine.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Adds script code to the engine. Potentially allocates memory.
===================================================================*/
HRESULT CActiveScriptEngine::AddScriptlet
(
LPCOLESTR wstrScript // scriptlet text
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::AddScriptlet()";
	HRESULT hr;
	EXCEPINFO excepinfo;

	AssertValid();

	// Tell ActiveScripting to add the script to the engine

    TRYCATCH_HR(m_pASP->ParseScriptText(
						wstrScript,			// the scriptlet text
						NULL,				// pstrItemName
						NULL,				// punkContext
						L"</SCRIPT>",		// End Delimiter -- Engine will never see this, but does tell it to strip comments.
						m_dwSourceContext,	// dwSourceContextCookie
						1,					// ulStartingLineNumber
						SCRIPTTEXT_ISPERSISTENT | SCRIPTTEXT_HOSTMANAGESSOURCE,
						NULL,				// pvarResult
						&excepinfo),		// exception info filled in on error
                hr,
                "IActiveScriptParse::ParseScriptText()");

	if (SUCCEEDED(hr))
		m_fScriptLoaded = TRUE;

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::AddObjects

Add named objects to the script name space

Returns:
	HRESULT.  Always returns S_OK.

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::AddObjects
(
BOOL fPersistNames			// = TRUE
)
	{
	HRESULT hr = S_OK;
	AssertValid();

	// There must be a hit object set
	Assert(m_pHitObj != NULL);

	// Leverage RequestItems to give AS all the names
	hr = RequestItems(fPersistNames);

	if (SUCCEEDED(hr))
		m_fObjectsLoaded = TRUE;

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::AddAdditionalObject

Add additional named objects to the script name space beyond the
names already added with AddObject.  Note: the caller MUST have
added then names to the HitObj before making this call

Returns:
	HRESULT.  S_OK on success

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::AddAdditionalObject
(
LPWSTR strObjName,
BOOL fPersistNames			// = TRUE
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::AddAdditionalObject()";
	HRESULT hr = S_OK;
	DWORD grf;
	
	AssertValid();

	// There must be a hit object set
	Assert(m_pHitObj != NULL);

	// CONSIDER: It would be nice in debug code to walk the hitobj objlist and make sure
	//			that the given name is in there

	/*
	 * Give AS the names
	 */
	grf = SCRIPTITEM_ISVISIBLE;
	if (fPersistNames)
		grf |= SCRIPTITEM_ISPERSISTENT;
		
    TRYCATCH_HR(m_pAS->AddNamedItem(strObjName, grf), hr, "IActiveScript::AddNamedItem()");

    Assert(SUCCEEDED(hr));		// Should never fail!

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::AddScriptingNamespace

Add the given scripting namespace object to the engine.

Note that it is added as GLOBALMEMBERS, and Not as ISPERSISTENT

Returns:
	HRESULT.  S_OK on success

Side effects:
	None.
===================================================================*/
HRESULT CActiveScriptEngine::AddScriptingNamespace
(
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::AddScriptingNamespace()";
	HRESULT hr = S_OK;
	
	AssertValid();
	Assert(m_pHitObj != NULL);
	
	/*
	 * Give AXS the name and mark it GLOBALMEMBERS so all members are top level names
	 * in the namespace
	 */
    TRYCATCH_HR(m_pAS->AddNamedItem(WSZ_OBJ_SCRIPTINGNAMESPACE, SCRIPTITEM_ISVISIBLE | SCRIPTITEM_GLOBALMEMBERS), 
                hr,
                "IActiveScript::AddNamedItem()");
	Assert(SUCCEEDED(hr));		// Should never fail!

	return(hr);
	}

/*===================================================================
CActiveScriptEngine::CheckEntryPoint

Determines if the specific named entry point exists in the given script.

Returns:
	S_OK if found
	DISP_E_UNKNOWNNAME if not found
	Other OLE errors may be returned

Side effects:
	None
===================================================================*/
HRESULT CActiveScriptEngine::CheckEntryPoint
(
LPCOLESTR strEntryPoint		// The name of the sub/fn to look for
)
	{
    static const char *_pFuncName = "CActiveScriptEngine::CheckEntryPoint()";
	HRESULT hr;
	DISPID dispid;

	AssertValid();

	if (strEntryPoint == NULL)
		{
		Assert(FALSE);
		hr = DISP_E_UNKNOWNNAME;
		}
	else
		{
		// Get the DISPID of the method to call

        TRYCATCH_HR(m_pDisp->GetIDsOfNames(IID_NULL,		// REFIID - Reserved, must be NULL
	    							 (LPOLESTR *)&strEntryPoint, // Array of names to look up
		    						 1,					// Number of names in array
			    					 m_lcid,			// Locale id
				    				 &dispid),			// returned dispid
                    hr,
                    "IScriptDispatch::GetIDsOfNames()");
								 
		// Only error we expect is DISP_E_UNKNOWNNAME (or DISP_E_MEMBERNOTFOUND)
		Assert(hr == S_OK || hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND);
		}
		
	return(hr);
	}

/*===================================================================
CActiveScriptEngine::Call

Runs the specified function.

If a specific named entry point is provided (e.g. Session_OnStart)
then we will call that by name.  Otherwise (e.g. a "main" script),
pass NULL for the name and we will run just the mainline code.

Calls TryCall (optionally from under TRY CATCH) to do the job

Returns:
	HRESULT.  S_OK on success.

Side effects:
	May have various side effects depending on the script run
===================================================================*/
HRESULT CActiveScriptEngine::Call
(
LPCOLESTR strEntryPoint		// The name of the sub/fn to call (may be NULL for "main")
)
{
	HRESULT hr;
	
	AssertValid();

	if (Glob(fExceptionCatchEnable)) {
    	// Catch any GPFs in VBS, OleAut, or external components

        TRY

            hr = TryCall(strEntryPoint);
        
    	CATCH(nExcept)
    		/*
    		 * Catching a GPF or stack overflow
    		 * Report it to the user, Assert (if debug), and exit with E_UNEXPECTED.
    		 */
    		if (STATUS_STACK_OVERFLOW == nExcept) {
    			HandleErrorMissingFilename(IDE_STACK_OVERFLOW, m_pHitObj);
#if UNICODE
                DBGPRINTF((DBG_CONTEXT, "Invoking the script '%S' overflowed the stack", m_szTemplateName));
#else
                DBGPRINTF((DBG_CONTEXT, "Invoking the script '%s' overflowed the stack", m_szTemplateName));
#endif
            }
    		else {	
    			HandleErrorMissingFilename(IDE_SCRIPT_GPF, m_pHitObj, TRUE, nExcept);
#if UNICODE
                DBGPRINTF((DBG_CONTEXT, "Invoking the script '%S' threw an exception (%x).", m_szTemplateName, nExcept));
#else
                DBGPRINTF((DBG_CONTEXT, "Invoking the script '%s' threw an exception (%x).", m_szTemplateName, nExcept));
#endif
            }

    		// Dont reuse the engine
    		m_fCorrupted = TRUE;
    		
    		hr = E_UNEXPECTED;
    	END_TRY
    }
    else {
        // Don't catch exceptions
        hr = TryCall(strEntryPoint);
    }

	return(hr);
}

/*===================================================================
CActiveScriptEngine::TryCall

Runs the specified function.

If a specific named entry point is provided (e.g. Session_OnStart)
then we will call that by name.  Otherwise (e.g. a "main" script),
pass NULL for the name and we will run just the mainline code.

Called from Call (optionally from under TRY CATCH)

Returns:
	HRESULT.  S_OK on success.

Side effects:
	May have various side effects depending on the script run
===================================================================*/
HRESULT CActiveScriptEngine::TryCall
(
LPCOLESTR strEntryPoint		// The name of the sub/fn to call (may be NULL for "main")
)
	{
	HRESULT hr;
	DISPID dispid;
	DISPPARAMS dispparams;
	UINT nArgErr;

	/*
	 * Before calling any code we will transition the script to "STARTED" state.
	 * This is part of the ActiveXScripting Reset work.
	 */
    hr = m_pAS->SetScriptState(SCRIPTSTATE_STARTED);

	if (FAILED(hr))
		{
		Assert(FALSE);
		goto LRet;
		}
		
	if (strEntryPoint != NULL)
		{
		// Get the DISPID of the method to call
		hr = m_pDisp->GetIDsOfNames(IID_NULL,		// REFIID - Reserved, must be NULL
								 (LPOLESTR *)&strEntryPoint, // Array of names to look up
								 1,					// Number of names in array
								 m_lcid,			// Locale id
								 &dispid);			// returned dispid
		if (FAILED(hr))
			{
			// Only error we expect is DISP_E_UNKNOWNNAME (or DISP_E_MEMBERNOTFOUND)
			Assert(hr == DISP_E_UNKNOWNNAME || hr == DISP_E_MEMBERNOTFOUND);
			goto LRet;
			}

		// There are no arguments
		memset(&dispparams, 0, sizeof(dispparams));

		// Invoke it
		hr = m_pDisp->Invoke(dispid,			// dispid to invoke
						IID_NULL,				// REFIID - Reserved, must be NULL
						 m_lcid,				// Locale id
						 DISPATCH_METHOD,		// Calling a method, not a property get/put
						 &dispparams,			// pass arguments
						 NULL,					// return value
						 NULL,					// We aren't interested in the exception info
						 &nArgErr);				// if there is a Type Mismatch, which argument was the problem
		}
	
LRet:
	return(hr);
	}


/*
 *
 *
 *
 * C S c r i p t M a n a g e r
 *
 *
 *
 *
 */ 

/*===================================================================
CScriptManager::CScriptManager

Constructor for CScriptManager object

Returns:
	Nothing

Side effects:
	None.
===================================================================*/
CScriptManager::CScriptManager()
	: m_fInited(FALSE), m_idScriptKiller(0)
	{
	}

/*===================================================================
CScriptManager::~CScriptManager

Destructor for CScriptManager object

Returns:
	Nothing

Side effects:
	None.
===================================================================*/
CScriptManager::~CScriptManager()
	{
	}

/*===================================================================
CScriptManager::Init

Init the script manager.  This must only be called once.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::Init
(
)
	{
    static const char *_pFuncName = "CScriptManager::Init()";
	HRESULT hr;
	BOOL fPLLInited = FALSE;
	BOOL fcsPLLInited = FALSE;
	BOOL fFSQInited = FALSE;
	BOOL fcsFSQInited = FALSE;
	BOOL fRSLInited = FALSE;
	BOOL fcsRSLInited = FALSE;
	DWORD cBuckets;
	DWORD rgPrime[] = { 3, 11, 23, 57, 89 };
	WORD iP;
    IActiveScript *pAST = NULL;
	static const GUID uid_VBScript	= { 0xB54F3741, 0x5B07, 0x11cf, { 0xA4, 0xB0, 0x00, 0xAA, 0x00, 0x4A, 0x55, 0xE8}};
	
	// Illegal to re-init
	if (m_fInited)
		{
		Assert(FALSE);
		return(ERROR_ALREADY_INITIALIZED);
		}

	// Create the critical sections for serializing access to lists
	ErrInitCriticalSection(&m_cSPLL, hr);
	if (FAILED(hr))
		goto LError;
	fcsPLLInited = TRUE;
	ErrInitCriticalSection(&m_csFSQ, hr);
	if (FAILED(hr))
		goto LError;
	fcsFSQInited = TRUE;
	ErrInitCriticalSection(&m_csRSL, hr);
	if (FAILED(hr))
		goto LError;
	fcsRSLInited = TRUE;

	// List of programming language clsid's
	hr = m_hTPLL.Init();
	if (FAILED(hr))
		goto LError;
	fPLLInited = TRUE;

	// Free Script Queue
	// Init it with a prime # of buckets in relation to script engine cache max
	cBuckets = (Glob(dwScriptEngineCacheMax) / 2) + 1;
	for (iP = (sizeof(rgPrime) / sizeof(DWORD)) - 1; iP > 0; iP--)
		if (rgPrime[iP] < cBuckets)
			{
			cBuckets = rgPrime[iP];
			break;
			}
	if (cBuckets < rgPrime[1])
		cBuckets = rgPrime[0];
			
	hr = m_htFSQ.Init(cBuckets);
	if (FAILED(hr))
		goto LError;
	fFSQInited = TRUE;

	// Running Script List
	// Init it with a prime # of buckets in relation to max # of threads
	cBuckets = Glob(dwThreadMax) / 2;
	for (iP = (sizeof(rgPrime) / sizeof(DWORD)) - 1; iP > 0; iP--)
		if (rgPrime[iP] < cBuckets)
			{
			cBuckets = rgPrime[iP];
			break;
			}
	if (cBuckets < rgPrime[1])
		cBuckets = rgPrime[0];
		
	hr = m_htRSL.Init(cBuckets);
	if (FAILED(hr))
		goto LError;
	fRSLInited = TRUE;

	// Schedule script killer
    m_msecScriptKillerTimeout = Glob(dwScriptTimeout) * 500;
	m_idScriptKiller = ScheduleWorkItem
	    (
	    CScriptManager::ScriptKillerSchedulerCallback,  // callback
	    this,                                           // context
        m_msecScriptKillerTimeout,                      // timeout
        TRUE                                            // periodic
        );
    if (!m_idScriptKiller)
        {
        hr = E_FAIL;
        goto LError;
        }

    // TypeLib support: Create a scripting engine and QI it for the TypeLib wrapper support
    hr = CoCreateInstance(uid_VBScript, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void**)&pAST);
    if (FAILED(hr))
        goto LError;
    TRYCATCH_HR_NOHITOBJ(pAST->QueryInterface(IID_IWrapTypeLibs, (VOID **)&g_pWrapTypelibs),
                         hr,
                         "IActiveScript::QueryInterface()");
    TRYCATCH_NOHITOBJ(pAST->Release(),"IActiveScript::Release()");        // No longer need the pointer to the engine
    if (FAILED(hr))
        goto LError;
    
	// All OK.  We are inited.
	m_fInited = TRUE;

	goto LExit;
	
LError:
    if (m_idScriptKiller)
        RemoveWorkItem(m_idScriptKiller);
	if (fcsPLLInited)
		DeleteCriticalSection(&m_cSPLL);
	if (fcsFSQInited)
		DeleteCriticalSection(&m_csFSQ);
	if (fcsRSLInited)
		DeleteCriticalSection(&m_csRSL);
	if (fPLLInited)
		m_hTPLL.UnInit();
	if (fFSQInited)
		m_htFSQ.UnInit();
	if (fRSLInited)
		m_htRSL.UnInit();
	if (pAST)
	    TRYCATCH_NOHITOBJ(pAST->Release(),"IActiveScript::Release()");
	if (g_pWrapTypelibs)
	    {
	    TRYCATCH_NOHITOBJ(g_pWrapTypelibs->Release(),"IWrapTypeLibs::Release()");
	    g_pWrapTypelibs = NULL;
	    }

LExit:	
	return(hr);
	}

/*===================================================================
CScriptManager::UnInit

UnInit the script manager.  This must only be called once.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::UnInit
(
)
	{
    static const char *_pFuncName = "CScriptManager::UnInit()";
	HRESULT hr = S_OK, hrT;
	
	if (m_fInited)
		{
		// Un-schedule script killer
        if (m_idScriptKiller)
            {
            RemoveWorkItem(m_idScriptKiller);
            m_idScriptKiller = 0;
            }
        
		// Uninit each of the lists.  Attempt to uninit them all, even if we get an error.
		// Dont lose any errors along the way.
		hr = UnInitASEElems();
		hrT = UnInitPLL();
		if (SUCCEEDED(hr))
			hr = hrT;	
		hrT = m_hTPLL.UnInit();
		if (SUCCEEDED(hr))
			hr = hrT;	
		hrT = m_htFSQ.UnInit();
		if (SUCCEEDED(hr))
			hr = hrT;	
		hrT = m_htRSL.UnInit();
		if (SUCCEEDED(hr))
			hr = hrT;	

        if (g_pWrapTypelibs)
            {
            TRYCATCH_NOHITOBJ(g_pWrapTypelibs->Release(),"IWrapTypeLibs::Release()");
            g_pWrapTypelibs = NULL;
            }
            
		// Free the critical sections (bug 1140: do this after freeing everything else)
		DeleteCriticalSection(&m_cSPLL);
		DeleteCriticalSection(&m_csFSQ);
		DeleteCriticalSection(&m_csRSL);

		m_fInited = FALSE;
		}

	return(hr);
	}


/*===================================================================
CScriptManager::AdjustScriptKillerTimeout

Adjust (shorten) script killer timeout when needed.
The caller should take care of the critical sectioning.

Parameters:
    msecNewTimeout    new suggested timeout value (in ms)

Returns:
	HRESULT.  S_OK on success.
===================================================================*/
HRESULT CScriptManager::AdjustScriptKillerTimeout
(
DWORD msecNewTimeout
)
    {
    const DWORD MSEC_MIN_SCRIPT_TIMEOUT = 5000;   // 5 seconds

    if (!m_idScriptKiller)
        return E_FAIL;  // no script killer scheduled

    // don't set to < minimum
    if (msecNewTimeout < MSEC_MIN_SCRIPT_TIMEOUT)
        msecNewTimeout = MSEC_MIN_SCRIPT_TIMEOUT;
        
    if (m_msecScriptKillerTimeout <= msecNewTimeout)
        return S_OK; // the timeout already short enough

    if (ScheduleAdjustTime(
            m_idScriptKiller, 
            msecNewTimeout) == S_OK)
        {
        m_msecScriptKillerTimeout = msecNewTimeout;
        return S_OK;
        }
    else
        {
        return E_FAIL;
        }
    }

/*===================================================================
CScriptManager::GetEngine

Return an engine to the caller.  Ideally, we will find an engine
that already has the given script in it in our Free Script Queue
and will just hand it out.  If there isnt one, then we will look
in the Running Script List and attempt to clone a running script.
Failing that, we will create a new script
engine.  We return an ENGINESTATE state indicating if the engine
is filled with script or not.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially allocates memory.
===================================================================*/
HRESULT CScriptManager::GetEngine
(
LCID lcid,					// The system language to use
PROGLANG_ID& progLangId,	// prog lang id of the script
LPCTSTR szTemplateName,		// Template we want an engine for
CHitObj *pHitObj,			// Hit obj to use in this engine
CScriptEngine **ppSE,		// Returned script engine
ENGINESTATE *pdwState,		// Current state of the engine
CTemplate *pTemplate,		// template which engine is based from
DWORD dwSourceContext		// source context cookie (engine ID)
)
	{
	HRESULT hr = S_OK;
	CActiveScriptEngine *pASE = NULL;
	CASEElem *pASEElem = NULL;
	DWORD dwInstanceID = pHitObj->DWInstanceID();
	
	AssertValid();

	/*	NOTE progLangId must be valid because CTemplate::Compile() 
		fails way upstream of this point if it cannot generate a valid progLangId.
		Unfortunately there is no easy way to assert progLangId is valid ...
	*/

	/*
	 * First try to find the engine in the FSQ
	 *
	 * Note: We are going to enter our CS now, and keep it until we have
	 * secured the engine for ourselves.  Otherwise, it might be possible
	 * for us to get an engine, and then have another thread get the
	 * same engine before we manage to get it off of the FSQ.
	 * This makes the code a little hard to read, but is nessecary.
	 */
	EnterCriticalSection(&m_csFSQ);
#ifndef REUSE_ENGINE

    // This will only find fully loaded engines
	hr = FindEngineInList(szTemplateName, progLangId, dwInstanceID, /*fFSQ*/TRUE, &pASEElem);
	
#endif
	if (FAILED(hr))
		{
    	LeaveCriticalSection(&m_csFSQ);
		goto LFail;
		}

	if (pASEElem == NULL || pASEElem->PASE() == NULL)
		{
    	LeaveCriticalSection(&m_csFSQ);
		}
	else
		{
		// We got an engine we want to use, remove it from FSQ
		(VOID)m_htFSQ.RemoveElem(pASEElem);
#ifndef PERF_DISABLE
        g_PerfData.Decr_SCRIPTFREEENG();
#endif
    	LeaveCriticalSection(&m_csFSQ);

		pASE = pASEElem->PASE();
		Assert(!pASE->FIsZombie());
		Assert(pASE->FFullyLoaded());
		hr = pASE->ReuseEngine(pHitObj, pTemplate, dwSourceContext, dwInstanceID);
		if (FAILED(hr))
			goto LFail;
		}
		
	/*
	 * If not found, try to find the engine in the RSL and clone it
	 */
	if (pASE == NULL)
		{
		CASEElem *pASEElemRunning = NULL;
		CActiveScriptEngine *pASERunning = NULL;

		// If we do find an engine to clone, dont let anyone at it until we've cloned it
    	EnterCriticalSection(&m_csRSL);

#ifndef CLONE
		hr = FindEngineInList(szTemplateName, progLangId, dwInstanceID, /*fFSQ*/FALSE, &pASEElemRunning);
#else	// CLONE
		// Clone turned off - pretend one wasnt found
		pASEElemRunning = NULL;
#endif
		/*
		 * If we didnt find an element, or it was null, or (bug 1225) it was corrupted
		 * by a GPF running a script, or it was a zombie, then leave the CS and continue.
		 */
		if (FAILED(hr) || pASEElemRunning == NULL || pASEElemRunning->PASE() == NULL ||
			pASEElemRunning->PASE()->FIsCorrupted() || pASEElemRunning->PASE()->FIsZombie())
			{
			LeaveCriticalSection(&m_csRSL);
			if (FAILED(hr))
				goto LFail;
			}
		else
	        {
			pASERunning = pASEElemRunning->PASE();
			Assert(pASERunning != NULL);
			}

		if (pASERunning != NULL)
			{
			IActiveScript *pAS, *pASClone;

    		Assert(!pASERunning->FIsZombie());
	    	Assert(pASERunning->FFullyLoaded());

			// Found a running engine, clone it
			pAS = pASERunning->GetActiveScript();
			Assert(pAS != NULL);
			hr = pAS->Clone(&pASClone);

			// We've cloned the engine, we can let go of the CS
			LeaveCriticalSection(&m_csRSL);

			// Scripting engines are not required to implement clone.  If we get an error,
			// just continue on and create a new engine
			if (FAILED(hr))
				{
				Assert(hr == E_NOTIMPL);		// I only expect E_NOTIMPL
				Assert(pASE == NULL);			// the ASE should not be filled in
				pASE = NULL;
				hr = S_OK;
				}
			else
				{
				// Got back a cloned IActiveScript.  Create a new ASE and fill it in
				pASE = new CActiveScriptEngine;
				if (!pASE)
					{
					hr = E_OUTOFMEMORY;
					pASClone->Release();
					goto LFail;
					}
				hr = pASE->MakeClone(progLangId, szTemplateName, lcid, pHitObj, pTemplate, dwSourceContext, dwInstanceID, pASClone);
				if (FAILED(hr))
					{
					// if we failed, we must release the clone AS
					pASClone->Release();
					goto LFail;
					}
				}
			}
		}

	/*
	 * Have an engine that we can reuse
	 */
	if (pASE != NULL)
		{
		// Reusing an engine.  Let the caller know that it is already initialized
		*pdwState = SCRIPTSTATE_INITIALIZED;

		goto LHaveEngine;
		}

	/*
	 * No suitable engine to reuse.  Return a new one
	 */
	pASE = new CActiveScriptEngine;
	if (!pASE)
		{
		hr = E_OUTOFMEMORY;
		goto LFail;
		}
	hr = pASE->Init(progLangId, szTemplateName, lcid, pHitObj, pTemplate, dwSourceContext);
	if (FAILED(hr))
		goto LFail;

	// This is a new engine, let the caller know it is uninitialized
	*pdwState = SCRIPTSTATE_UNINITIALIZED;

LHaveEngine:
	// Return the engine as a CScriptEngine -- the caller only needs those interfaces
	pASE->AssertValid();				// The engine we're about to give back should be valid
	*ppSE = (CScriptEngine *)pASE;
	
	// Put the engine on the Running Scrips List
	// If we got this engine from the FSQ, reuse that elem.
	if (pASEElem == NULL)
		{
		pASEElem = new CASEElem;
		if (!pASEElem)
			{
			hr = E_OUTOFMEMORY;
			goto LFail;
			}
		hr = pASEElem->Init(pASE);
		if (FAILED(hr))
			{
			Assert(FALSE);		// Shouldnt fail
			delete pASEElem;
			goto LFail;
			}
		}

    /*
     * Above, we may have gotten an engine from the FSQ or cloned on from the RSL or
     * created a new one.  And, we are about to put that engine on the RSL.  However,
     * it is possible that the template in question was flushed due to a change notification
     * while this was going on.  Regardless of how we got the engine, there is the possibility
     * that the template was flushed while we were holding onto an engine which was not on
     * any list (the FSQ or RSL), and so we have an engine which should be flushed but isnt.
     * We can detect this by seeing if the template is marked as being a Zombie.  If it is
     * we must mark this engine as being a zombie too, so it wont be returned to the FSQ when
     * it is done running.  Note that once we add this engine to the RSL we are "safe", because
     * any flushes after that point would correctly zombify the engine.
     */
    EnterCriticalSection(&m_csRSL);    
    if (pTemplate->FIsZombie())
        {
        // The template asking for this engine is obsolete. Make sure that no
        // one else will use this engine by marking it zombie
        DBGPRINTF((DBG_CONTEXT, "[CScriptManager] Zombie template found.\n"));
        (*ppSE)->Zombify();
        }

	(VOID)m_htRSL.AddElem(pASEElem);
	LeaveCriticalSection(&m_csRSL);


	// Set the time that the engine was handed out so we will know when to kill it
	pASE->SetTimeStarted(time(NULL));

LFail:

	Assert(SUCCEEDED(hr) || hr == TYPE_E_ELEMENTNOTFOUND);
	return(hr);
	}

/*===================================================================
CScriptManager::ReturnEngineToCache

Caller is done with the engine.  Return it to the cache.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially allocates memory.
===================================================================*/
HRESULT CScriptManager::ReturnEngineToCache
(
CScriptEngine **ppSE,
CAppln *pAppln
)
{
	HRESULT hr = S_OK;
	CASEElem *pASEElem;
	CActiveScriptEngine *pASE;

	Assert(ppSE != NULL);
	Assert(*ppSE != NULL);

	pASE = static_cast<CActiveScriptEngine *>(*ppSE);
	
	// Remove the engine from the Running Script List
	EnterCriticalSection( &m_csRSL );
	hr = FindASEElemInList(static_cast<CActiveScriptEngine *>(*ppSE), /*fFSQ*/FALSE, &pASEElem);
	if (FAILED(hr)) {
		LeaveCriticalSection( &m_csRSL );
		goto LExit;
    }
		
	// Note: Sometimes a script will not be in the RSL!  This occurs when
	//       we are reusing a script that is stored in the CTemplate object.
	//       (When the script is reloaded, it is retrieved directly from the
	//        template, bypassing our code which places engines on the RSL)
	//
	if (pASEElem != NULL)
		m_htRSL.RemoveElem(pASEElem);

	LeaveCriticalSection( &m_csRSL );

	/*
	 * If the engine was zombified while it was running, deallocate it.
	 * Or, if there was a GPF while then engine was running, then it might
	 * be in a corrupted state (bug 1225).  Also remove it in that case.
	 */
	pASE = static_cast<CActiveScriptEngine *>(*ppSE);
	if (pASE->FIsZombie() || pASE->FIsCorrupted()) {
		delete pASEElem;
		pASE->FinalRelease();
		goto LExit;
    }

	HRESULT hrT;
	/*
	 * We want to reuse this engine.  Try to return it to the "Uninitialized"
	 * state.  Some engine languages arent able to do this.  If it fails, deallocate
	 * the engine; it cant be reused.
	 */
	hrT = pASE->ResetToUninitialized();
	if (FAILED(hrT)) {
		// Engine doesnt support this, sigh.  Deallocate and continue.
		delete pASEElem;
		pASE->FinalRelease();
		goto LExit;
    }

    // Get the pTemplate for this engine
	CTemplate *pTemplate;
	DWORD dwEngine;
	pASE->GetDebugDocument(&pTemplate, &dwEngine);

	// CONSIDER: Better strategy for keeping live scripts?
	// Only remember good (no compiler errors) scripts in the template
	if (pAppln->FDebuggable() && pASE->FFullyLoaded() && pTemplate && !pTemplate->FDontAttach()) {
		// Template is marked as incomplete (invalid) when change notification occurs
		// and template is flushed from cache.  In this case, don't cache in CTemplate
		// object!

		if (pTemplate->FIsValid())
			pTemplate->AddScript(dwEngine, pASE);

		// NOTE: Always release the scripting engine.  Exec code is structured so that it
		//       consumes a reference (either through GetEngine() or CTemplate::GetActiveScript())
		//       and assumes that ReturnToCache will release its reference.
		// CONSIDER: Bad design.  Caller should do the release

		delete pASEElem;
		pASE->Release();
    }
	else {
	    // reuse engines, not debugging
		/*
		 * We removed the engine from the RSL, put it onto the FSQ for potential reuse.
		 *
		 * In certain multi-threaded change-notify situations it is possible
		 * that the template was flushed (zombied) while we were in the middle
		 * of returning this engine to the cache.  That is to say, between the time
		 * that we took the engine off the RSL and when we are going to put it on the FSQ
		 * it might have been flushed. In that case, this engine should
		 * not go into the FSQ, but should be deleted instead.  Check for that case.
		 * Do that inside the FSQ CS so that we are safe from the template getting zombied
		 * after we do the test but before the engine goes into the FSQ.  Also, do not
		 * put template on FSQ during shut down phase, since FSQ may go away soon, and
		 * the final destination of the engine is FinalRelease() anyway.
		 */
    	EnterCriticalSection(&m_csFSQ);
    	if (!pTemplate->FIsZombie() && !IsShutDownInProgress()) {
    		AddToFSQ(pASEElem);
        }
    	else {
    		delete pASEElem;
    		pASE->FinalRelease();
        }
		LeaveCriticalSection(&m_csFSQ);
    }

LExit:
	return(hr);
}

/*===================================================================
CScriptManager::FlushCache

A script has been edited; cached versions must be discarded

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially allocates memory.
===================================================================*/
HRESULT CScriptManager::FlushCache
(
LPCTSTR szTemplateName
)
	{
	HRESULT hr = S_OK;
	CASEElem *pASEElem;
	CASEElem *pASEElemNext = NULL;
	CActiveScriptEngine *pASE;
	
	AssertValid();

	EnterCriticalSection(&m_csRSL);
	EnterCriticalSection(&m_csFSQ);

	// First Zombify engines on the RSL of the given name.
	// Note: must explicitly loop through all elements, since the hash table implementation
	//		doesnt support FindNext to find subsequent elements of the same name.  Repeated
	//		calls to find returns the same element over and over
	// CONSIDER: I have written a custom FindElem.  Consider using it.
	
	pASEElem = (CASEElem *)m_htRSL.Head();
	while (pASEElem != NULL)
		{
		pASEElemNext = (CASEElem *)pASEElem->m_pNext;
		pASE = pASEElem->PASE();

		if (_tcsicmp(pASE->SzTemplateName(), szTemplateName) == 0)
			pASE->Zombify();
		
		pASEElem = pASEElemNext;
		}


	// Now throw out engines on the FSQ of the given name
	// Delete any item with the given name (may be several)
	pASEElem = (CASEElem *)m_htFSQ.Head();
	while (pASEElem != NULL)
		{
		pASEElemNext = (CASEElem *)pASEElem->m_pNext;
		pASE = pASEElem->PASE();

		if (_tcsicmp(pASE->SzTemplateName(), szTemplateName) == 0)
			{
			(VOID)m_htFSQ.RemoveElem(pASEElem);
#ifndef PERF_DISABLE
            g_PerfData.Decr_SCRIPTFREEENG();
#endif
			pASE->FinalRelease();
			delete pASEElem;
			}
		
		pASEElem = pASEElemNext;
		}

	LeaveCriticalSection(&m_csFSQ);
	LeaveCriticalSection(&m_csRSL);

	return(hr);
	}

/*===================================================================
CScriptManager::FlushAll

global.asa changed, everything must go

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially allocates memory.
===================================================================*/
HRESULT CScriptManager::FlushAll
(
)
	{
	HRESULT hr = S_OK;
	CASEElem *pASEElem;
	CASEElem *pASEElemNext = NULL;
	CActiveScriptEngine *pASE;
	
	AssertValid();

	EnterCriticalSection(&m_csRSL);
	EnterCriticalSection(&m_csFSQ);

	// First Zombify all engines on the RSL
	// Note: must explicitly loop through all elements, since the hash table implementation
	//		doesnt support FindNext to find subsequent elements of the same name.  Repeated
	//		calls to find returns the same element over and over
	// CONSIDER: I have written a custom FindElem.  Consider using it.
	
	pASEElem = (CASEElem *)m_htRSL.Head();
	while (pASEElem != NULL)
		{
		pASEElemNext = (CASEElem *)pASEElem->m_pNext;
		pASEElem->PASE()->Zombify();
		pASEElem = pASEElemNext;
		}

	// Now throw out engines on the FSQ
	pASEElem = (CASEElem *)m_htFSQ.Head();
	while (pASEElem != NULL)
		{
		pASEElemNext = (CASEElem *)pASEElem->m_pNext;
		(VOID)m_htFSQ.RemoveElem(pASEElem);
#ifndef PERF_DISABLE
		g_PerfData.Decr_SCRIPTFREEENG();
#endif
		pASEElem->PASE()->FinalRelease();
		delete pASEElem;
		pASEElem = pASEElemNext;
		}

	LeaveCriticalSection(&m_csFSQ);
	LeaveCriticalSection(&m_csRSL);

	return(hr);
	}

/*===================================================================
CScriptManager::GetDebugScript

Try to find an engine via template pointer, and query for IActiveScriptDebug,
in the RSL.

Returns:
	An AddRef'ed copy of the script engine if found, or NULL if not.
===================================================================*/
IActiveScriptDebug *
CScriptManager::GetDebugScript
(
CTemplate *pTemplate,
DWORD dwSourceContext
)
	{
	EnterCriticalSection(&m_csRSL);

	CASEElem *pASEElem = static_cast<CASEElem *>(m_htRSL.Head());
	while (pASEElem != NULL)
		{
		CTemplate *pScriptTemplate = NULL;
		DWORD dwScriptSourceContext = -1;
		CActiveScriptEngine *pASE = pASEElem->PASE();
		pASE->GetDebugDocument(&pScriptTemplate, &dwScriptSourceContext);

		if (pTemplate == pScriptTemplate && dwSourceContext == dwScriptSourceContext)
			{
			IActiveScript *pActiveScript = pASE->GetActiveScript();
			void *pDebugScript;
			if (SUCCEEDED(pActiveScript->QueryInterface(IID_IActiveScriptDebug, &pDebugScript)))
				{
				pASE->IsBeingDebugged();
				LeaveCriticalSection(&m_csRSL);
				return reinterpret_cast<IActiveScriptDebug *>(pDebugScript);
				}
			else
				{
				LeaveCriticalSection(&m_csRSL);
				return NULL;
				}
			}

		pASEElem = static_cast<CASEElem *>(pASEElem->m_pNext);
		}

	LeaveCriticalSection(&m_csRSL);

	return NULL;
	}

/*===================================================================
CScriptManager::FindEngineInList

Try to find an engine of the given name in the given list (either
the FSQ or the RSL.)

Returns:
	HRESULT.  S_OK on success.
	ppASEElem contains found engine
===================================================================*/
HRESULT CScriptManager::FindEngineInList
(
LPCTSTR szTemplateName,	// Template we want an engine for
PROGLANG_ID progLangId,	// what language do we want this engine for
DWORD dwInstanceID,     // which server instance
BOOL fFSQ,				// TRUE -> look in FSQ, FALSE -> look in RSQ
CASEElem **ppASEElem
)
	{
	HRESULT hr = S_OK;
	DWORD cb;
	
	AssertValid();
	Assert(ppASEElem != NULL);

	*ppASEElem = NULL;
	
	// Key is name
	cb = _tcslen(szTemplateName)*sizeof(TCHAR);
	if (fFSQ)
		{
		EnterCriticalSection(&m_csFSQ);
		*ppASEElem = static_cast<CASEElem *>(m_htFSQ.FindElem((VOID *)szTemplateName, cb, 
											progLangId, dwInstanceID, /*fCheckLoaded*/TRUE));
		LeaveCriticalSection(&m_csFSQ);
		}
	else
		{
		EnterCriticalSection(&m_csRSL);
		*ppASEElem = static_cast<CASEElem *>(m_htRSL.FindElem((VOID *)szTemplateName, cb,
											progLangId, dwInstanceID, /*fCheckLoaded*/TRUE));
		LeaveCriticalSection(&m_csRSL);
		}

	return(hr);
	}

/*===================================================================
CScriptManager::FindASEElemInList

Given an ASE, find its corresponding ASEElem in the hash table. Note
that this is relatively slow because it is doing a linked list traversal
not a hash table lookup.  

CONSIDER: create second hash table to do this quickly.

Returns:
	HRESULT.  S_OK on success.
	ppASEElem contains found engine
===================================================================*/
HRESULT CScriptManager::FindASEElemInList
(
CActiveScriptEngine *pASE,
BOOL fFSQ,				// TRUE -> look in FSQ, FALSE -> look in RSQ
CASEElem **ppASEElem
)
	{
	HRESULT hr = S_OK;
	CASEElem *pASEElem;
	
	AssertValid();
	Assert(pASE != NULL);
	Assert(ppASEElem != NULL);

	*ppASEElem = NULL;
	
	if (fFSQ)
		{
		EnterCriticalSection(&m_csFSQ);
		pASEElem = static_cast<CASEElem *>(m_htFSQ.Head());
		}
	else
		{
		EnterCriticalSection(&m_csRSL);
		pASEElem = static_cast<CASEElem *>(m_htRSL.Head());
		}

	while (pASEElem != NULL)
		{
		if (pASE == pASEElem->PASE())
			break;
		pASEElem = static_cast<CASEElem *>(pASEElem->m_pNext);
		}

	if (fFSQ)
		LeaveCriticalSection(&m_csFSQ);
	else
		LeaveCriticalSection(&m_csRSL);

	*ppASEElem = pASEElem;
		
	return(hr);
	}

/*===================================================================
CScriptManager::KillOldEngines

Loops through all running engines and kills any engines which are "old"
(presumably they are stuck in an infinite loop in VBS.)

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially kills off engines
===================================================================*/
HRESULT CScriptManager::KillOldEngines
(
BOOLB fKillNow // Kill all engines now if TRUE
)
	{
	HRESULT hr = S_OK;
	CASEElem *pASEElem, *pASEElemNext;
	time_t timeNow;
	time_t timeRunning;
	CActiveScriptEngine *pASE;
	
	AssertValid();

	timeNow = time(NULL);

	EnterCriticalSection(&m_csRSL);
	
	pASEElemNext = static_cast<CASEElem *>(m_htRSL.Head());

	/*
	 * Loop through each element.  Turn it into an ASE.
	 * If it is older than cSeconds, then kill it.
	 */
	while (pASEElemNext)
		{
		pASEElem = pASEElemNext;
		pASEElemNext = static_cast<CASEElem *>(pASEElemNext->m_pNext);
		pASE = pASEElem->PASE();

		timeRunning = timeNow - pASE->TimeStarted();

		if (TRUE == fKillNow || timeRunning >= pASE->GetTimeout())
			{
			// Too old. Kill it.
			pASE->InterruptScript();
			}
		}
	
	LeaveCriticalSection(&m_csRSL);

	return(hr);
	}

/*===================================================================
CScriptManager::EmptyRunningScriptList

When we are going to shut down, the RSL must be empty.  This routine
kills off all running engines, then waits up to 5 minutes
for the engines to leave the RSL.  Added for Bug 1140

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Potentially kills off engines
===================================================================*/
HRESULT CScriptManager::EmptyRunningScriptList
(
)
	{
	HRESULT hr;
	UINT cTrys;

	hr = KillOldEngines(TRUE);
	Assert(SUCCEEDED(hr));
	for (cTrys = 0; cTrys < 300; cTrys++)
		{
		if (static_cast<CASEElem *>(m_htRSL.Head()) == NULL)
			break;
		Sleep(1000);			// sleep 1 seconds
		}

	return(S_OK);
	}

/*===================================================================
CScriptManager::UnInitASEElems

Free engines in FSQ and RSL

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Frees memory
===================================================================*/
HRESULT CScriptManager::UnInitASEElems()
	{
	CASEElem *pASEElem = NULL;
	CASEElem *pASEElemNext = NULL;

	// First the FSQ
	EnterCriticalSection(&m_csFSQ);
	pASEElem = static_cast<CASEElem *>(m_htFSQ.Head());
	while (pASEElem != NULL)
		{
		pASEElemNext = static_cast<CASEElem *>(pASEElem->m_pNext);
		pASEElem->PASE()->FinalRelease();
		delete pASEElem;
		pASEElem = pASEElemNext;
		}
	LeaveCriticalSection(&m_csFSQ);

	/*
	 * Next the RSL (note: this really should be empty)
	 *
	 * Bug 1140: This is very dangerous, but we have no choice left at this point
	 */
	EnterCriticalSection(&m_csRSL);
	pASEElem = static_cast<CASEElem *>(m_htRSL.Head());
	while (pASEElem != NULL)
		{
		pASEElemNext = static_cast<CASEElem *>(pASEElem->m_pNext);
		pASEElem->PASE()->FinalRelease();
		delete pASEElem;
		pASEElem = pASEElemNext;
		}
	LeaveCriticalSection(&m_csRSL);
	
	return(S_OK);
	}

/*===================================================================
CScriptManager::AddToFSQ

Add the given ASEElem to the FSQ and to the front of the LRU list

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::AddToFSQ
(
CASEElem *pASEElem
)
	{
	HRESULT hr = S_OK;

	Assert(pASEElem != NULL);

	// If CacheMax is 0, this is a NoOp
	if (Glob(dwScriptEngineCacheMax) <= 0)
		{
		// delete the passed in ASEElem because it wont be saved
		pASEElem->PASE()->FinalRelease();
		delete pASEElem;

		return(S_OK);
		}

	EnterCriticalSection(&m_csFSQ);

	// Add the element to the FSQ
	(VOID)m_htFSQ.AddElem(pASEElem);

#ifndef PERF_DISABLE
    g_PerfData.Incr_SCRIPTFREEENG();
#endif

	// Check the FSQ LRU too see if it is too long
	CheckFSQLRU();

	LeaveCriticalSection(&m_csFSQ);

	return(hr);
	}

/*===================================================================
CScriptManager::CheckFSQLRU

Check to see if the FSQ is too long, and if so throw out the LRU engine

WARNING: Caller must enter FSQ critical section before calling

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::CheckFSQLRU()
	{
	HRESULT hr = S_OK;
	CASEElem *pASEElemOld;
	CActiveScriptEngine *pASE;

	// If the list isnt too long, noop
	if (m_htFSQ.Count() <= Glob(dwScriptEngineCacheMax) || Glob(dwScriptEngineCacheMax) == 0xFFFFFFFF)
		return(S_OK);

	// FSQLRU list is too long, remove oldest
	Assert (! m_htFSQ.FLruElemIsEmpty( m_htFSQ.End() ));
	pASEElemOld = static_cast<CASEElem *>(m_htFSQ.RemoveElem(m_htFSQ.End()));
	Assert(pASEElemOld != NULL);
	pASE = pASEElemOld->PASE();

#ifndef PERF_DISABLE
    g_PerfData.Decr_SCRIPTFREEENG();
#endif

	// Delete the engine
	delete pASEElemOld;
	pASE->FinalRelease();

	return(hr);
	}

/*===================================================================
CScriptManager::UnInitPLL

Free the names of the script engines

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Frees memory
===================================================================*/
HRESULT CScriptManager::UnInitPLL()
	{
	CPLLElem *pPLLElem = NULL;
	CPLLElem *pPLLElemNext = NULL;

	pPLLElem = (CPLLElem *)m_hTPLL.Head();

	while (pPLLElem != NULL)
		{
		pPLLElemNext = (CPLLElem *)pPLLElem->m_pNext;
		if (pPLLElem->m_pKey != NULL)
			free((CHAR *)(pPLLElem->m_pKey));
		pPLLElem->m_pKey = NULL;
		delete pPLLElem;
		pPLLElem = pPLLElemNext;
		}
	
	return(S_OK);
	}

/*===================================================================
CScriptManager::ProgLangIdOfLangName

Given a programming language name, get the CLSID of the ActiveX Scripting
Engine which runs that language.

WARNING: Needs to look in the registry for this info.  Maybe slow

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::ProgLangIdOfLangName
(
LPCSTR szProgLang,			// The programming lang of the script
PROGLANG_ID *pProgLangId	// The programming language id
)
	{
	HRESULT hr = S_OK;
	CPLLElem *pPLLElem;
	
	AssertValid();
	
	EnterCriticalSection(&m_cSPLL);
	pPLLElem = (CPLLElem *) m_hTPLL.FindElem((VOID *)szProgLang, strlen(szProgLang));
	if (pPLLElem != NULL)
		{
		*pProgLangId = pPLLElem->ProgLangId();
		}
	else
		{
		// Not already in list, look in registry
		hr = GetProgLangIdOfName(szProgLang, pProgLangId);
		if (FAILED(hr))
			{
			hr = TYPE_E_ELEMENTNOTFOUND;
			goto LExit;
			}

		// Add it to the list so we dont have to re-look it up
		hr = AddProgLangToPLL((CHAR *)szProgLang, *pProgLangId);
		if (FAILED(hr))
			goto LExit;
		}

LExit:
	LeaveCriticalSection(&m_cSPLL);
	return(hr);
	}

/*===================================================================
CScriptManager::AddProgLangToPLL

Keep list of programming language CLSIDs so we dont have to look
them up every time.  Add the given programming language name/id pair
to the Programming Language List.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CScriptManager::AddProgLangToPLL
(
CHAR *szProgLangName,
PROGLANG_ID progLangId
)
	{
	HRESULT hr;
	CPLLElem *pPLLElem = NULL;

	// Put the language clsid on the Programming Language List
	pPLLElem = new CPLLElem;
	if (!pPLLElem)
		{
		hr = E_OUTOFMEMORY;
		goto LFail;
		}

	hr = pPLLElem->Init(szProgLangName, progLangId);
	if (FAILED(hr))
		{
		Assert(FALSE);		// Shouldnt fail
		goto LFail;
		}

	EnterCriticalSection(&m_cSPLL);
	(VOID)m_hTPLL.AddElem(pPLLElem);
	LeaveCriticalSection(&m_cSPLL);

LFail:
	return(hr);
	}

/*===================================================================
CScriptManager::ScriptKillerSchedulerCallback

Static method implements ATQ scheduler callback functions.
Replaces script killer thread 

Parameters:
    void *pv    context pointer (points to script mgr)
    
Returns:

Side effects:
	None.
===================================================================*/
void WINAPI CScriptManager::ScriptKillerSchedulerCallback
(
void *pv
)
    {
    if (IsShutDownInProgress())
        return;

    Assert(pv);
    
    CScriptManager *pScriptMgr = reinterpret_cast<CScriptManager *>(pv);
    if (pScriptMgr->m_fInited)
        {
        pScriptMgr->KillOldEngines();
        }
    }

#ifdef DBG
/*===================================================================
CScriptManager::AssertValid

Test to make sure that the CScriptManager object is currently correctly formed
and assert if it is not.

Returns:

Side effects:
	None.
===================================================================*/
VOID CScriptManager::AssertValid() const
	{
	Assert(m_fInited);
	}
#endif // DBG



/*
 *
 *
 *
 * C A S E E l e m
 *
 * Active Script Engine Elements
 *
 *
 *
 */ 

/*===================================================================
CASEElem::~CASEElem

Destructor for CASEElem object.

Returns:
	Nothing

Side effects:
	None
===================================================================*/
CASEElem::~CASEElem()
	{
	}

/*===================================================================
CASEElem::Init

Init the Active Script Engine Elem.  This must only be called once.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT CASEElem::Init
(
CActiveScriptEngine *pASE
)
	{
	HRESULT hr = S_OK;
	TCHAR *szT = pASE->SzTemplateName();

	if (szT == NULL)
		{
		Assert(FALSE);
		return(E_FAIL);
		}

	// Key is name
	hr = CLinkElem::Init((LPVOID) szT, _tcslen(szT)*sizeof(TCHAR));
	if (FAILED(hr))
		{
		Assert(FALSE);		// Shouldnt fail
		return(hr);
		}

	m_pASE = pASE;

	return(hr);
	}




/*
 *
 *
 *
 * C P L L E l e m
 *
 * Programming Language List Element
 *
 *
 *
 *
 */ 

/*===================================================================
CPLLElem::~CPLLElem

Destructor for CPLLElem object.

Returns:
	Nothing

Side effects:
	Deallocates memory
===================================================================*/
CPLLElem::~CPLLElem()
	{
	CHAR *szT;

	// Free the memory allocated for the key string
	szT = (CHAR *)m_pKey;
	if (szT != NULL)
		free(szT);
	m_pKey = NULL;
	}

/*===================================================================
CPLLElem::Init

Init the Prog Lang Elem.  This must only be called once.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Allocates memory
===================================================================*/
HRESULT CPLLElem::Init
(
CHAR *szProgLangName,
PROGLANG_ID progLangId
)
	{
	HRESULT hr = S_OK;
	CHAR *szT;
	UINT cch;

	if (szProgLangName == NULL)
		{
		Assert(FALSE);
		return(E_FAIL);
		}
		
	cch = strlen(szProgLangName);
	szT = (CHAR *)malloc(cch+1);
	if (!szT)
		{
		return(E_OUTOFMEMORY);
		}
	strcpy(szT, szProgLangName);
	hr = CLinkElem::Init((LPVOID) szT, cch);
	if (FAILED(hr))
		{
		Assert(FALSE);		// Shouldnt fail
		free(szT);
		return(hr);
		}

	m_ProgLangId = progLangId;

	return(hr);
	}


/*===================================================================
GetProgLangIdOfName

Given the name of a programming language, get its programming
language Id from the registry.

Returns:
	HRESULT.  S_OK on success.

Side effects:
	None.
===================================================================*/
HRESULT GetProgLangIdOfName
(
LPCSTR szProgLangName,
PROGLANG_ID *pProgLangId
)
	{
	HRESULT hr = S_OK;
	LONG lT;
	HKEY hkeyRoot, hkeyCLSID;
	DWORD dwType;
	CLSID clsid;
	CHAR szClsid[40];
	DWORD cbData;
	LPOLESTR strClsid;
    CMBCSToWChar    convStr;
	
	// The programming language id is really the CLSID of the scripting engine
	// It is in the registry under HKEY_CLASSES_ROOT.  Under the script name,
	// there is a key for "CLSID".  The CLSID is a value under the 
	// engine name.  E.g. \HKEY_CLASSES_ROOT\VBScript\CLSID
	lT = RegOpenKeyExA(HKEY_CLASSES_ROOT, szProgLangName, 0,
						KEY_READ, &hkeyRoot);
	if (lT != ERROR_SUCCESS)
		return(HRESULT_FROM_WIN32(lT));
	lT = RegOpenKeyExA(hkeyRoot, "CLSID", 0,
						KEY_READ, &hkeyCLSID);
	RegCloseKey(hkeyRoot);
	if (lT != ERROR_SUCCESS)
		return(HRESULT_FROM_WIN32(lT));

	cbData = sizeof(szClsid);
	lT = RegQueryValueExA(hkeyCLSID, NULL, 0, &dwType, (BYTE *)szClsid, &cbData);
	if (lT != ERROR_SUCCESS)
		{
		hr = HRESULT_FROM_WIN32(lT);
		goto lExit;
		}
	Assert(cbData <= sizeof(szClsid));

	// What we got back was the GUID as a string (e.g. {089999-444....}). Convert to a CLSID

    convStr.Init(szClsid);
	strClsid = convStr.GetString();
	hr = CLSIDFromString(strClsid, &clsid);

	*pProgLangId = clsid;

lExit:
	RegCloseKey(hkeyCLSID);

	return(hr);
	}




/*
 *
 *
 *
 * C S c r i p t i n g N a m e s p a c e
 *
 * Scripting namespace object
 *
 *
 *
 */ 

/*===================================================================
CScriptingNamespace::CScriptingNamespace

Constructor for CScriptingNamespace object.

Returns:
	Nothing

Side effects:
	None
===================================================================*/
CScriptingNamespace::CScriptingNamespace()
	: m_fInited(FALSE), m_cRef(1), m_cEngDispMac(0) 
	{
	}

/*===================================================================
CScriptingNamespace::~CScriptingNamespace

Destructor for CScriptingNamespace object.

Returns:
	Nothing

Side effects:
	Deallocates memory
===================================================================*/
CScriptingNamespace::~CScriptingNamespace()
	{
	UnInit();
	}

/*===================================================================
CScriptingNamespace::Init

Init the CScriptingNamespace object.

Returns:
	S_OK on success
===================================================================*/
HRESULT CScriptingNamespace::Init()
	{
	Assert(m_fInited == FALSE);
	
	m_fInited = TRUE;

	AssertValid();

	return(S_OK);
	}

/*===================================================================
CScriptingNamespace::UnInit

Free the script engine dispatch's

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Frees memory
===================================================================*/
HRESULT CScriptingNamespace::UnInit()
	{
    static const char *_pFuncName = "CScriptingNamespace::UnInit()";
	CEngineDispElem *pElem = NULL;
	ENGDISPBUCKET *pBucket = NULL;

	if (!m_fInited)
		return(S_OK);
		
	while (!m_listSE.FIsEmpty())
		{
		pElem = static_cast<CEngineDispElem *>(m_listSE.PNext());
		TRYCATCH_NOHITOBJ(pElem->m_pDisp->Release(),"IScriptDispatch::Release()");
        if (pElem->m_pDispEx) {
		        TRYCATCH_NOHITOBJ(pElem->m_pDispEx->Release(),"IScriptDispatchEx::Release()");
        }
		delete pElem;
		}

    while (!m_listEngDisp.FIsEmpty())
        {
        pBucket = static_cast<ENGDISPBUCKET *>(m_listEngDisp.PNext());
        delete pBucket;
        }
		
	m_cEngDispMac = 0;
	m_fInited = FALSE;

	return(S_OK);
	}

/*===================================================================
CScriptingNamespace::ReInit

Reinit the scripting namespace object

Returns:
	HRESULT.  S_OK on success.

Side effects:
	Frees memory
===================================================================*/
HRESULT CScriptingNamespace::ReInit()
	{
	HRESULT hr;

	hr = UnInit();
	if (SUCCEEDED(hr))
		hr = Init();

	return(hr);
	}

/*===================================================================
CScriptingNamespace::AddEngineToNamespace

Add an engine to the list of engines

Returns:
	S_OK on success
===================================================================*/
HRESULT CScriptingNamespace::AddEngineToNamespace(CActiveScriptEngine *pASE)
	{
    static const char *_pFuncName = "CScriptingNamespace::AddEngineToNamespace()";
	HRESULT hr;
	IDispatch *pDisp = NULL;
	CEngineDispElem *pElem;

	AssertValid();
	Assert(pASE != NULL);
	pASE->AssertValid();

    TRYCATCH_HR_NOHITOBJ(pASE->GetActiveScript()->GetScriptDispatch(NULL, &pDisp), 
                         hr,
                         "IActiveScript::GetScriptDispatch()");	// FYI - does addref

	if (FAILED(hr))
		{
		goto LFail;
		}
	else
		if (pDisp == NULL)
			{
			hr = E_FAIL;
			goto LFail;
			}

	// Add the engine to the engine hash table.
	pElem = new CEngineDispElem;
	if (pElem == NULL)
		{
		hr = E_OUTOFMEMORY;
		goto LFail;
		}
	pElem->m_pDisp = pDisp;
    pElem->m_pDispEx = NULL;

    // QI for IDispatchEx if available
    TRYCATCH_NOHITOBJ(pDisp->QueryInterface(IID_IDispatchEx, (void **)&pElem->m_pDispEx),"IScriptDispatch::QueryInterface()");
	
	pElem->AppendTo(m_listSE);

	return(S_OK);
	
LFail:
    if (pDisp) {
		TRYCATCH_NOHITOBJ(pDisp->Release(),"IScriptDispatch::Release()");
    }
	return(hr);
	}

/*===================================================================
CScriptingNamespace::QueryInterface
CScriptingNamespace::AddRef
CScriptingNamespace::Release

IUnknown members for CScriptingNamespace object.
===================================================================*/
STDMETHODIMP CScriptingNamespace::QueryInterface(REFIID iid, void **ppvObj)
	{
	AssertValid();

	if (iid == IID_IUnknown || iid == IID_IDispatch || iid == IID_IDispatchEx)
	    {
		*ppvObj = this;
        AddRef();
		return S_OK;
		}

	*ppvObj = NULL;
	return E_NOINTERFACE;
	}

STDMETHODIMP_(ULONG) CScriptingNamespace::AddRef(void)
	{
	AssertValid();

	return ++m_cRef;
	}

STDMETHODIMP_(ULONG) CScriptingNamespace::Release(void)
	{
	if (--m_cRef > 0)
		return m_cRef;

    delete this;
	return 0;
	}

/*===================================================================
CScriptingNamespace::GetTypeInfoCount

We have no typeinfo, so 0.

Parameters:
	pcInfo		UINT * to the location to receive
				the count of interfaces.

Return Value:
	HRESULT		 S_OK or a general error code.
===================================================================*/
STDMETHODIMP CScriptingNamespace::GetTypeInfoCount(UINT *pcInfo)
	{
	AssertValid();
	
	*pcInfo = 0;
	return S_OK;
	}

/*===================================================================
CScriptingNamespace::GetTypeInfo

We dont have a typeinfo

Parameters:
	itInfo			UINT reserved.	Must be zero.
	lcid			LCID providing the locale for the type
					information.	If the object does not support
					localization, this is ignored.
	ppITypeInfo		ITypeInfo ** in which to store the ITypeInfo
					interface for the object.

Return Value:
	HRESULT			S_OK or a general error code.
===================================================================*/
STDMETHODIMP CScriptingNamespace::GetTypeInfo
(
UINT itInfo,
LCID lcid,
ITypeInfo **ppITypeInfo
)
	{
	AssertValid();

	*ppITypeInfo = NULL;
	return(E_NOTIMPL);
	}

/*===================================================================
CScriptingNamespace::GetIDsOfNames

Looks through all the engines we know about, calling GetIdsOfNames on
them till we find the requested name.

Parameters:
	riid			REFIID reserved. Must be IID_NULL.
	rgszNames		OLECHAR ** pointing to the array of names to be mapped.
	cNames			UINT number of names to be mapped.
	lcid			LCID of the locale.
	rgDispID		DISPID * caller allocated array containing IDs
					corresponging to those names in rgszNames.

Return Value:
	HRESULT		 S_OK or a general error code.
===================================================================*/
STDMETHODIMP CScriptingNamespace::GetIDsOfNames
(
REFIID riid,
OLECHAR **rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispID
)
	{
    static const char *_pFuncName = "CScriptingNamespace::GetIDsOfNames()";
	HRESULT hr;
	CEngineDispElem *pElem;

	AssertValid();

	if (IID_NULL != riid)
		return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

	/*
	 * Loop through the engines we know about until we find the one that has the requested name
	 * (or hit the end of the list, in which case it is not found)
	 */
	for (pElem = static_cast<CEngineDispElem *>(m_listSE.PNext());
		 pElem != &m_listSE;
		 pElem = static_cast<CEngineDispElem *>(pElem->PNext()))
		{
		Assert(pElem->m_pDisp != NULL);
		
		    TRYCATCH_HR_NOHITOBJ(pElem->m_pDisp->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispID), 
                                 hr,
                                 "IScriptDispatch::GetIDsOfNames()");
		
		if (SUCCEEDED(hr))
		    {
            return CacheDispID(pElem, rgDispID[0], rgDispID);
			}
		}

	return DISP_E_UNKNOWNNAME;
	}

/*===================================================================
CScriptingNamespace::Invoke

Map the dispID to the correct engine, and pass the invoke on to that
engine.

Parameters:
	dispID			DISPID of the method or property of interest.
	riid			REFIID reserved, must be IID_NULL.
	lcid			LCID of the locale.
	wFlags			USHORT describing the context of the invocation.
	pDispParams		DISPPARAMS * to the array of arguments.
	pVarResult		VARIANT * in which to store the result.	Is
					NULL if the caller is not interested.
	pExcepInfo		EXCEPINFO * to exception information.
	puArgErr		UINT * in which to store the index of an
					invalid parameter if DISP_E_TYPEMISMATCH
					is returned.

Return Value:
	HRESULT		 S_OK or a general error code.
===================================================================*/
STDMETHODIMP CScriptingNamespace::Invoke
(
DISPID dispID,
REFIID riid,
LCID lcid,
unsigned short wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *puArgErr
)
	{
    static const char *_pFuncName = "CScriptingNamespace::Invoke()";
	HRESULT hr;
    ENGDISP *pEngDisp;

	AssertValid();
	
	// riid is supposed to be IID_NULL always
	if (IID_NULL != riid)
		return ResultFromScode(DISP_E_UNKNOWNINTERFACE);

    // navigate to the correct ENGDISP structure 
    hr = FetchDispID(dispID, &pEngDisp);
    if (FAILED(hr))
        return hr;
	
	Assert(pEngDisp->pDisp != NULL);
	
    // invoke
	TRYCATCH_HR_NOHITOBJ(pEngDisp->pDisp->Invoke
	                        (
	                        pEngDisp->dispid, 
	                        riid, 
	                        lcid,
		                    wFlags, 
		                    pDispParams, 
		                    pVarResult, 
		                    pExcepInfo, 
		                    puArgErr
		                    ),
                         hr,
                         "IScriptDispatch::Invoke()");
	
	return hr;
	}

/*===================================================================
CScriptingNamespace::  IDispatchEx implementation stubs
===================================================================*/
STDMETHODIMP CScriptingNamespace::DeleteMemberByDispID(DISPID id)
    {
    return E_NOTIMPL;
    }
    
STDMETHODIMP CScriptingNamespace::DeleteMemberByName(BSTR bstrName, DWORD grfdex)
    {
    return E_NOTIMPL;
    }
    
STDMETHODIMP CScriptingNamespace::GetMemberName(DISPID id, BSTR *pbstrName)
    {
    return E_NOTIMPL;
    }
    
STDMETHODIMP CScriptingNamespace::GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
    {
    return E_NOTIMPL;
    }
    
STDMETHODIMP CScriptingNamespace::GetNameSpaceParent(IUnknown **ppunk)
    {
    return E_NOTIMPL;
    }
    
STDMETHODIMP CScriptingNamespace::GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid)
    {
    return E_NOTIMPL;
    }
    
/*===================================================================
CScriptingNamespace::GetDispID

IDispatchEx replacement for GetIDsOfNames
===================================================================*/
STDMETHODIMP CScriptingNamespace::GetDispID
(
BSTR bstrName, 
DWORD grfdex, 
DISPID *pid
)
    {
    static const char *_pFuncName = "CScriptingNamespace::GetDispID()";
    HRESULT hr;
	CEngineDispElem *pElem = NULL;
    grfdex &= ~fdexNameEnsure;  // engines shouldn't create new names

    // Try IDispatchEx for all engines that have it
    
	for (pElem = static_cast<CEngineDispElem *>(m_listSE.PNext());
		 pElem != &m_listSE;
		 pElem = static_cast<CEngineDispElem *>(pElem->PNext()))
		{
		if (pElem->m_pDispEx != NULL)
		    {
		    TRYCATCH_HR_NOHITOBJ(pElem->m_pDispEx->GetDispID(bstrName, grfdex, pid), 
                                 hr,
                                 "IScriptDispatchEx::GetDispID()");
		    
    		if (SUCCEEDED(hr))
	    	    {
                return CacheDispID(pElem, *pid, pid);
			    }
		    }
		}

    // Try IDispatch for engines that don't have IDispatchEx
	for (pElem = static_cast<CEngineDispElem *>(m_listSE.PNext());
		 pElem != &m_listSE;
		 pElem = static_cast<CEngineDispElem *>(pElem->PNext()))
		{
		if (pElem->m_pDispEx == NULL)
		    {
    		Assert(pElem->m_pDisp != NULL);
    		TRYCATCH_HR_NOHITOBJ(pElem->m_pDisp->GetIDsOfNames
            		                (
    	        	                IID_NULL,
    		                        &bstrName,
    		                        1,
    		                        LOCALE_SYSTEM_DEFAULT,
    		                        pid
    		                        ),
                                 hr,
                                 "IScriptDispatch::GetIDsOfNames()");
    		    
    		if (SUCCEEDED(hr))
	    	    {
                return CacheDispID(pElem, *pid, pid);
			    }
		    }
		}
	
	return DISP_E_UNKNOWNNAME;
    }
    
/*===================================================================
CScriptingNamespace::Invoke

IDispatchEx replacement for Invoke
===================================================================*/
STDMETHODIMP CScriptingNamespace::InvokeEx
(
DISPID id, 
LCID lcid, 
WORD wFlags, 
DISPPARAMS *pdp,
VARIANT *pVarRes,    
EXCEPINFO *pei,    
IServiceProvider *pspCaller 
)
    {
    static const char *_pFuncName = "CScriptingNamespace::InvokeEx()";
	HRESULT hr;
    ENGDISP *pEngDisp;

    // navigate to the correct ENGDISP structure 
    hr = FetchDispID(id, &pEngDisp);
    if (FAILED(hr))
        return hr;
	
    if (pEngDisp->pDispEx != NULL)
        {
        // InvokeEx if the engine supports IDispatchEx
        
    	TRYCATCH_HR_NOHITOBJ(pEngDisp->pDispEx->InvokeEx
    	                            (
    	                            pEngDisp->dispid, 
                                    lcid, 
                                    wFlags, 
                                    pdp,
                                    pVarRes,    
                                    pei,    
                                    pspCaller 
    	                            ),
                             hr,
                             "IScriptDispatchEx::InvokeEx()");
        }
    else
        {
        // use IDispatch::Invoke if the engine doesn't support IDispatchEx
    	Assert(pEngDisp->pDisp != NULL);

    	UINT uArgErr;
        
    	TRYCATCH_HR_NOHITOBJ(pEngDisp->pDisp->Invoke
    	                            (
    	                            pEngDisp->dispid, 
    	                            IID_NULL, 
    	                            lcid,
    		                        wFlags, 
    		                        pdp, 
    		                        pVarRes, 
    		                        pei, 
    		                        &uArgErr
    		                        ),
                             hr,
                             "IScriptDispatch::Invoke()");
	    }
	
	return hr;
    }

/*===================================================================
CScriptingNamespace::CacheDispID

Adds new DISPID to the list

Parameters
    pEngine         -- engine for which disp id found
    dispidEngine    -- found dispid
    pdispidCached   -- [out] cached dispid (for ScriptingNamespace)

Returns
    HRESULT    
===================================================================*/
HRESULT CScriptingNamespace::CacheDispID
(
CEngineDispElem *pEngine,
DISPID dispidEngine,
DISPID *pdispidCached
)
	{
	ENGDISPBUCKET *pEngDispBucket;
	
	// See if we need to add another bucket
	if ((m_cEngDispMac % ENGDISPMAX) == 0)
		{
		pEngDispBucket = new ENGDISPBUCKET;
		if (pEngDispBucket == NULL)
			return E_OUTOFMEMORY;

		pEngDispBucket->AppendTo(m_listEngDisp);
		}

    // Navigate to the correct bucket
	unsigned iEngDisp = m_cEngDispMac;
	pEngDispBucket = static_cast<ENGDISPBUCKET *>(m_listEngDisp.PNext());
	while (iEngDisp > ENGDISPMAX)
		{
		iEngDisp -= ENGDISPMAX;
		pEngDispBucket = static_cast<ENGDISPBUCKET *>(pEngDispBucket->PNext());
		}

	pEngDispBucket->rgEngDisp[iEngDisp].dispid  = dispidEngine;
	pEngDispBucket->rgEngDisp[iEngDisp].pDisp   = pEngine->m_pDisp;
	pEngDispBucket->rgEngDisp[iEngDisp].pDispEx = pEngine->m_pDispEx;

	// Return index as the dispid
	*pdispidCached = (DISPID)m_cEngDispMac;
	m_cEngDispMac++;
	return S_OK;
	}
    
/*===================================================================
CScriptingNamespace::FetchDispID

Find ENGDISP by DISPID

Parameters
    dispid      - in
    ppEngDisp   - out

Returns
    HRESULT    
===================================================================*/
HRESULT CScriptingNamespace::FetchDispID
(
DISPID dispid, 
ENGDISP **ppEngDisp
)
    {
	if (dispid >= (DISPID)m_cEngDispMac)
	    return E_FAIL;

	unsigned iEngDisp = dispid;
	ENGDISPBUCKET *pEngDispBucket = static_cast<ENGDISPBUCKET *>(m_listEngDisp.PNext());
	while (iEngDisp > ENGDISPMAX)
		{
		iEngDisp -= ENGDISPMAX;
		pEngDispBucket = static_cast<ENGDISPBUCKET *>(pEngDispBucket->PNext());
		}

    *ppEngDisp = &pEngDispBucket->rgEngDisp[iEngDisp];
    return S_OK;
    }
    

#ifdef DBG
/*===================================================================
CScriptingNamespace::AssertValid

Test to make sure that the CScriptingNamespace object is currently correctly formed
and assert if it is not.

Returns:

Side effects:
	None.
===================================================================*/
VOID CScriptingNamespace::AssertValid() const
	{
	Assert(m_fInited);
	Assert(m_cRef > 0);
	}
	
#endif // DBG



/*
 *
 *
 * U t i l i t i e s
 *
 * General utility functions
 *
 */



/*===================================================================
WrapTypeLibs

Utility routine to take an array of Typelibs, and return an IDispatch
implementation that wraps the array of typelibs.

Parameters:
    ITypeLib **prgpTypeLib  - pointer to an array of typelibs
    UINT cTypeLibs          - count of typelibs in array
    IDispatch **ppDisp      - returned IDispatch

Return Value:
	HRESULT		 S_OK or a general error code.
===================================================================*/
HRESULT WrapTypeLibs
(
ITypeLib **prgpTypeLib,
UINT cTypeLibs,
IDispatch **ppDisp
)
	{
	HRESULT hr;
	Assert(g_pWrapTypelibs != NULL);
	Assert(prgpTypeLib != NULL);
	Assert(cTypeLibs > 0);
	Assert(ppDisp != NULL);

    hr = g_pWrapTypelibs->WrapTypeLib(prgpTypeLib, cTypeLibs, ppDisp);
    
	return(hr);
	}