You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2357 lines
66 KiB
2357 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bizrule.cxx
|
|
|
|
Abstract:
|
|
|
|
Routines implementing Business Rules
|
|
|
|
Author:
|
|
|
|
IAcativeScript sample code taken from http://support.microsoft.com/support/kb/articles/Q183/6/98.ASP
|
|
and from from \nt\inetsrv\iis\svcs\cmp\asp as originally written by AndrewS
|
|
|
|
Cliff Van Dyke (cliffv) 18-July-2001
|
|
|
|
--*/
|
|
|
|
#include "pch.hxx"
|
|
#define AZD_COMPONENT AZD_SCRIPT
|
|
|
|
//
|
|
// Performance measurement globals
|
|
//
|
|
#define ENABLE_PERF 1
|
|
#ifdef ENABLE_PERF
|
|
LONG AzGlPerfEngineCacheTries;
|
|
LONG AzGlPerfEnginesInFreeScriptList;
|
|
LONG AzGlPerfEngineCacheHits;
|
|
LONG AzGlPerfEngineFlushes;
|
|
LONG AzGlPerfRunningEngineCacheHits;
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// 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
|
|
//
|
|
// 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.
|
|
//*****************************************************************************
|
|
// ??? We could log it here
|
|
|
|
#if DBG
|
|
#define START_TRYCATCH( _IFStr ) { \
|
|
AzPrint((AZD_SCRIPT_MORE, "Calling: %s\n", _IFStr ));
|
|
#define END_TRYCATCH(_hr, _IFStr) }
|
|
#else // DBG
|
|
|
|
#define START_TRYCATCH( _IFStr ) __try {
|
|
#define END_TRYCATCH(_hr, _IFStr) \
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) { \
|
|
_hr = GetExceptionCode(); \
|
|
AzPrint((AZD_CRITICAL, "Exception: %s: 0x%lx\n", _IFStr, _hr )); \
|
|
}
|
|
#endif // DBG
|
|
|
|
//*****************************************************************************
|
|
// Definition of TRYCATCH_INT which is used by all of the TRYCATCH macros
|
|
// described above.
|
|
//*****************************************************************************
|
|
|
|
#define TRYCATCH_INT(_s, _hr, _IFStr) \
|
|
START_TRYCATCH( _IFStr ) \
|
|
TEST_THROW_ERROR \
|
|
_hr = _s; \
|
|
END_TRYCATCH(_hr, _IFStr)
|
|
|
|
//*****************************************************************************
|
|
// Here are the actual definitions of the TRYCATCH macros described above.
|
|
//*****************************************************************************
|
|
|
|
#define TRYCATCH(_s, _IFStr) \
|
|
{ \
|
|
HRESULT _tempHR; \
|
|
TRYCATCH_INT(_s, _tempHR, _IFStr); \
|
|
}
|
|
#define TRYCATCH_HR(_s, _hr, _IFStr) TRYCATCH_INT(_s, _hr, _IFStr)
|
|
|
|
//
|
|
// Name of the IAzBizRuleContext interface
|
|
//
|
|
|
|
#define BIZRULE_CONTEXT_INTERFACE_NAME L"AzBizRuleContext"
|
|
|
|
|
|
HRESULT
|
|
AzpGetScriptEngine(
|
|
IN PAZP_TASK Task,
|
|
OUT CScriptEngine ** RetScriptEngine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a script engine to run the bizrule task.
|
|
|
|
Ideally, we will find an engine in our FreeScript list
|
|
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.
|
|
|
|
Arguments:
|
|
|
|
Task - Task containing the BizRule to process
|
|
|
|
RetScriptEngine - Returns a pointer to the script engine to use
|
|
The caller should free the engine by calling AzpReturnEngineToCache.
|
|
|
|
Return Value:
|
|
|
|
S_OK: a script engine was successfully returned
|
|
OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
|
|
|
|
CScriptEngine *ScriptEngine = NULL;
|
|
IActiveScript *ClonedActiveScript = NULL;
|
|
DWORD ClonedBizRuleSerialNumber = 0;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ELEMENT ListElement;
|
|
|
|
|
|
//
|
|
// First try to find an engine in the FreeScript list
|
|
//
|
|
|
|
#ifdef ENABLE_PERF
|
|
InterlockedIncrement( &AzGlPerfEngineCacheTries );
|
|
#endif
|
|
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
|
|
if ( !IsListEmpty( &Task->FreeScriptHead ) ) {
|
|
|
|
//
|
|
// Try to find an engine that was initialized by this thread
|
|
//
|
|
|
|
for ( ListEntry = Task->FreeScriptHead.Flink;
|
|
ListEntry != &Task->FreeScriptHead;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
ListElement = CONTAINING_RECORD( ListEntry,
|
|
LIST_ELEMENT,
|
|
Next );
|
|
|
|
ASSERT( ListElement->This != NULL );
|
|
ScriptEngine = (CScriptEngine *) ListElement->This;
|
|
|
|
if ( ScriptEngine->IsBaseThread() ) {
|
|
break;
|
|
}
|
|
|
|
ScriptEngine = NULL;
|
|
AzPrint((AZD_SCRIPT_MORE, "Avoided script engine in non-base thread.\n" ));
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If not,
|
|
// grab the one at the front of the list.
|
|
// ResuseEngine will fix it up
|
|
//
|
|
|
|
if ( ScriptEngine == NULL ) {
|
|
//
|
|
// Remove the entry from the list
|
|
//
|
|
|
|
ListEntry = Task->FreeScriptHead.Flink;
|
|
|
|
ListElement = CONTAINING_RECORD( ListEntry,
|
|
LIST_ELEMENT,
|
|
Next );
|
|
|
|
ASSERT( ListElement->This != NULL );
|
|
ScriptEngine = ListElement->This;
|
|
AzPrint((AZD_SCRIPT, "Using free script engine from non-base thread.\n" ));
|
|
}
|
|
|
|
ScriptEngine->RemoveListEntry();
|
|
ScriptEngine->RemoveLruListEntry(); // Remove from the LRU list too
|
|
#ifdef ENABLE_PERF
|
|
InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
|
|
#endif
|
|
|
|
// There was a reference for being in the FreeScript list.
|
|
// That reference is now ours.
|
|
|
|
// Drop the crit sect before actually doing anything with the script engine
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
|
|
//
|
|
// Never re-use an engine that has a script that is not up to date
|
|
// The Assert below is overactive. The script was up to date when it was placed
|
|
// on the free script list. However, the script may be changing out from under
|
|
// us. That's fine. This script will be used one last time then will be ditched
|
|
// as we try to put it onto the free script list again.
|
|
//
|
|
|
|
// ASSERT( ScriptEngine->GetBizRuleSerialNumber() == Task->BizRuleSerialNumber );
|
|
|
|
|
|
//
|
|
// Update the script engine so it can be reused.
|
|
//
|
|
hr = ScriptEngine->ReuseEngine();
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "ReuseEngine failed: 0x%lx\n", hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Got an engine for sure...so just incr the cache hit count
|
|
#ifdef ENABLE_PERF
|
|
InterlockedIncrement( &AzGlPerfEngineCacheHits );
|
|
#endif
|
|
AzPrint((AZD_SCRIPT, "Using free script engine.\n" ));
|
|
|
|
} else {
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
}
|
|
|
|
//
|
|
// If not found, try to find the engine in the Running script list and clone it
|
|
//
|
|
|
|
if ( ScriptEngine == NULL ) {
|
|
CScriptEngine *RunningScriptEngine = NULL;
|
|
|
|
//
|
|
// Search the running script list
|
|
//
|
|
// This is a different crit sect than FreeScriptCritSect since we actually keep
|
|
// this crit sect locked while cloning the engine. That can take a long time
|
|
// and we don't want to increase the contention on FreeScriptCritSect.
|
|
//
|
|
|
|
SafeEnterCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
for ( ListEntry = Task->RunningScriptHead.Flink;
|
|
ListEntry != &Task->RunningScriptHead;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
ListElement = CONTAINING_RECORD( ListEntry,
|
|
LIST_ELEMENT,
|
|
Next );
|
|
|
|
ASSERT( ListElement->This != NULL );
|
|
RunningScriptEngine = (CScriptEngine *) ListElement->This;
|
|
|
|
//
|
|
// If the script engine hasn't GPF'd and
|
|
// is running the current version of the bizrule,
|
|
// use it.
|
|
//
|
|
|
|
if ( !RunningScriptEngine->FIsCorrupted() &&
|
|
RunningScriptEngine->GetBizRuleSerialNumber() == Task->BizRuleSerialNumber ) {
|
|
|
|
ClonedBizRuleSerialNumber = Task->BizRuleSerialNumber;
|
|
break;
|
|
}
|
|
|
|
RunningScriptEngine = NULL;
|
|
}
|
|
|
|
//
|
|
// If we didn't find a script engine,
|
|
// we're done.
|
|
//
|
|
|
|
if ( RunningScriptEngine == NULL ) {
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
//
|
|
// If we found a running script engine,
|
|
// clone it.
|
|
|
|
} else {
|
|
IActiveScript *pAS;
|
|
|
|
RunningScriptEngine->AssertValid();
|
|
|
|
//
|
|
// Clone the engine
|
|
//
|
|
pAS = RunningScriptEngine->GetActiveScript();
|
|
|
|
ASSERT(pAS != NULL);
|
|
|
|
hr = pAS->Clone( &ClonedActiveScript );
|
|
|
|
// We've cloned the engine, we can let go of the CS
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
//
|
|
// 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
|
|
ClonedActiveScript = NULL;
|
|
} else {
|
|
|
|
#ifdef ENABLE_PERF
|
|
InterlockedIncrement( &AzGlPerfRunningEngineCacheHits );
|
|
#endif
|
|
|
|
AzPrint((AZD_SCRIPT, "Using clone of running script engine.\n" ));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we couldn't find any script engine to reuse,
|
|
// allocate a new one.
|
|
//
|
|
|
|
if ( ScriptEngine == NULL) {
|
|
|
|
//
|
|
// Allocate the object
|
|
//
|
|
|
|
ScriptEngine = new CScriptEngine;
|
|
|
|
if ( ScriptEngine == NULL ) {
|
|
hr = E_OUTOFMEMORY;
|
|
AzPrint((AZD_CRITICAL, "new CScriptEngine failed: 0x%lx\n", hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize it
|
|
//
|
|
|
|
hr = ScriptEngine->Init( Task, ClonedActiveScript, ClonedBizRuleSerialNumber );
|
|
|
|
if (FAILED(hr)) {
|
|
ClonedActiveScript = NULL; // ::Init always steals this reference
|
|
AzPrint((AZD_CRITICAL, "ScriptEngine->Init failed: 0x%lx\n", hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( ClonedActiveScript == NULL ) {
|
|
AzPrint((AZD_SCRIPT, "Using new script engine.\n" ));
|
|
}
|
|
ClonedActiveScript = NULL; // ::Init always steals this reference
|
|
|
|
}
|
|
|
|
//
|
|
// Put the script engine onto the RunningScripts list.
|
|
//
|
|
|
|
ScriptEngine->AssertValid();
|
|
|
|
SafeEnterCriticalSection( &Task->RunningScriptCritSect );
|
|
ScriptEngine->InsertHeadList( &Task->RunningScriptHead );
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
//
|
|
// Return the script engine to the caller
|
|
//
|
|
// The reference for being in the running script list is shared with our caller.
|
|
//
|
|
hr = S_OK;
|
|
*RetScriptEngine = ScriptEngine;
|
|
ScriptEngine = NULL;
|
|
|
|
Cleanup:
|
|
|
|
ASSERT(SUCCEEDED(hr) || hr == TYPE_E_ELEMENTNOTFOUND || hr == OLESCRIPT_E_SYNTAX || hr == E_OUTOFMEMORY );
|
|
|
|
if ( ScriptEngine != NULL ) {
|
|
ScriptEngine->FinalRelease();
|
|
}
|
|
|
|
if ( ClonedActiveScript != NULL ) {
|
|
ClonedActiveScript->Release();
|
|
}
|
|
return (hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
AzpReturnEngineToCache(
|
|
IN PAZP_TASK Task,
|
|
IN CScriptEngine *ScriptEngine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Caller is done with the engine. Return it to the cache.
|
|
|
|
Arguments:
|
|
|
|
Task - Task containing the BizRule to process
|
|
|
|
ScriptEngine - Specifies the script engine to free
|
|
|
|
Return Value:
|
|
|
|
S_OK: a script engine was successfully returned
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
|
|
|
|
ASSERT( ScriptEngine != NULL);
|
|
|
|
//
|
|
// Remove the engine from the Running Script List
|
|
//
|
|
|
|
SafeEnterCriticalSection( &Task->RunningScriptCritSect );
|
|
ScriptEngine->RemoveListEntry();
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
hr = ScriptEngine->ResetToUninitialized();
|
|
|
|
if (FAILED(hr)) {
|
|
// Engine doesnt support this, sigh. Deallocate and continue.
|
|
AzPrint((AZD_CRITICAL, "Engine doesn't support reset: 0x%lx\n", hr ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the script changed while this engine was running,
|
|
// Or if there was a GPF while then engine was running,
|
|
// then it might be in a corrupted state.
|
|
//
|
|
// In either case, delete the engine.
|
|
//
|
|
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
SafeEnterCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
if ( ScriptEngine->GetBizRuleSerialNumber() != Task->BizRuleSerialNumber ||
|
|
ScriptEngine->FIsCorrupted() ) {
|
|
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
AzPrint((AZD_SCRIPT, "Script changed while engine was running\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the script to the free script list for potential re-use.
|
|
//
|
|
|
|
ScriptEngine->InsertHeadList( &Task->FreeScriptHead );
|
|
ScriptEngine->InsertHeadLruList();
|
|
ScriptEngine = NULL;
|
|
|
|
#ifdef ENABLE_PERF
|
|
InterlockedIncrement( &AzGlPerfEnginesInFreeScriptList );
|
|
#endif
|
|
|
|
//
|
|
// If there are too many scripts cached,
|
|
// delete the least recently used engine.
|
|
//
|
|
|
|
if ( AzAuthorizationStore->LruFreeScriptCount > AzAuthorizationStore->MaxScriptEngines ) {
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ELEMENT ListElement;
|
|
|
|
AzPrint((AZD_SCRIPT, "Script LRU'ed out: %ld\n", AzAuthorizationStore->MaxScriptEngines ));
|
|
|
|
//
|
|
// Grab the entry from the tail of the list
|
|
//
|
|
|
|
ListEntry = AzAuthorizationStore->LruFreeScriptHead.Blink;
|
|
|
|
ListElement = CONTAINING_RECORD( ListEntry,
|
|
LIST_ELEMENT,
|
|
Next );
|
|
|
|
ASSERT( ListElement->This != NULL );
|
|
ScriptEngine = ListElement->This;
|
|
|
|
//
|
|
// Delink it
|
|
//
|
|
ScriptEngine->RemoveListEntry();
|
|
ScriptEngine->RemoveLruListEntry(); // Remove from the LRU list too
|
|
#ifdef ENABLE_PERF
|
|
InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
|
|
Cleanup:
|
|
if ( ScriptEngine != NULL ) {
|
|
ScriptEngine->FinalRelease();
|
|
}
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
AzpFlushBizRule(
|
|
IN PAZP_TASK Task
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine flushes the bizrule cache.
|
|
|
|
This routine is called whenever the bizrule script is changed
|
|
|
|
Arguments:
|
|
|
|
Task - Task containing the BizRule to flush
|
|
|
|
BizRuleResult - Result of the bizrule
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
|
|
Other exception status codes
|
|
|
|
--*/
|
|
{
|
|
PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
|
|
PLIST_ENTRY ListEntry;
|
|
PLIST_ELEMENT ListElement;
|
|
CScriptEngine *ScriptEngine = NULL;
|
|
|
|
//
|
|
// Delete engines on the Free Script List
|
|
// Engines on the running script list are deleted as they finish running
|
|
//
|
|
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
while ( !IsListEmpty( &Task->FreeScriptHead ) ) {
|
|
|
|
|
|
//
|
|
// Remove the entry from the list
|
|
//
|
|
|
|
ListEntry = Task->FreeScriptHead.Flink;
|
|
|
|
ListElement = CONTAINING_RECORD( ListEntry,
|
|
LIST_ELEMENT,
|
|
Next );
|
|
|
|
ASSERT( ListElement->This != NULL );
|
|
ScriptEngine = ListElement->This;
|
|
|
|
ScriptEngine->RemoveListEntry();
|
|
ScriptEngine->RemoveLruListEntry(); // Remove it from the LRU list too
|
|
ScriptEngine->FinalRelease();
|
|
|
|
#ifdef ENABLE_PERF
|
|
InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
|
|
InterlockedIncrement( &AzGlPerfEngineFlushes );
|
|
#endif
|
|
|
|
AzPrint((AZD_SCRIPT, "Script Freed from free script list\n" ));
|
|
|
|
}
|
|
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
|
|
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
AzpProcessBizRule(
|
|
IN PACCESS_CHECK_CONTEXT AcContext,
|
|
IN PAZP_TASK Task,
|
|
OUT PBOOL BizRuleResult
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the main bizrule routine that determine whether a bizrule is satisfied.
|
|
If the AzAuthorizationStore object's script engine timeout has been set to 0, then return
|
|
the biz rule result as false without processing the biz rule.
|
|
|
|
On entry, AcContext->ClientContext.CritSect must be locked.
|
|
|
|
Arguments:
|
|
|
|
AcContext - Specifies the context of the accesscheck operation the bizrule is being evaluated for
|
|
|
|
Task - Task containing the BizRule to process
|
|
|
|
BizRuleResult - Result of the bizrule
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
|
|
Other exception status codes
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
HRESULT hr;
|
|
CScriptEngine *ScriptEngine = NULL;
|
|
|
|
// BOOL fCoInit = FALSE;
|
|
|
|
//
|
|
// Check Root AzAuthorizationStore object's script engine timeout parameter
|
|
//
|
|
|
|
if ( (Task->GenericObject).AzStoreObject->ScriptEngineTimeout == 0 ) {
|
|
WinStatus = NO_ERROR;
|
|
*BizRuleResult = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzpIsCritsectLocked( &AcContext->ClientContext->CritSect ) );
|
|
*BizRuleResult = FALSE;
|
|
|
|
#if 0
|
|
// init COM
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
ASSERT( hr != S_OK ); // engine caching breaks if com isn't already initialized
|
|
if (hr == S_OK || hr == S_FALSE) {
|
|
fCoInit = TRUE;
|
|
} else if (hr != RPC_E_CHANGED_MODE) {
|
|
WinStatus = AzpHresultToWinStatus(hr);
|
|
AzPrint((AZD_CRITICAL, "CoInitializeEx failed: 0x%lx, %ld\n", hr, WinStatus));
|
|
goto Cleanup;
|
|
}
|
|
#endif // 0
|
|
|
|
//
|
|
// Get a script engine to run the script in
|
|
//
|
|
|
|
hr = AzpGetScriptEngine( Task, &ScriptEngine );
|
|
|
|
if (FAILED(hr)) {
|
|
WinStatus = AzpHresultToWinStatus(hr);
|
|
AzPrint((AZD_CRITICAL, "AzpGetScriptEngine failed: 0x%lx, %ld\n", hr, WinStatus));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Run the script
|
|
//
|
|
|
|
hr = ScriptEngine->RunScript( AcContext, BizRuleResult );
|
|
|
|
if (FAILED(hr)) {
|
|
WinStatus = AzpHresultToWinStatus(hr);
|
|
AzPrint((AZD_CRITICAL, "RunScript failed: 0x%lx, %ld\n", hr, WinStatus));
|
|
*BizRuleResult = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
|
|
//
|
|
// Free any local resources
|
|
//
|
|
Cleanup:
|
|
|
|
if (ScriptEngine != NULL) {
|
|
AzpReturnEngineToCache( Task, ScriptEngine );
|
|
}
|
|
#if 0
|
|
if (fCoInit) {
|
|
CoUninitialize();
|
|
}
|
|
#endif // 0
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
DWORD
|
|
AzpParseBizRule(
|
|
IN PAZP_TASK Task
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
This routine parses a bizrule to see if it is syntactically valid.
|
|
|
|
Arguments:
|
|
|
|
Task - Task containing the BizRule to parse
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful
|
|
ERROR_NOT_ENOUGH_MEMORY - not enough memory
|
|
OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
HRESULT hr;
|
|
CScriptEngine *ScriptEngine = NULL;
|
|
BOOL fCoInit = FALSE;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
// init COM
|
|
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
|
ASSERT( hr != S_OK ); // engine caching breaks if com isn't already initialized
|
|
if (hr == S_OK || hr == S_FALSE) {
|
|
fCoInit = TRUE;
|
|
} else if (hr != RPC_E_CHANGED_MODE) {
|
|
WinStatus = AzpHresultToWinStatus(hr);
|
|
AzPrint((AZD_CRITICAL, "CoInitializeEx failed: 0x%lx, %ld\n", hr, WinStatus));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get a script engine to run the script in
|
|
//
|
|
|
|
hr = AzpGetScriptEngine( Task, &ScriptEngine );
|
|
|
|
if (FAILED(hr)) {
|
|
WinStatus = AzpHresultToWinStatus(hr);
|
|
AzPrint((AZD_CRITICAL, "AzpGetScriptEngine failed: 0x%lx, %ld\n", hr, WinStatus));
|
|
goto Cleanup;
|
|
}
|
|
|
|
WinStatus = NO_ERROR;
|
|
|
|
|
|
//
|
|
// Free any local resources
|
|
//
|
|
Cleanup:
|
|
|
|
if (ScriptEngine != NULL) {
|
|
AzpReturnEngineToCache( Task, ScriptEngine );
|
|
}
|
|
if (fCoInit) {
|
|
CoUninitialize();
|
|
}
|
|
return WinStatus;
|
|
|
|
}
|
|
|
|
VOID
|
|
AzpInterruptScript(
|
|
PVOID Parameter,
|
|
BOOLEAN TimerFired
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the callback routine that fires when a script takes too long to execute
|
|
|
|
Arguments:
|
|
|
|
Parameter - "This" pointer for the script that took too long
|
|
|
|
TimerFired - Not used
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
CScriptEngine *ScriptEngine = (CScriptEngine *)Parameter;
|
|
UNREFERENCED_PARAMETER( TimerFired );
|
|
|
|
AzPrint((AZD_SCRIPT, "Script Timed out.\n"));
|
|
ScriptEngine->InterruptScript();
|
|
}
|
|
|
|
|
|
|
|
//Constructor
|
|
CScriptEngine::CScriptEngine()
|
|
{
|
|
//
|
|
// Fields are initialized below in the order they are defined
|
|
//
|
|
|
|
m_cRef = 1;
|
|
m_Task = NULL;
|
|
|
|
::InitializeListHead( &m_Next.Next );
|
|
m_Next.This = this;
|
|
|
|
::InitializeListHead( &m_LruNext.Next );
|
|
m_LruNext.This = this;
|
|
|
|
m_Engine = NULL;
|
|
m_Parser = NULL;
|
|
m_BizRuleContext = NULL;
|
|
m_AcContext = NULL;
|
|
|
|
m_BaseThread = 0;
|
|
|
|
m_BizRuleSerialNumber = 0;
|
|
|
|
m_ScriptError = S_OK;
|
|
|
|
m_fInited = FALSE;
|
|
m_fCorrupted = FALSE;
|
|
m_fTimedOut = FALSE;
|
|
|
|
m_bCaseSensitive = TRUE;
|
|
|
|
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine\n"));
|
|
}
|
|
|
|
//Destructor
|
|
CScriptEngine::~CScriptEngine()
|
|
{
|
|
AzPrint((AZD_SCRIPT_MORE, "~CScriptEngine\n"));
|
|
|
|
//
|
|
// Assert that FinalRelease was called
|
|
//
|
|
ASSERT( m_Engine == NULL );
|
|
ASSERT( m_Parser == NULL );
|
|
ASSERT( m_BizRuleContext == NULL );
|
|
ASSERT( m_AcContext == NULL );
|
|
ASSERT( IsListEmpty( &m_Next.Next ) );
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CScriptEngine::Init(
|
|
IN PAZP_TASK Task,
|
|
IN IActiveScript *ClonedActiveScript OPTIONAL,
|
|
IN DWORD ClonedBizRuleSerialNumber OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates the ActiveX Scripting Engine and initializes it.
|
|
|
|
Arguments:
|
|
|
|
Task - Task containing the BizRule to process
|
|
|
|
ClonedActiveScript - Pointer to an instance of a cloned script engine.
|
|
NULL: This is a new script engine and not a clone.
|
|
|
|
This routine always steals ClonedActiveScript and will always release it sooner
|
|
or later. The caller should not use this argument after making this call.
|
|
|
|
ClonedBizRuleSerialNumber - Serial number of the bizrule parsed by ClonedActiveScript.
|
|
|
|
Return Value:
|
|
|
|
HRESULT status of the operation
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
LPWSTR BizRule = NULL;
|
|
|
|
EXCEPINFO ParseExceptionInfo;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InitializeScriptEngine\n"));
|
|
|
|
if ( m_fInited ) {
|
|
ASSERT(!m_fInited);
|
|
hr = AZ_HRESULT(ERROR_ALREADY_INITIALIZED);
|
|
|
|
|
|
if ( ClonedActiveScript != NULL ) {
|
|
ClonedActiveScript->Release();
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
m_Task = Task;
|
|
|
|
|
|
//
|
|
// If this is a clone,
|
|
// simply steal the passed in pointer to the script engine.
|
|
//
|
|
|
|
if ( ClonedActiveScript != NULL ) {
|
|
m_Engine = ClonedActiveScript;
|
|
|
|
//
|
|
// If this isn't a clone,
|
|
// instantiate a script engine
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Convert the language name to a Clsid
|
|
//
|
|
|
|
if ( IsEqualGUID( Task->BizRuleLanguageClsid, AzGlZeroGuid ) ) {
|
|
|
|
hr = CLSIDFromProgID( Task->BizRuleLanguage.String, &Task->BizRuleLanguageClsid);
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to get scripting engine CLSID: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// determine if our language is case-sensitive or not
|
|
//
|
|
|
|
m_bCaseSensitive = _wcsicmp(Task->BizRuleLanguage.String, L"VBScript") != 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the scripting engine with a call to CoCreateInstance,
|
|
// placing the created engine in m_Engine.
|
|
//
|
|
hr = CoCreateInstance( Task->BizRuleLanguageClsid,
|
|
NULL, // Not part of an aggregate
|
|
CLSCTX_INPROC_SERVER, // In process
|
|
IID_IActiveScript,
|
|
(void **)&m_Engine);
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to create scripting engine: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query for the IActiveScriptParse interface of the engine
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->QueryInterface(IID_IActiveScriptParse, (void **)&m_Parser), hr, "IActiveScript::QueryInterface()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Engine doesn't support IActiveScriptParse: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
} else if ( m_Parser == NULL ) {
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create an object for the script to interact with.
|
|
//
|
|
hr = CoCreateInstance( CLSID_AzBizRuleContext,
|
|
NULL, // Not part of an aggregate
|
|
CLSCTX_INPROC_SERVER, // In process
|
|
IID_IAzBizRuleContext,
|
|
(void **)&m_BizRuleContext);
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to create AzBizRuleContext instance: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Remember the thread ID of this thread.
|
|
// Only this thread can re-use the engine without going to unitialized state first
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &m_BaseThread), hr, "IActiveScript::GetCurrentScriptThreadID()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
AzPrint((AZD_SCRIPT, "Set ThreadId to: 0x%lx 0x%lx\n", this, m_BaseThread));
|
|
|
|
//
|
|
// The engine needs to know the host it runs on.
|
|
//
|
|
// Once we've set the script site, IActiveScript can call back
|
|
m_fInited = TRUE;
|
|
TRYCATCH_HR(m_Engine->SetScriptSite((IActiveScriptSite *) this), hr, "IActiveScript::SetScriptSite()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling SetScriptSite: 0x%lx\n", hr));
|
|
m_fInited = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If this isn't a clone,
|
|
// the parse the script.
|
|
//
|
|
|
|
if ( ClonedActiveScript == NULL ) {
|
|
|
|
//
|
|
// Initialize the script engine so it's ready to run.
|
|
//
|
|
|
|
TRYCATCH_HR(m_Parser->InitNew(), hr, "IActiveScriptParse::InitNew()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling InitNew: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the name of the object that will respond to the script
|
|
//
|
|
|
|
TRYCATCH_HR( m_Engine->AddNamedItem( BIZRULE_CONTEXT_INTERFACE_NAME,
|
|
SCRIPTITEM_ISPERSISTENT | SCRIPTITEM_ISVISIBLE ),
|
|
hr,
|
|
"IActiveScript::AddNamedItem()" );
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to AddNamedItem: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Grab a copy of the bizrule
|
|
//
|
|
|
|
SafeEnterCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
SafeAllocaAllocate( BizRule, Task->BizRule.StringSize );
|
|
if ( BizRule == NULL ) {
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( BizRule, Task->BizRule.String, Task->BizRule.StringSize );
|
|
m_BizRuleSerialNumber = Task->BizRuleSerialNumber;
|
|
SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
|
|
|
|
//
|
|
//
|
|
// Pass the script to be run to the script engine.
|
|
// I don't know what SCRIPTTEXT_HOSTMANAGESSOURCE is but IIS sets it so I will.
|
|
//
|
|
|
|
TRYCATCH_HR( m_Parser->ParseScriptText( BizRule,
|
|
NULL, // pstrItemName
|
|
NULL, // punkContext
|
|
NULL, // pstrDelimiter
|
|
0, // dwSourceContextCookie
|
|
1, // ulStartingLineNumber
|
|
SCRIPTTEXT_ISPERSISTENT | SCRIPTTEXT_HOSTMANAGESSOURCE,
|
|
NULL, // pvarResult
|
|
&ParseExceptionInfo), // exception info filled in on error
|
|
hr,
|
|
"IActiveScriptParse::ParseScriptText()");
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to ParseScriptText: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If it is a clone,
|
|
// clone the bizrule serial number.
|
|
//
|
|
} else {
|
|
m_BizRuleSerialNumber = ClonedBizRuleSerialNumber;
|
|
}
|
|
|
|
|
|
//
|
|
// Done initialization
|
|
//
|
|
AssertValid();
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
SafeAllocaFree( BizRule );
|
|
if ( FAILED(hr)) {
|
|
|
|
// Note: Our caller has to call FinalRelease which will do most of the cleanup
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
CScriptEngine::RunScript(
|
|
IN PACCESS_CHECK_CONTEXT AcContext,
|
|
OUT PBOOL BizRuleResult
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs an initialized script
|
|
|
|
Arguments:
|
|
|
|
AcContext - Specifies the context of the accesscheck operation the bizrule is being evaluated for
|
|
|
|
BizRuleResult - Result of the bizrule
|
|
|
|
Return Value:
|
|
|
|
HRESULT describing whether the operation worked
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
|
|
BOOLEAN InterfaceNamesLocked = FALSE;
|
|
BOOLEAN InterfaceFlagsLocked = FALSE;
|
|
VARIANT varName;
|
|
VARIANT varFlag;
|
|
|
|
HANDLE TimerHandle = INVALID_HANDLE_VALUE;
|
|
HRESULT ScriptErrorFromAccessCheck = S_OK;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RunScript\n"));
|
|
AssertValid();
|
|
m_ScriptError = S_OK;
|
|
m_AcContext = AcContext;
|
|
|
|
VariantInit(&varName);
|
|
VariantInit(&varFlag);
|
|
|
|
//
|
|
// Tell the access check object the context of the current access check
|
|
//
|
|
|
|
SetAccessCheckContext( static_cast<CAzBizRuleContext *> (m_BizRuleContext), m_bCaseSensitive, AcContext, BizRuleResult, &ScriptErrorFromAccessCheck );
|
|
|
|
//
|
|
// Add each interface that the caller passed to access check
|
|
//
|
|
|
|
if ( AcContext->Interfaces != NULL ) {
|
|
VARIANT HUGEP *varNames;
|
|
VARIANT HUGEP *varFlags;
|
|
ULONG Index;
|
|
|
|
//
|
|
// We didn't capture the array
|
|
// So access it under a try/except
|
|
__try {
|
|
|
|
//
|
|
// Access the variant arrays directly
|
|
//
|
|
ASSERT( AcContext->InterfaceNames != NULL && AcContext->InterfaceFlags != NULL );
|
|
|
|
hr = SafeArrayAccessData( AcContext->InterfaceNames, (void HUGEP **)&varNames);
|
|
_JumpIfError(hr, Cleanup, "SafeArrayAccessData");
|
|
InterfaceNamesLocked = TRUE;
|
|
|
|
hr = SafeArrayAccessData( AcContext->InterfaceFlags, (void HUGEP **)&varFlags);
|
|
_JumpIfError(hr, Cleanup, "SafeArrayAccessData");
|
|
InterfaceFlagsLocked = TRUE;
|
|
|
|
//
|
|
// Loop adding each name
|
|
//
|
|
|
|
for ( Index=0; Index<AcContext->InterfaceNames->rgsabound[0].cElements; Index++ ) {
|
|
|
|
//
|
|
// Stop at the end of the array
|
|
//
|
|
if ( V_VT(&varNames[Index]) == VT_EMPTY ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Convert the interface name to a BSTR
|
|
//
|
|
hr = VariantChangeType(&varName, &varNames[Index], 0, VT_BSTR);
|
|
_JumpIfError(hr, Cleanup, "VariantChangeType");
|
|
|
|
|
|
//
|
|
// Convert the flags to a LONG
|
|
//
|
|
|
|
hr = VariantChangeType(&varFlag, &varFlags[Index], 0, VT_I4 );
|
|
_JumpIfError(hr, Cleanup, "VariantChangeType");
|
|
|
|
|
|
//
|
|
// Add the name of the object that will respond to the script
|
|
// Ignore SCRIPTITEM_ISPERSISTENT since that would invalidate our caching.
|
|
//
|
|
|
|
TRYCATCH_HR( m_Engine->AddNamedItem(
|
|
V_BSTR(&varName),
|
|
(V_I4(&varFlag) | SCRIPTITEM_ISVISIBLE) & ~SCRIPTITEM_ISPERSISTENT ),
|
|
hr,
|
|
"IActiveScript::AddNamedItem()" );
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to AddNamedItem: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
VariantClear(&varName);
|
|
VariantClear(&varFlag);
|
|
}
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
hr = GetExceptionCode();
|
|
AzPrint((AZD_CRITICAL, "RunScript took an exception: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Queue a timer to kill the engine if it takes too long
|
|
//
|
|
|
|
m_fTimedOut = FALSE;
|
|
if ( !CreateTimerQueueTimer(
|
|
&TimerHandle,
|
|
AzAuthorizationStore->ScriptEngineTimerQueue,
|
|
AzpInterruptScript,
|
|
this,
|
|
AzAuthorizationStore->ScriptEngineTimeout,
|
|
0, // Not a period timer
|
|
0 ) ) { // No special flags
|
|
|
|
hr = AZ_HRESULT(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Tell the engine to start processing the script with a call to
|
|
// SetScriptState().
|
|
//
|
|
__try {
|
|
hr = m_Engine->SetScriptState( SCRIPTSTATE_STARTED );
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
hr = GetExceptionCode();
|
|
AzPrint((AZD_CRITICAL, "Script took an exception: 0x%lx\n", hr));
|
|
|
|
// Dont reuse the engine
|
|
m_fCorrupted = TRUE;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to SetScriptState(STARTED): 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// See if IAzBizRuleContext detected an error
|
|
//
|
|
|
|
if ( ScriptErrorFromAccessCheck != S_OK ) {
|
|
hr = ScriptErrorFromAccessCheck;
|
|
AzPrint((AZD_CRITICAL, "Script from access check to caller: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// See if the script had an error
|
|
//
|
|
|
|
if ( m_ScriptError != S_OK ) {
|
|
hr = m_ScriptError;
|
|
AzPrint((AZD_SCRIPT, "Return script error to caller: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
hr = S_OK;
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Delete any timer
|
|
if ( TimerHandle != INVALID_HANDLE_VALUE ) {
|
|
if ( !DeleteTimerQueueTimer(
|
|
AzAuthorizationStore->ScriptEngineTimerQueue,
|
|
TimerHandle,
|
|
INVALID_HANDLE_VALUE ) ) {
|
|
|
|
AzPrint((AZD_CRITICAL, "Cannot DeleteTimerQueurTimer: %ld\n", GetLastError() ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Cleanup from adding interfaces
|
|
//
|
|
if ( AcContext->Interfaces != NULL ) {
|
|
|
|
//
|
|
// We didn't capture the array
|
|
// So access it under a try/except
|
|
__try {
|
|
if ( InterfaceNamesLocked ) {
|
|
SafeArrayUnaccessData( AcContext->InterfaceNames );
|
|
}
|
|
if ( InterfaceFlagsLocked ) {
|
|
SafeArrayUnaccessData( AcContext->InterfaceFlags );
|
|
}
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
AzPrint((AZD_CRITICAL, "RunScript took an exception: 0x%lx\n", hr));
|
|
}
|
|
|
|
VariantClear(&varName);
|
|
VariantClear(&varFlag);
|
|
}
|
|
|
|
//
|
|
// Tell the access check object to forget about the current access check
|
|
//
|
|
|
|
SetAccessCheckContext( static_cast<CAzBizRuleContext *> (m_BizRuleContext), TRUE, NULL, NULL, NULL );
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
CScriptEngine::InterruptScript(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs stops a currently running script
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT describing whether the operation worked
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
EXCEPINFO excepinfo;
|
|
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InterruptScript\n"));
|
|
AssertValid();
|
|
|
|
|
|
//
|
|
// Fill in the excepinfo. This will be passed to OnScriptError
|
|
//
|
|
|
|
RtlZeroMemory( &excepinfo, sizeof(EXCEPINFO));
|
|
excepinfo.wCode = ERROR_TIMEOUT;
|
|
m_fTimedOut = TRUE;
|
|
|
|
//
|
|
// Blow the script away
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->InterruptScriptThread(
|
|
SCRIPTTHREADID_BASE, // The thread in which the engine was instantiated
|
|
&excepinfo,
|
|
0 ),
|
|
hr,
|
|
"IActiveScript::InterruptScriptThread()");
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CScriptEngine::ResetToUninitialized()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When we want to reuse an engine, we reset it to an uninited state
|
|
before putting it on the FreeScript list
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT describing whether the operation worked
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::ResetToUninitialized\n"));
|
|
|
|
//
|
|
// Release interfaces, they will need to be re-gotten when
|
|
// the engine is reused
|
|
//
|
|
if (m_Parser) {
|
|
TRYCATCH(m_Parser->Release(), "IActiveScriptParse::Release()");
|
|
m_Parser = NULL;
|
|
}
|
|
|
|
//
|
|
// Set the script state to Uninitialized
|
|
// IIS sets the state to uninitialized here. That does not give good performance.
|
|
// Setting it to initialized give better performance as long as the next thread to
|
|
// reuse the engine comes in on the same thread.
|
|
//
|
|
if (m_Engine) {
|
|
|
|
TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_INITIALIZED ), hr, "IActiveScript::SetScriptState()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to SetScriptState(INITIALIZED): 0x%lx\n", hr));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Indicate the the bizrule is no longer associated with a particular access check
|
|
// ... Can't do this until the script state is set
|
|
//
|
|
|
|
m_AcContext = NULL;
|
|
|
|
return (hr);
|
|
}
|
|
|
|
HRESULT
|
|
CScriptEngine::ReuseEngine(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When reusing an engine from the FreeScript list, this routine is called
|
|
to set the script engine to the initialized state.
|
|
|
|
Basically, this routine undoes the effects of ResetToUninitialized.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT describing whether the operation worked
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
|
|
SCRIPTSTATE nScriptState = SCRIPTSTATE_UNINITIALIZED;
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::ReuseEngine\n"));
|
|
|
|
//
|
|
// IIS enters this routine with an INITIALIZED engine when debugging the script.
|
|
// In that case, it only sets the script site if the state is uninitilized.
|
|
// We'll always be initialized.
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->GetScriptState(&nScriptState), hr, "IActiveScript::GetScriptState()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to GetScriptState: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the script engine has thread state,
|
|
// ditch it.
|
|
//
|
|
|
|
if ( nScriptState == SCRIPTSTATE_INITIALIZED ) {
|
|
SCRIPTTHREADID ThreadId = 0;
|
|
|
|
//
|
|
// Get the thread id of the current thread
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &ThreadId), hr, "IActiveScript::GetCurrentScriptThreadID()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this thread isn't the thread that put the engine into initialized state,
|
|
// then go to uninitialized state do clear the engines thread local storage.
|
|
//
|
|
|
|
if ( m_BaseThread != ThreadId ) {
|
|
TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_UNINITIALIZED), hr, "IActiveScript::SetScriptState()");
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to SetScriptState(UNINITIALIZED): 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
nScriptState = SCRIPTSTATE_UNINITIALIZED;
|
|
m_BaseThread = ThreadId;
|
|
AzPrint((AZD_SCRIPT, "Changed ThreadId to: 0x%lx 0x%lx\n", this, m_BaseThread));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// The engine needs to know the host it runs on.
|
|
//
|
|
if ( nScriptState == SCRIPTSTATE_UNINITIALIZED ) {
|
|
TRYCATCH_HR(m_Engine->SetScriptSite((IActiveScriptSite *) this), hr, "IActiveScript::SetScriptSite()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling SetScriptSite: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Query for the IActiveScriptParse interface of the engine
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->QueryInterface(IID_IActiveScriptParse, (void **)&m_Parser), hr, "IActiveScript::QueryInterface()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Engine doesn't support IActiveScriptParse: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
} else if ( m_Parser == NULL ) {
|
|
hr = E_FAIL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
AssertValid();
|
|
|
|
Cleanup:
|
|
return (hr);
|
|
}
|
|
|
|
BOOL
|
|
CScriptEngine::IsBaseThread(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns TRUE if the calling thread is the base thread that initialized the engine
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE - The calling thread is the base thread
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr;
|
|
SCRIPTTHREADID ThreadId = 0;
|
|
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::IsBaseThread\n"));
|
|
|
|
ASSERT( m_Engine != NULL );
|
|
|
|
//
|
|
// Get the thread id of the current thread
|
|
//
|
|
|
|
TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &ThreadId), hr, "IActiveScript::GetCurrentScriptThreadID()");
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check to see if it is the base thread
|
|
//
|
|
|
|
return m_BaseThread == ThreadId;
|
|
}
|
|
|
|
|
|
VOID
|
|
CScriptEngine::FinalRelease(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
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 will be zero when last external user is done with the engine.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::FinalRelease\n"));
|
|
|
|
//
|
|
// Free all of the interfaces used by the script engine
|
|
//
|
|
|
|
if (m_BizRuleContext != NULL) {
|
|
m_BizRuleContext->Release();
|
|
m_BizRuleContext = NULL;
|
|
}
|
|
|
|
if (m_Parser) {
|
|
TRYCATCH(m_Parser->Release(), "IActiveScriptParse::Release()");
|
|
m_Parser = NULL;
|
|
}
|
|
|
|
if (m_Engine) {
|
|
HRESULT hr;
|
|
|
|
// Engine needs to be in uninitialized state before closing.
|
|
// This is only needed if a different thread than this one put it in the
|
|
// initialized state.
|
|
//
|
|
// This is a hack. If I can set it from initialized to uninitialized in a different
|
|
// thread, then why can't close do it in a different thread.
|
|
//
|
|
TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_UNINITIALIZED), hr, "IActiveScript::SetScriptState()");
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Failed to SetScriptState(UNINITIALIZED): 0x%lx\n", hr));
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
//
|
|
// Close the engine before releasing it.
|
|
//
|
|
TRYCATCH_HR(m_Engine->Close(), hr, "IActiveScript::Close()");
|
|
|
|
if (FAILED(hr)) {
|
|
AzPrint((AZD_CRITICAL, "Cannot CloseEngine: 0x%lx\n", hr ));
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
// Then we can release it
|
|
TRYCATCH(m_Engine->Release(), "IActiveScript::Release()");
|
|
|
|
m_Engine = NULL;
|
|
}
|
|
|
|
//
|
|
// We must be the last reference
|
|
//
|
|
|
|
#if DBG
|
|
ULONG cRefs = Release();
|
|
ASSERT(cRefs == 0);
|
|
#else
|
|
Release();
|
|
#endif //DBG
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
CScriptEngine::InsertHeadList(
|
|
IN PLIST_ENTRY ListHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts this ScriptEngine object at the head of the list specified.
|
|
The caller must ensure this object is in at most only one list.
|
|
The caller must provide any serialization.
|
|
|
|
Arguments:
|
|
|
|
ListHead - List to insert the object into.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InsertHeadList\n"));
|
|
|
|
::InsertHeadList( ListHead, &m_Next.Next );
|
|
// m_Next.This = this;
|
|
|
|
}
|
|
|
|
VOID
|
|
CScriptEngine::RemoveListEntry(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes this ScriptEngine object from whatever list it is in.
|
|
The caller must ensure this object is in a list.
|
|
The caller must provide any serialization.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RemoveListEntry\n"));
|
|
|
|
::RemoveEntryList( &m_Next.Next );
|
|
::InitializeListHead( &m_Next.Next );
|
|
// m_Next.This = this;
|
|
|
|
}
|
|
|
|
VOID
|
|
CScriptEngine::InsertHeadLruList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts this ScriptEngine object at the head of the LRU list.
|
|
|
|
The caller must provide any serialization.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InsertHeadLruList\n"));
|
|
|
|
::InsertHeadList( &AzAuthorizationStore->LruFreeScriptHead, &m_LruNext.Next );
|
|
AzAuthorizationStore->LruFreeScriptCount ++;
|
|
// m_Next.This = this;
|
|
|
|
}
|
|
|
|
VOID
|
|
CScriptEngine::RemoveLruListEntry(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes this ScriptEngine object from whatever LRU list it is in.
|
|
The caller must ensure this object is in a list.
|
|
The caller must provide any serialization.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RemoveLruListEntry\n"));
|
|
|
|
::RemoveEntryList( &m_LruNext.Next );
|
|
::InitializeListHead( &m_LruNext.Next );
|
|
AzAuthorizationStore->LruFreeScriptCount --;
|
|
// m_Next.This = this;
|
|
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* IUnknown Interfaces -- All COM objects must implement, either directly or
|
|
* indirectly, the IUnknown interface.
|
|
******************************************************************************/
|
|
|
|
STDMETHODIMP CScriptEngine::QueryInterface(REFIID riid, void **ppvObj)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Standard COM QueryInterface routine.
|
|
|
|
Determines if this component supports the requested interface.
|
|
|
|
Arguments:
|
|
|
|
riid - Id of the interface being queried
|
|
|
|
ppvObj - On success, returns a pointer to that interface.
|
|
|
|
Return Value:
|
|
|
|
S_OK: ppvObj is returned
|
|
E_NOINTERFACE: riid is not supported
|
|
|
|
--*/
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::QueryInterface->"));
|
|
|
|
//
|
|
// Only two interfaces are supported
|
|
//
|
|
if (riid == IID_IUnknown) {
|
|
AzPrint((AZD_SCRIPT_MORE, "IUnknown\n"));
|
|
*ppvObj = static_cast < IActiveScriptSite * >(this);
|
|
|
|
} else if (riid == IID_IActiveScriptSite) {
|
|
AzPrint((AZD_SCRIPT_MORE, "IActiveScriptSite\n"));
|
|
*ppvObj = static_cast < IActiveScriptSite * >(this);
|
|
|
|
} else {
|
|
AzPrint((AZD_SCRIPT_MORE, "Unsupported Interface: "));
|
|
AzpDumpGuid(AZD_SCRIPT_MORE, (GUID *) & riid);
|
|
AzPrint((AZD_SCRIPT_MORE, "\n"));
|
|
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static_cast < IUnknown * >(*ppvObj)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* AddRef() -- In order to allow an object to delete itself when it is no
|
|
* longer needed, it is necessary to maintain a count of all references to
|
|
* this object. When a new reference is created, this function increments
|
|
* the count.
|
|
******************************************************************************/
|
|
STDMETHODIMP_(ULONG) CScriptEngine::AddRef()
|
|
{
|
|
ULONG cRef;
|
|
|
|
cRef = InterlockedIncrement(&m_cRef);
|
|
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::AddRef %ld\n", cRef ));
|
|
return(cRef);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Release() -- When a reference to this object is removed, this function
|
|
* decrements the reference count. If the reference count is 0, then this
|
|
* function deletes this object and returns 0;
|
|
******************************************************************************/
|
|
STDMETHODIMP_(ULONG) CScriptEngine::Release()
|
|
{
|
|
ULONG cRef = InterlockedDecrement(&m_cRef);
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::Release %ld\n", cRef));
|
|
|
|
if (0 == cRef) {
|
|
delete this;
|
|
}
|
|
return(cRef);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* IActiveScriptSite Interfaces -- These interfaces define the exposed methods
|
|
* of ActiveX Script Hosts.
|
|
******************************************************************************/
|
|
|
|
/******************************************************************************
|
|
* GetLCID() -- Gets the identifier of the host's user interface. This method
|
|
* returns S_OK if the identifier was placed in plcid, E_NOTIMPL if this
|
|
* function is not implemented, in which case the system-defined identifier
|
|
* should be used, and E_POINTER if the specified pointer was invalid.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::GetLCID(LCID * plcid)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetLCID\n"));
|
|
UNREFERENCED_PARAMETER(plcid);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* GetItemInfo() -- Retrieves information about an item that was added to the
|
|
* script engine through a call to AddNamedItem.
|
|
* Parameters: pstrName -- the name of the item, specified in AddNamedItem.
|
|
* dwReturnMask -- Mask indicating what kind of pointer to return
|
|
* SCRIPTINFO_IUNKNOWN or SCRIPTINFO_ITYPEINFO
|
|
* ppunkItem -- return spot for an IUnknown pointer
|
|
* ppTypeInfo -- return spot for an ITypeInfo pointer
|
|
* Returns: S_OK if the call was successful
|
|
* E_INVALIDARG if one of the arguments was invalid
|
|
* E_POINTER if one of the pointers was invalid
|
|
* TYPE_E_ELEMENTNOTFOUND if there wasn't an item of the
|
|
* specified type.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::GetItemInfo(
|
|
LPCOLESTR pstrName,
|
|
DWORD dwReturnMask,
|
|
IUnknown ** ppunkItem,
|
|
ITypeInfo ** ppTypeInfo
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
VARIANT varName;
|
|
VARIANT varInterface;
|
|
IDispatch *Interface = NULL;
|
|
BOOLEAN InterfaceNamesLocked = FALSE;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetItemInfo: %ws\n", pstrName));
|
|
VariantInit( &varName );
|
|
VariantInit( &varInterface );
|
|
|
|
|
|
//Use logical ANDs to determine which type(s) of pointer the caller wants,
|
|
//and make sure that that placeholder is currently valid.
|
|
if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
|
|
if (!ppunkItem) {
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
*ppunkItem = NULL;
|
|
}
|
|
if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
|
|
if (!ppTypeInfo) {
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
*ppTypeInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// We didn't capture the array
|
|
// So access it under a try/except
|
|
__try {
|
|
|
|
//
|
|
// Check for the BizRuleContext interface itself
|
|
//
|
|
if (!_wcsicmp( BIZRULE_CONTEXT_INTERFACE_NAME, pstrName)) {
|
|
|
|
//
|
|
// Use the access check interface
|
|
//
|
|
Interface = m_BizRuleContext;
|
|
|
|
//
|
|
// If the caller passed interfaces to access check,
|
|
// look there.
|
|
//
|
|
|
|
} else if ( m_AcContext != NULL && m_AcContext->Interfaces != NULL ) {
|
|
VARIANT HUGEP *varNames;
|
|
ULONG Index;
|
|
LONG InterfacesIndex;
|
|
|
|
//
|
|
// Convert name to an easier form to compare
|
|
//
|
|
|
|
V_VT(&varName) = VT_BSTR;
|
|
V_BSTR(&varName) = (BSTR) pstrName;
|
|
|
|
//
|
|
// Access the variant array directly
|
|
//
|
|
ASSERT( m_AcContext->InterfaceNames != NULL && m_AcContext->InterfaceFlags != NULL );
|
|
|
|
hr = SafeArrayAccessData( m_AcContext->InterfaceNames, (void HUGEP **)&varNames);
|
|
_JumpIfError(hr, Cleanup, "SafeArrayAccessData");
|
|
InterfaceNamesLocked = TRUE;
|
|
|
|
|
|
//
|
|
// Find an interface name that matches the passed in name
|
|
//
|
|
|
|
for ( Index=0; Index<m_AcContext->InterfaceNames->rgsabound[0].cElements; Index++ ) {
|
|
|
|
//
|
|
// Stop at the end of the array
|
|
//
|
|
if ( V_VT(&varNames[Index]) == VT_EMPTY ) {
|
|
break;
|
|
}
|
|
|
|
if ( VarCmp( &varName, &varNames[Index], LOCALE_USER_DEFAULT, NORM_IGNORECASE ) == (HRESULT)VARCMP_EQ ) {
|
|
|
|
//
|
|
// Copy out the array element.
|
|
//
|
|
|
|
InterfacesIndex = m_AcContext->InterfaceLower + Index;
|
|
|
|
hr = SafeArrayGetElement( m_AcContext->Interfaces, &InterfacesIndex, &varInterface );
|
|
_JumpIfError(hr, Cleanup, "SafeArrayGetElement");
|
|
|
|
//
|
|
// Ensure it is an IDispatch interface
|
|
//
|
|
|
|
if ( V_VT( &varInterface ) != VT_DISPATCH ) {
|
|
hr = E_INVALIDARG;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Use the passed in interface
|
|
//
|
|
|
|
Interface = V_DISPATCH( &varInterface );
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If no interface was found,
|
|
// fail
|
|
//
|
|
|
|
if ( Interface == NULL ) {
|
|
hr = TYPE_E_ELEMENTNOTFOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If an interface was found,
|
|
// return the requested information about the interface.
|
|
//
|
|
|
|
hr = S_OK;
|
|
if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
|
|
Interface->QueryInterface(IID_IUnknown, (void **)ppunkItem);
|
|
}
|
|
|
|
if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
|
|
hr = Interface->GetTypeInfo( 0, 0, ppTypeInfo );
|
|
}
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
hr = GetExceptionCode();
|
|
AzPrint((AZD_CRITICAL, "GetItemInfo took an exception: 0x%lx\n", hr));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
//
|
|
// Cleanup from finding interfaces
|
|
//
|
|
if ( m_AcContext != NULL && m_AcContext->Interfaces != NULL ) {
|
|
|
|
//
|
|
// We didn't capture the array
|
|
// So access it under a try/except
|
|
__try {
|
|
if ( InterfaceNamesLocked ) {
|
|
SafeArrayUnaccessData( m_AcContext->InterfaceNames );
|
|
}
|
|
|
|
} __except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
AzPrint((AZD_CRITICAL, "GetItemInfo took an exception: 0x%lx\n", hr));
|
|
}
|
|
|
|
VariantClear(&varName);
|
|
VariantClear(&varInterface);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* GetDocVersionString() -- It is possible, even likely that a script document
|
|
* can be changed between runs. The host can define a unique version number
|
|
* for the script, which can be saved along with the script. If the version
|
|
* changes, the engine will know to recompile the script on the next run.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::GetDocVersionString(BSTR * pbstrVersionString)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetDocVersionString\n"));
|
|
UNREFERENCED_PARAMETER(pbstrVersionString);
|
|
|
|
//For the generic case, this function isn't implemented.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OnScriptTerminate() -- This method may give the host a chance to react when
|
|
* the script terminates. pvarResult give the result of the script or NULL
|
|
* if the script doesn't give a result, and pexcepinfo gives the location of
|
|
* any exceptions raised by the script. Returns S_OK if the calls succeeds.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::OnScriptTerminate(const VARIANT * pvarResult,
|
|
const EXCEPINFO * pexcepinfo)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnScriptTerminate\n"));
|
|
UNREFERENCED_PARAMETER(pvarResult);
|
|
UNREFERENCED_PARAMETER(pexcepinfo);
|
|
|
|
//If something needs to happen when the script terminates, put it here.
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OnStateChange() -- This function gives the host a chance to react when the
|
|
* state of the script engine changes. ssScriptState lets the host know the
|
|
* new state of the machine. Returns S_OK if successful.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::OnStateChange(SCRIPTSTATE ssScriptState)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnStateChange:"));
|
|
|
|
//If something needs to happen when the script enters a certain state,
|
|
//put it here.
|
|
switch (ssScriptState) {
|
|
case SCRIPTSTATE_UNINITIALIZED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Uninitialized.\n"));
|
|
break;
|
|
case SCRIPTSTATE_INITIALIZED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Initialized.\n"));
|
|
break;
|
|
case SCRIPTSTATE_STARTED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Started.\n"));
|
|
break;
|
|
case SCRIPTSTATE_CONNECTED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Connected.\n"));
|
|
break;
|
|
case SCRIPTSTATE_DISCONNECTED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Disconnected.\n"));
|
|
break;
|
|
case SCRIPTSTATE_CLOSED:
|
|
AzPrint((AZD_SCRIPT_MORE, "State: Closed.\n"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OnScriptError() -- This function gives the host a chance to respond when
|
|
* an error occurs while running a script. pase holds a reference to the
|
|
* IActiveScriptError object, which the host can use to get information about
|
|
* the error. Returns S_OK if the error was handled successfully, and an OLE
|
|
* error code if not.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::OnScriptError(
|
|
IActiveScriptError * pscripterror)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT, "CScriptEngine::OnScriptError\n"));
|
|
|
|
ASSERT(pscripterror);
|
|
AssertValid();
|
|
|
|
if ( m_fTimedOut ) {
|
|
m_ScriptError = AZ_HRESULT(ERROR_TIMEOUT);
|
|
}
|
|
|
|
//
|
|
// Only report the first error in this script
|
|
//
|
|
|
|
if ( m_ScriptError == S_OK ) {
|
|
|
|
m_ScriptError = E_UNEXPECTED;
|
|
|
|
if (pscripterror) {
|
|
EXCEPINFO theException;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Get a description of the exception
|
|
//
|
|
|
|
hr = pscripterror->GetExceptionInfo(&theException);
|
|
|
|
if ( FAILED(hr)) {
|
|
|
|
m_ScriptError = hr;
|
|
|
|
//
|
|
// Log the exception
|
|
//
|
|
} else {
|
|
|
|
m_ScriptError = theException.wCode == 0 ?
|
|
theException.scode :
|
|
theException.wCode;
|
|
|
|
AzPrint(( AZD_CRITICAL,
|
|
"Script Error: Code: 0x%lx %ld\n"
|
|
" Src: %ws\n"
|
|
" File: %ws\n"
|
|
" Desc: %ws\n",
|
|
m_ScriptError,
|
|
m_ScriptError,
|
|
theException.bstrSource,
|
|
theException.bstrHelpFile,
|
|
theException.bstrDescription));
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OnEnterScript() -- This function gives the host a chance to respond when
|
|
* the script begins running. Returns S_OK if the call was successful.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::OnEnterScript(void)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnEnterScript\n"));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* OnExitScript() -- This function gives the host a chance to respond when
|
|
* the script finishes running. Returns S_OK if the call was successful.
|
|
******************************************************************************/
|
|
STDMETHODIMP CScriptEngine::OnLeaveScript(void)
|
|
{
|
|
//tracing purposes only
|
|
AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnLeaveScript\n"));
|
|
|
|
return S_OK;
|
|
}
|