Leaked source code of windows server 2003
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

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. bizrule.cxx
  5. Abstract:
  6. Routines implementing Business Rules
  7. Author:
  8. IAcativeScript sample code taken from http://support.microsoft.com/support/kb/articles/Q183/6/98.ASP
  9. and from from \nt\inetsrv\iis\svcs\cmp\asp as originally written by AndrewS
  10. Cliff Van Dyke (cliffv) 18-July-2001
  11. --*/
  12. #include "pch.hxx"
  13. #define AZD_COMPONENT AZD_SCRIPT
  14. //
  15. // Performance measurement globals
  16. //
  17. #define ENABLE_PERF 1
  18. #ifdef ENABLE_PERF
  19. LONG AzGlPerfEngineCacheTries;
  20. LONG AzGlPerfEnginesInFreeScriptList;
  21. LONG AzGlPerfEngineCacheHits;
  22. LONG AzGlPerfEngineFlushes;
  23. LONG AzGlPerfRunningEngineCacheHits;
  24. #endif
  25. //*****************************************************************************
  26. // The following macros are used to catch exceptions thrown from the external
  27. // scripting engines.
  28. //
  29. // Use of TRY/CATCH blocks around calls to the script engine is controlled by
  30. // the DBG compile #define. If DBG is 1, then the TRY/CATCH blocks are NOT
  31. // used so that checked builds break into the debugger and we can examine why
  32. // the error occurred. If DBG is 0, then the TRY/CATCH blocks are used and
  33. // exceptions are captured and logged to the browser (if possible) and the NT
  34. // Event log.
  35. //
  36. // The TRYCATCH macros are:
  37. //
  38. // TRYCATCH(_s, _IFStr)
  39. // _s - statement to execute inside of TRY/CATCH block.
  40. // _IFStr - string containing the name of interface invoked
  41. // TRYCATCH_HR(_s, _hr, _IFStr)
  42. // _s - statement to execute inside of TRY/CATCH block.
  43. // _hr - HRESULT to store return from _s
  44. // _IFStr - string containing the name of interface invoked
  45. //
  46. // NOTES:
  47. // The macros also expect that there is a local variable defined in the function
  48. // the macros is used of type char * named _pFuncName.
  49. //
  50. // A minimal test capability is included to allow for random errors to be throw.
  51. // The test code is compiled in based on the TEST_TRYCATCH #define.
  52. //
  53. //*****************************************************************************
  54. //*****************************************************************************
  55. // TEST_TRYCATCH definitions
  56. //*****************************************************************************
  57. #define TEST_TRYCATCH 0
  58. #if TEST_TRYCATCH
  59. #define THROW_INTERVAL 57
  60. int g_TryCatchCount = 0;
  61. #define TEST_THROW_ERROR g_TryCatchCount++; if ((g_TryCatchCount % THROW_INTERVAL) == 0) {THROW(0x80070000+g_TryCatchCount);}
  62. #else
  63. #define TEST_THROW_ERROR
  64. #endif
  65. //*****************************************************************************
  66. // The following is the heart of the TRYCATCH macros. The definitions here are
  67. // based on the definition of DBG. Again, note that when DBG is off that the
  68. // TRYCATCH defines are removed.
  69. //*****************************************************************************
  70. // ??? We could log it here
  71. #if DBG
  72. #define START_TRYCATCH( _IFStr ) { \
  73. AzPrint((AZD_SCRIPT_MORE, "Calling: %s\n", _IFStr ));
  74. #define END_TRYCATCH(_hr, _IFStr) }
  75. #else // DBG
  76. #define START_TRYCATCH( _IFStr ) __try {
  77. #define END_TRYCATCH(_hr, _IFStr) \
  78. } __except( EXCEPTION_EXECUTE_HANDLER ) { \
  79. _hr = GetExceptionCode(); \
  80. AzPrint((AZD_CRITICAL, "Exception: %s: 0x%lx\n", _IFStr, _hr )); \
  81. }
  82. #endif // DBG
  83. //*****************************************************************************
  84. // Definition of TRYCATCH_INT which is used by all of the TRYCATCH macros
  85. // described above.
  86. //*****************************************************************************
  87. #define TRYCATCH_INT(_s, _hr, _IFStr) \
  88. START_TRYCATCH( _IFStr ) \
  89. TEST_THROW_ERROR \
  90. _hr = _s; \
  91. END_TRYCATCH(_hr, _IFStr)
  92. //*****************************************************************************
  93. // Here are the actual definitions of the TRYCATCH macros described above.
  94. //*****************************************************************************
  95. #define TRYCATCH(_s, _IFStr) \
  96. { \
  97. HRESULT _tempHR; \
  98. TRYCATCH_INT(_s, _tempHR, _IFStr); \
  99. }
  100. #define TRYCATCH_HR(_s, _hr, _IFStr) TRYCATCH_INT(_s, _hr, _IFStr)
  101. //
  102. // Name of the IAzBizRuleContext interface
  103. //
  104. #define BIZRULE_CONTEXT_INTERFACE_NAME L"AzBizRuleContext"
  105. HRESULT
  106. AzpGetScriptEngine(
  107. IN PAZP_TASK Task,
  108. OUT CScriptEngine ** RetScriptEngine
  109. )
  110. /*++
  111. Routine Description:
  112. This routine allocates a script engine to run the bizrule task.
  113. Ideally, we will find an engine in our FreeScript list
  114. and will just hand it out. If there isnt one, then we will look
  115. in the Running Script List and attempt to clone a running script.
  116. Failing that, we will create a new script engine.
  117. Arguments:
  118. Task - Task containing the BizRule to process
  119. RetScriptEngine - Returns a pointer to the script engine to use
  120. The caller should free the engine by calling AzpReturnEngineToCache.
  121. Return Value:
  122. S_OK: a script engine was successfully returned
  123. OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
  124. --*/
  125. {
  126. HRESULT hr;
  127. PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
  128. CScriptEngine *ScriptEngine = NULL;
  129. IActiveScript *ClonedActiveScript = NULL;
  130. DWORD ClonedBizRuleSerialNumber = 0;
  131. PLIST_ENTRY ListEntry;
  132. PLIST_ELEMENT ListElement;
  133. //
  134. // First try to find an engine in the FreeScript list
  135. //
  136. #ifdef ENABLE_PERF
  137. InterlockedIncrement( &AzGlPerfEngineCacheTries );
  138. #endif
  139. SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  140. if ( !IsListEmpty( &Task->FreeScriptHead ) ) {
  141. //
  142. // Try to find an engine that was initialized by this thread
  143. //
  144. for ( ListEntry = Task->FreeScriptHead.Flink;
  145. ListEntry != &Task->FreeScriptHead;
  146. ListEntry = ListEntry->Flink) {
  147. ListElement = CONTAINING_RECORD( ListEntry,
  148. LIST_ELEMENT,
  149. Next );
  150. ASSERT( ListElement->This != NULL );
  151. ScriptEngine = (CScriptEngine *) ListElement->This;
  152. if ( ScriptEngine->IsBaseThread() ) {
  153. break;
  154. }
  155. ScriptEngine = NULL;
  156. AzPrint((AZD_SCRIPT_MORE, "Avoided script engine in non-base thread.\n" ));
  157. }
  158. //
  159. // If not,
  160. // grab the one at the front of the list.
  161. // ResuseEngine will fix it up
  162. //
  163. if ( ScriptEngine == NULL ) {
  164. //
  165. // Remove the entry from the list
  166. //
  167. ListEntry = Task->FreeScriptHead.Flink;
  168. ListElement = CONTAINING_RECORD( ListEntry,
  169. LIST_ELEMENT,
  170. Next );
  171. ASSERT( ListElement->This != NULL );
  172. ScriptEngine = ListElement->This;
  173. AzPrint((AZD_SCRIPT, "Using free script engine from non-base thread.\n" ));
  174. }
  175. ScriptEngine->RemoveListEntry();
  176. ScriptEngine->RemoveLruListEntry(); // Remove from the LRU list too
  177. #ifdef ENABLE_PERF
  178. InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
  179. #endif
  180. // There was a reference for being in the FreeScript list.
  181. // That reference is now ours.
  182. // Drop the crit sect before actually doing anything with the script engine
  183. SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  184. //
  185. // Never re-use an engine that has a script that is not up to date
  186. // The Assert below is overactive. The script was up to date when it was placed
  187. // on the free script list. However, the script may be changing out from under
  188. // us. That's fine. This script will be used one last time then will be ditched
  189. // as we try to put it onto the free script list again.
  190. //
  191. // ASSERT( ScriptEngine->GetBizRuleSerialNumber() == Task->BizRuleSerialNumber );
  192. //
  193. // Update the script engine so it can be reused.
  194. //
  195. hr = ScriptEngine->ReuseEngine();
  196. if (FAILED(hr)) {
  197. AzPrint((AZD_CRITICAL, "ReuseEngine failed: 0x%lx\n", hr ));
  198. goto Cleanup;
  199. }
  200. // Got an engine for sure...so just incr the cache hit count
  201. #ifdef ENABLE_PERF
  202. InterlockedIncrement( &AzGlPerfEngineCacheHits );
  203. #endif
  204. AzPrint((AZD_SCRIPT, "Using free script engine.\n" ));
  205. } else {
  206. SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  207. }
  208. //
  209. // If not found, try to find the engine in the Running script list and clone it
  210. //
  211. if ( ScriptEngine == NULL ) {
  212. CScriptEngine *RunningScriptEngine = NULL;
  213. //
  214. // Search the running script list
  215. //
  216. // This is a different crit sect than FreeScriptCritSect since we actually keep
  217. // this crit sect locked while cloning the engine. That can take a long time
  218. // and we don't want to increase the contention on FreeScriptCritSect.
  219. //
  220. SafeEnterCriticalSection( &Task->RunningScriptCritSect );
  221. for ( ListEntry = Task->RunningScriptHead.Flink;
  222. ListEntry != &Task->RunningScriptHead;
  223. ListEntry = ListEntry->Flink) {
  224. ListElement = CONTAINING_RECORD( ListEntry,
  225. LIST_ELEMENT,
  226. Next );
  227. ASSERT( ListElement->This != NULL );
  228. RunningScriptEngine = (CScriptEngine *) ListElement->This;
  229. //
  230. // If the script engine hasn't GPF'd and
  231. // is running the current version of the bizrule,
  232. // use it.
  233. //
  234. if ( !RunningScriptEngine->FIsCorrupted() &&
  235. RunningScriptEngine->GetBizRuleSerialNumber() == Task->BizRuleSerialNumber ) {
  236. ClonedBizRuleSerialNumber = Task->BizRuleSerialNumber;
  237. break;
  238. }
  239. RunningScriptEngine = NULL;
  240. }
  241. //
  242. // If we didn't find a script engine,
  243. // we're done.
  244. //
  245. if ( RunningScriptEngine == NULL ) {
  246. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  247. //
  248. // If we found a running script engine,
  249. // clone it.
  250. } else {
  251. IActiveScript *pAS;
  252. RunningScriptEngine->AssertValid();
  253. //
  254. // Clone the engine
  255. //
  256. pAS = RunningScriptEngine->GetActiveScript();
  257. ASSERT(pAS != NULL);
  258. hr = pAS->Clone( &ClonedActiveScript );
  259. // We've cloned the engine, we can let go of the CS
  260. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  261. //
  262. // Scripting engines are not required to implement clone. If we get an error,
  263. // just continue on and create a new engine
  264. //
  265. if (FAILED(hr)) {
  266. ASSERT(hr == E_NOTIMPL); // I only expect E_NOTIMPL
  267. ClonedActiveScript = NULL;
  268. } else {
  269. #ifdef ENABLE_PERF
  270. InterlockedIncrement( &AzGlPerfRunningEngineCacheHits );
  271. #endif
  272. AzPrint((AZD_SCRIPT, "Using clone of running script engine.\n" ));
  273. }
  274. }
  275. }
  276. //
  277. // If we couldn't find any script engine to reuse,
  278. // allocate a new one.
  279. //
  280. if ( ScriptEngine == NULL) {
  281. //
  282. // Allocate the object
  283. //
  284. ScriptEngine = new CScriptEngine;
  285. if ( ScriptEngine == NULL ) {
  286. hr = E_OUTOFMEMORY;
  287. AzPrint((AZD_CRITICAL, "new CScriptEngine failed: 0x%lx\n", hr ));
  288. goto Cleanup;
  289. }
  290. //
  291. // Initialize it
  292. //
  293. hr = ScriptEngine->Init( Task, ClonedActiveScript, ClonedBizRuleSerialNumber );
  294. if (FAILED(hr)) {
  295. ClonedActiveScript = NULL; // ::Init always steals this reference
  296. AzPrint((AZD_CRITICAL, "ScriptEngine->Init failed: 0x%lx\n", hr ));
  297. goto Cleanup;
  298. }
  299. if ( ClonedActiveScript == NULL ) {
  300. AzPrint((AZD_SCRIPT, "Using new script engine.\n" ));
  301. }
  302. ClonedActiveScript = NULL; // ::Init always steals this reference
  303. }
  304. //
  305. // Put the script engine onto the RunningScripts list.
  306. //
  307. ScriptEngine->AssertValid();
  308. SafeEnterCriticalSection( &Task->RunningScriptCritSect );
  309. ScriptEngine->InsertHeadList( &Task->RunningScriptHead );
  310. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  311. //
  312. // Return the script engine to the caller
  313. //
  314. // The reference for being in the running script list is shared with our caller.
  315. //
  316. hr = S_OK;
  317. *RetScriptEngine = ScriptEngine;
  318. ScriptEngine = NULL;
  319. Cleanup:
  320. ASSERT(SUCCEEDED(hr) || hr == TYPE_E_ELEMENTNOTFOUND || hr == OLESCRIPT_E_SYNTAX || hr == E_OUTOFMEMORY );
  321. if ( ScriptEngine != NULL ) {
  322. ScriptEngine->FinalRelease();
  323. }
  324. if ( ClonedActiveScript != NULL ) {
  325. ClonedActiveScript->Release();
  326. }
  327. return (hr);
  328. }
  329. VOID
  330. AzpReturnEngineToCache(
  331. IN PAZP_TASK Task,
  332. IN CScriptEngine *ScriptEngine
  333. )
  334. /*++
  335. Routine Description:
  336. Caller is done with the engine. Return it to the cache.
  337. Arguments:
  338. Task - Task containing the BizRule to process
  339. ScriptEngine - Specifies the script engine to free
  340. Return Value:
  341. S_OK: a script engine was successfully returned
  342. --*/
  343. {
  344. HRESULT hr;
  345. PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
  346. ASSERT( ScriptEngine != NULL);
  347. //
  348. // Remove the engine from the Running Script List
  349. //
  350. SafeEnterCriticalSection( &Task->RunningScriptCritSect );
  351. ScriptEngine->RemoveListEntry();
  352. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  353. //
  354. // We want to reuse this engine. Try to return it to the "Uninitialized"
  355. // state. Some engine languages arent able to do this. If it fails, deallocate
  356. // the engine; it cant be reused.
  357. //
  358. hr = ScriptEngine->ResetToUninitialized();
  359. if (FAILED(hr)) {
  360. // Engine doesnt support this, sigh. Deallocate and continue.
  361. AzPrint((AZD_CRITICAL, "Engine doesn't support reset: 0x%lx\n", hr ));
  362. goto Cleanup;
  363. }
  364. //
  365. // If the script changed while this engine was running,
  366. // Or if there was a GPF while then engine was running,
  367. // then it might be in a corrupted state.
  368. //
  369. // In either case, delete the engine.
  370. //
  371. SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  372. SafeEnterCriticalSection( &Task->RunningScriptCritSect );
  373. if ( ScriptEngine->GetBizRuleSerialNumber() != Task->BizRuleSerialNumber ||
  374. ScriptEngine->FIsCorrupted() ) {
  375. SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  376. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  377. AzPrint((AZD_SCRIPT, "Script changed while engine was running\n" ));
  378. goto Cleanup;
  379. }
  380. //
  381. // Add the script to the free script list for potential re-use.
  382. //
  383. ScriptEngine->InsertHeadList( &Task->FreeScriptHead );
  384. ScriptEngine->InsertHeadLruList();
  385. ScriptEngine = NULL;
  386. #ifdef ENABLE_PERF
  387. InterlockedIncrement( &AzGlPerfEnginesInFreeScriptList );
  388. #endif
  389. //
  390. // If there are too many scripts cached,
  391. // delete the least recently used engine.
  392. //
  393. if ( AzAuthorizationStore->LruFreeScriptCount > AzAuthorizationStore->MaxScriptEngines ) {
  394. PLIST_ENTRY ListEntry;
  395. PLIST_ELEMENT ListElement;
  396. AzPrint((AZD_SCRIPT, "Script LRU'ed out: %ld\n", AzAuthorizationStore->MaxScriptEngines ));
  397. //
  398. // Grab the entry from the tail of the list
  399. //
  400. ListEntry = AzAuthorizationStore->LruFreeScriptHead.Blink;
  401. ListElement = CONTAINING_RECORD( ListEntry,
  402. LIST_ELEMENT,
  403. Next );
  404. ASSERT( ListElement->This != NULL );
  405. ScriptEngine = ListElement->This;
  406. //
  407. // Delink it
  408. //
  409. ScriptEngine->RemoveListEntry();
  410. ScriptEngine->RemoveLruListEntry(); // Remove from the LRU list too
  411. #ifdef ENABLE_PERF
  412. InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
  413. #endif
  414. }
  415. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  416. SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  417. Cleanup:
  418. if ( ScriptEngine != NULL ) {
  419. ScriptEngine->FinalRelease();
  420. }
  421. return;
  422. }
  423. VOID
  424. AzpFlushBizRule(
  425. IN PAZP_TASK Task
  426. )
  427. /*++
  428. Routine Description:
  429. This routine flushes the bizrule cache.
  430. This routine is called whenever the bizrule script is changed
  431. Arguments:
  432. Task - Task containing the BizRule to flush
  433. BizRuleResult - Result of the bizrule
  434. Return Value:
  435. NO_ERROR - The operation was successful
  436. ERROR_NOT_ENOUGH_MEMORY - not enough memory
  437. OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
  438. Other exception status codes
  439. --*/
  440. {
  441. PAZP_AZSTORE AzAuthorizationStore = Task->GenericObject.AzStoreObject;
  442. PLIST_ENTRY ListEntry;
  443. PLIST_ELEMENT ListElement;
  444. CScriptEngine *ScriptEngine = NULL;
  445. //
  446. // Delete engines on the Free Script List
  447. // Engines on the running script list are deleted as they finish running
  448. //
  449. SafeEnterCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  450. while ( !IsListEmpty( &Task->FreeScriptHead ) ) {
  451. //
  452. // Remove the entry from the list
  453. //
  454. ListEntry = Task->FreeScriptHead.Flink;
  455. ListElement = CONTAINING_RECORD( ListEntry,
  456. LIST_ELEMENT,
  457. Next );
  458. ASSERT( ListElement->This != NULL );
  459. ScriptEngine = ListElement->This;
  460. ScriptEngine->RemoveListEntry();
  461. ScriptEngine->RemoveLruListEntry(); // Remove it from the LRU list too
  462. ScriptEngine->FinalRelease();
  463. #ifdef ENABLE_PERF
  464. InterlockedDecrement( &AzGlPerfEnginesInFreeScriptList );
  465. InterlockedIncrement( &AzGlPerfEngineFlushes );
  466. #endif
  467. AzPrint((AZD_SCRIPT, "Script Freed from free script list\n" ));
  468. }
  469. SafeLeaveCriticalSection( &AzAuthorizationStore->FreeScriptCritSect );
  470. return;
  471. }
  472. DWORD
  473. AzpProcessBizRule(
  474. IN PACCESS_CHECK_CONTEXT AcContext,
  475. IN PAZP_TASK Task,
  476. OUT PBOOL BizRuleResult
  477. )
  478. /*++
  479. Routine Description:
  480. This routine is the main bizrule routine that determine whether a bizrule is satisfied.
  481. If the AzAuthorizationStore object's script engine timeout has been set to 0, then return
  482. the biz rule result as false without processing the biz rule.
  483. On entry, AcContext->ClientContext.CritSect must be locked.
  484. Arguments:
  485. AcContext - Specifies the context of the accesscheck operation the bizrule is being evaluated for
  486. Task - Task containing the BizRule to process
  487. BizRuleResult - Result of the bizrule
  488. Return Value:
  489. NO_ERROR - The operation was successful
  490. ERROR_NOT_ENOUGH_MEMORY - not enough memory
  491. OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
  492. Other exception status codes
  493. --*/
  494. {
  495. DWORD WinStatus;
  496. HRESULT hr;
  497. CScriptEngine *ScriptEngine = NULL;
  498. // BOOL fCoInit = FALSE;
  499. //
  500. // Check Root AzAuthorizationStore object's script engine timeout parameter
  501. //
  502. if ( (Task->GenericObject).AzStoreObject->ScriptEngineTimeout == 0 ) {
  503. WinStatus = NO_ERROR;
  504. *BizRuleResult = FALSE;
  505. goto Cleanup;
  506. }
  507. //
  508. // Initialization
  509. //
  510. ASSERT( AzpIsCritsectLocked( &AcContext->ClientContext->CritSect ) );
  511. *BizRuleResult = FALSE;
  512. #if 0
  513. // init COM
  514. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  515. ASSERT( hr != S_OK ); // engine caching breaks if com isn't already initialized
  516. if (hr == S_OK || hr == S_FALSE) {
  517. fCoInit = TRUE;
  518. } else if (hr != RPC_E_CHANGED_MODE) {
  519. WinStatus = AzpHresultToWinStatus(hr);
  520. AzPrint((AZD_CRITICAL, "CoInitializeEx failed: 0x%lx, %ld\n", hr, WinStatus));
  521. goto Cleanup;
  522. }
  523. #endif // 0
  524. //
  525. // Get a script engine to run the script in
  526. //
  527. hr = AzpGetScriptEngine( Task, &ScriptEngine );
  528. if (FAILED(hr)) {
  529. WinStatus = AzpHresultToWinStatus(hr);
  530. AzPrint((AZD_CRITICAL, "AzpGetScriptEngine failed: 0x%lx, %ld\n", hr, WinStatus));
  531. goto Cleanup;
  532. }
  533. //
  534. // Run the script
  535. //
  536. hr = ScriptEngine->RunScript( AcContext, BizRuleResult );
  537. if (FAILED(hr)) {
  538. WinStatus = AzpHresultToWinStatus(hr);
  539. AzPrint((AZD_CRITICAL, "RunScript failed: 0x%lx, %ld\n", hr, WinStatus));
  540. *BizRuleResult = FALSE;
  541. goto Cleanup;
  542. }
  543. WinStatus = NO_ERROR;
  544. //
  545. // Free any local resources
  546. //
  547. Cleanup:
  548. if (ScriptEngine != NULL) {
  549. AzpReturnEngineToCache( Task, ScriptEngine );
  550. }
  551. #if 0
  552. if (fCoInit) {
  553. CoUninitialize();
  554. }
  555. #endif // 0
  556. return WinStatus;
  557. }
  558. DWORD
  559. AzpParseBizRule(
  560. IN PAZP_TASK Task
  561. )
  562. /*++
  563. Routine Description:
  564. This routine parses a bizrule to see if it is syntactically valid.
  565. Arguments:
  566. Task - Task containing the BizRule to parse
  567. Return Value:
  568. NO_ERROR - The operation was successful
  569. ERROR_NOT_ENOUGH_MEMORY - not enough memory
  570. OLESCRIPT_E_SYNTAX - The syntax of the bizrule is invalid
  571. --*/
  572. {
  573. DWORD WinStatus;
  574. HRESULT hr;
  575. CScriptEngine *ScriptEngine = NULL;
  576. BOOL fCoInit = FALSE;
  577. //
  578. // Initialization
  579. //
  580. // init COM
  581. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  582. ASSERT( hr != S_OK ); // engine caching breaks if com isn't already initialized
  583. if (hr == S_OK || hr == S_FALSE) {
  584. fCoInit = TRUE;
  585. } else if (hr != RPC_E_CHANGED_MODE) {
  586. WinStatus = AzpHresultToWinStatus(hr);
  587. AzPrint((AZD_CRITICAL, "CoInitializeEx failed: 0x%lx, %ld\n", hr, WinStatus));
  588. goto Cleanup;
  589. }
  590. //
  591. // Get a script engine to run the script in
  592. //
  593. hr = AzpGetScriptEngine( Task, &ScriptEngine );
  594. if (FAILED(hr)) {
  595. WinStatus = AzpHresultToWinStatus(hr);
  596. AzPrint((AZD_CRITICAL, "AzpGetScriptEngine failed: 0x%lx, %ld\n", hr, WinStatus));
  597. goto Cleanup;
  598. }
  599. WinStatus = NO_ERROR;
  600. //
  601. // Free any local resources
  602. //
  603. Cleanup:
  604. if (ScriptEngine != NULL) {
  605. AzpReturnEngineToCache( Task, ScriptEngine );
  606. }
  607. if (fCoInit) {
  608. CoUninitialize();
  609. }
  610. return WinStatus;
  611. }
  612. VOID
  613. AzpInterruptScript(
  614. PVOID Parameter,
  615. BOOLEAN TimerFired
  616. )
  617. /*++
  618. Routine Description:
  619. This is the callback routine that fires when a script takes too long to execute
  620. Arguments:
  621. Parameter - "This" pointer for the script that took too long
  622. TimerFired - Not used
  623. Return Value:
  624. None
  625. --*/
  626. {
  627. CScriptEngine *ScriptEngine = (CScriptEngine *)Parameter;
  628. UNREFERENCED_PARAMETER( TimerFired );
  629. AzPrint((AZD_SCRIPT, "Script Timed out.\n"));
  630. ScriptEngine->InterruptScript();
  631. }
  632. //Constructor
  633. CScriptEngine::CScriptEngine()
  634. {
  635. //
  636. // Fields are initialized below in the order they are defined
  637. //
  638. m_cRef = 1;
  639. m_Task = NULL;
  640. ::InitializeListHead( &m_Next.Next );
  641. m_Next.This = this;
  642. ::InitializeListHead( &m_LruNext.Next );
  643. m_LruNext.This = this;
  644. m_Engine = NULL;
  645. m_Parser = NULL;
  646. m_BizRuleContext = NULL;
  647. m_AcContext = NULL;
  648. m_BaseThread = 0;
  649. m_BizRuleSerialNumber = 0;
  650. m_ScriptError = S_OK;
  651. m_fInited = FALSE;
  652. m_fCorrupted = FALSE;
  653. m_fTimedOut = FALSE;
  654. m_bCaseSensitive = TRUE;
  655. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine\n"));
  656. }
  657. //Destructor
  658. CScriptEngine::~CScriptEngine()
  659. {
  660. AzPrint((AZD_SCRIPT_MORE, "~CScriptEngine\n"));
  661. //
  662. // Assert that FinalRelease was called
  663. //
  664. ASSERT( m_Engine == NULL );
  665. ASSERT( m_Parser == NULL );
  666. ASSERT( m_BizRuleContext == NULL );
  667. ASSERT( m_AcContext == NULL );
  668. ASSERT( IsListEmpty( &m_Next.Next ) );
  669. }
  670. HRESULT
  671. CScriptEngine::Init(
  672. IN PAZP_TASK Task,
  673. IN IActiveScript *ClonedActiveScript OPTIONAL,
  674. IN DWORD ClonedBizRuleSerialNumber OPTIONAL
  675. )
  676. /*++
  677. Routine Description:
  678. This routine creates the ActiveX Scripting Engine and initializes it.
  679. Arguments:
  680. Task - Task containing the BizRule to process
  681. ClonedActiveScript - Pointer to an instance of a cloned script engine.
  682. NULL: This is a new script engine and not a clone.
  683. This routine always steals ClonedActiveScript and will always release it sooner
  684. or later. The caller should not use this argument after making this call.
  685. ClonedBizRuleSerialNumber - Serial number of the bizrule parsed by ClonedActiveScript.
  686. Return Value:
  687. HRESULT status of the operation
  688. --*/
  689. {
  690. HRESULT hr;
  691. LPWSTR BizRule = NULL;
  692. EXCEPINFO ParseExceptionInfo;
  693. //
  694. // Initialization
  695. //
  696. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InitializeScriptEngine\n"));
  697. if ( m_fInited ) {
  698. ASSERT(!m_fInited);
  699. hr = AZ_HRESULT(ERROR_ALREADY_INITIALIZED);
  700. if ( ClonedActiveScript != NULL ) {
  701. ClonedActiveScript->Release();
  702. }
  703. goto Cleanup;
  704. }
  705. m_Task = Task;
  706. //
  707. // If this is a clone,
  708. // simply steal the passed in pointer to the script engine.
  709. //
  710. if ( ClonedActiveScript != NULL ) {
  711. m_Engine = ClonedActiveScript;
  712. //
  713. // If this isn't a clone,
  714. // instantiate a script engine
  715. //
  716. } else {
  717. //
  718. // Convert the language name to a Clsid
  719. //
  720. if ( IsEqualGUID( Task->BizRuleLanguageClsid, AzGlZeroGuid ) ) {
  721. hr = CLSIDFromProgID( Task->BizRuleLanguage.String, &Task->BizRuleLanguageClsid);
  722. if (FAILED(hr)) {
  723. AzPrint((AZD_CRITICAL, "Failed to get scripting engine CLSID: 0x%lx\n", hr));
  724. goto Cleanup;
  725. }
  726. //
  727. // determine if our language is case-sensitive or not
  728. //
  729. m_bCaseSensitive = _wcsicmp(Task->BizRuleLanguage.String, L"VBScript") != 0;
  730. }
  731. //
  732. // Create the scripting engine with a call to CoCreateInstance,
  733. // placing the created engine in m_Engine.
  734. //
  735. hr = CoCreateInstance( Task->BizRuleLanguageClsid,
  736. NULL, // Not part of an aggregate
  737. CLSCTX_INPROC_SERVER, // In process
  738. IID_IActiveScript,
  739. (void **)&m_Engine);
  740. if (FAILED(hr)) {
  741. AzPrint((AZD_CRITICAL, "Failed to create scripting engine: 0x%lx\n", hr));
  742. goto Cleanup;
  743. }
  744. }
  745. //
  746. // Query for the IActiveScriptParse interface of the engine
  747. //
  748. TRYCATCH_HR(m_Engine->QueryInterface(IID_IActiveScriptParse, (void **)&m_Parser), hr, "IActiveScript::QueryInterface()");
  749. if (FAILED(hr)) {
  750. AzPrint((AZD_CRITICAL, "Engine doesn't support IActiveScriptParse: 0x%lx\n", hr));
  751. goto Cleanup;
  752. } else if ( m_Parser == NULL ) {
  753. hr = E_FAIL;
  754. goto Cleanup;
  755. }
  756. //
  757. // Create an object for the script to interact with.
  758. //
  759. hr = CoCreateInstance( CLSID_AzBizRuleContext,
  760. NULL, // Not part of an aggregate
  761. CLSCTX_INPROC_SERVER, // In process
  762. IID_IAzBizRuleContext,
  763. (void **)&m_BizRuleContext);
  764. if (FAILED(hr)) {
  765. AzPrint((AZD_CRITICAL, "Failed to create AzBizRuleContext instance: 0x%lx\n", hr));
  766. goto Cleanup;
  767. }
  768. //
  769. // Remember the thread ID of this thread.
  770. // Only this thread can re-use the engine without going to unitialized state first
  771. //
  772. TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &m_BaseThread), hr, "IActiveScript::GetCurrentScriptThreadID()");
  773. if (FAILED(hr)) {
  774. AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
  775. goto Cleanup;
  776. }
  777. AzPrint((AZD_SCRIPT, "Set ThreadId to: 0x%lx 0x%lx\n", this, m_BaseThread));
  778. //
  779. // The engine needs to know the host it runs on.
  780. //
  781. // Once we've set the script site, IActiveScript can call back
  782. m_fInited = TRUE;
  783. TRYCATCH_HR(m_Engine->SetScriptSite((IActiveScriptSite *) this), hr, "IActiveScript::SetScriptSite()");
  784. if (FAILED(hr)) {
  785. AzPrint((AZD_CRITICAL, "Error calling SetScriptSite: 0x%lx\n", hr));
  786. m_fInited = FALSE;
  787. goto Cleanup;
  788. }
  789. //
  790. // If this isn't a clone,
  791. // the parse the script.
  792. //
  793. if ( ClonedActiveScript == NULL ) {
  794. //
  795. // Initialize the script engine so it's ready to run.
  796. //
  797. TRYCATCH_HR(m_Parser->InitNew(), hr, "IActiveScriptParse::InitNew()");
  798. if (FAILED(hr)) {
  799. AzPrint((AZD_CRITICAL, "Error calling InitNew: 0x%lx\n", hr));
  800. goto Cleanup;
  801. }
  802. //
  803. // Add the name of the object that will respond to the script
  804. //
  805. TRYCATCH_HR( m_Engine->AddNamedItem( BIZRULE_CONTEXT_INTERFACE_NAME,
  806. SCRIPTITEM_ISPERSISTENT | SCRIPTITEM_ISVISIBLE ),
  807. hr,
  808. "IActiveScript::AddNamedItem()" );
  809. if (FAILED(hr)) {
  810. AzPrint((AZD_CRITICAL, "Failed to AddNamedItem: 0x%lx\n", hr));
  811. goto Cleanup;
  812. }
  813. //
  814. // Grab a copy of the bizrule
  815. //
  816. SafeEnterCriticalSection( &Task->RunningScriptCritSect );
  817. SafeAllocaAllocate( BizRule, Task->BizRule.StringSize );
  818. if ( BizRule == NULL ) {
  819. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  820. hr = E_OUTOFMEMORY;
  821. goto Cleanup;
  822. }
  823. RtlCopyMemory( BizRule, Task->BizRule.String, Task->BizRule.StringSize );
  824. m_BizRuleSerialNumber = Task->BizRuleSerialNumber;
  825. SafeLeaveCriticalSection( &Task->RunningScriptCritSect );
  826. //
  827. //
  828. // Pass the script to be run to the script engine.
  829. // I don't know what SCRIPTTEXT_HOSTMANAGESSOURCE is but IIS sets it so I will.
  830. //
  831. TRYCATCH_HR( m_Parser->ParseScriptText( BizRule,
  832. NULL, // pstrItemName
  833. NULL, // punkContext
  834. NULL, // pstrDelimiter
  835. 0, // dwSourceContextCookie
  836. 1, // ulStartingLineNumber
  837. SCRIPTTEXT_ISPERSISTENT | SCRIPTTEXT_HOSTMANAGESSOURCE,
  838. NULL, // pvarResult
  839. &ParseExceptionInfo), // exception info filled in on error
  840. hr,
  841. "IActiveScriptParse::ParseScriptText()");
  842. if (FAILED(hr)) {
  843. AzPrint((AZD_CRITICAL, "Failed to ParseScriptText: 0x%lx\n", hr));
  844. goto Cleanup;
  845. }
  846. //
  847. // If it is a clone,
  848. // clone the bizrule serial number.
  849. //
  850. } else {
  851. m_BizRuleSerialNumber = ClonedBizRuleSerialNumber;
  852. }
  853. //
  854. // Done initialization
  855. //
  856. AssertValid();
  857. hr = S_OK;
  858. Cleanup:
  859. SafeAllocaFree( BizRule );
  860. if ( FAILED(hr)) {
  861. // Note: Our caller has to call FinalRelease which will do most of the cleanup
  862. }
  863. return hr;
  864. }
  865. HRESULT
  866. CScriptEngine::RunScript(
  867. IN PACCESS_CHECK_CONTEXT AcContext,
  868. OUT PBOOL BizRuleResult
  869. )
  870. /*++
  871. Routine Description:
  872. This routine runs an initialized script
  873. Arguments:
  874. AcContext - Specifies the context of the accesscheck operation the bizrule is being evaluated for
  875. BizRuleResult - Result of the bizrule
  876. Return Value:
  877. HRESULT describing whether the operation worked
  878. --*/
  879. {
  880. HRESULT hr;
  881. PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
  882. BOOLEAN InterfaceNamesLocked = FALSE;
  883. BOOLEAN InterfaceFlagsLocked = FALSE;
  884. VARIANT varName;
  885. VARIANT varFlag;
  886. HANDLE TimerHandle = INVALID_HANDLE_VALUE;
  887. HRESULT ScriptErrorFromAccessCheck = S_OK;
  888. //
  889. // Initialization
  890. //
  891. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RunScript\n"));
  892. AssertValid();
  893. m_ScriptError = S_OK;
  894. m_AcContext = AcContext;
  895. VariantInit(&varName);
  896. VariantInit(&varFlag);
  897. //
  898. // Tell the access check object the context of the current access check
  899. //
  900. SetAccessCheckContext( static_cast<CAzBizRuleContext *> (m_BizRuleContext), m_bCaseSensitive, AcContext, BizRuleResult, &ScriptErrorFromAccessCheck );
  901. //
  902. // Add each interface that the caller passed to access check
  903. //
  904. if ( AcContext->Interfaces != NULL ) {
  905. VARIANT HUGEP *varNames;
  906. VARIANT HUGEP *varFlags;
  907. ULONG Index;
  908. //
  909. // We didn't capture the array
  910. // So access it under a try/except
  911. __try {
  912. //
  913. // Access the variant arrays directly
  914. //
  915. ASSERT( AcContext->InterfaceNames != NULL && AcContext->InterfaceFlags != NULL );
  916. hr = SafeArrayAccessData( AcContext->InterfaceNames, (void HUGEP **)&varNames);
  917. _JumpIfError(hr, Cleanup, "SafeArrayAccessData");
  918. InterfaceNamesLocked = TRUE;
  919. hr = SafeArrayAccessData( AcContext->InterfaceFlags, (void HUGEP **)&varFlags);
  920. _JumpIfError(hr, Cleanup, "SafeArrayAccessData");
  921. InterfaceFlagsLocked = TRUE;
  922. //
  923. // Loop adding each name
  924. //
  925. for ( Index=0; Index<AcContext->InterfaceNames->rgsabound[0].cElements; Index++ ) {
  926. //
  927. // Stop at the end of the array
  928. //
  929. if ( V_VT(&varNames[Index]) == VT_EMPTY ) {
  930. break;
  931. }
  932. //
  933. // Convert the interface name to a BSTR
  934. //
  935. hr = VariantChangeType(&varName, &varNames[Index], 0, VT_BSTR);
  936. _JumpIfError(hr, Cleanup, "VariantChangeType");
  937. //
  938. // Convert the flags to a LONG
  939. //
  940. hr = VariantChangeType(&varFlag, &varFlags[Index], 0, VT_I4 );
  941. _JumpIfError(hr, Cleanup, "VariantChangeType");
  942. //
  943. // Add the name of the object that will respond to the script
  944. // Ignore SCRIPTITEM_ISPERSISTENT since that would invalidate our caching.
  945. //
  946. TRYCATCH_HR( m_Engine->AddNamedItem(
  947. V_BSTR(&varName),
  948. (V_I4(&varFlag) | SCRIPTITEM_ISVISIBLE) & ~SCRIPTITEM_ISPERSISTENT ),
  949. hr,
  950. "IActiveScript::AddNamedItem()" );
  951. if (FAILED(hr)) {
  952. AzPrint((AZD_CRITICAL, "Failed to AddNamedItem: 0x%lx\n", hr));
  953. goto Cleanup;
  954. }
  955. //
  956. // Clean up
  957. //
  958. VariantClear(&varName);
  959. VariantClear(&varFlag);
  960. }
  961. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  962. hr = GetExceptionCode();
  963. AzPrint((AZD_CRITICAL, "RunScript took an exception: 0x%lx\n", hr));
  964. goto Cleanup;
  965. }
  966. }
  967. //
  968. // Queue a timer to kill the engine if it takes too long
  969. //
  970. m_fTimedOut = FALSE;
  971. if ( !CreateTimerQueueTimer(
  972. &TimerHandle,
  973. AzAuthorizationStore->ScriptEngineTimerQueue,
  974. AzpInterruptScript,
  975. this,
  976. AzAuthorizationStore->ScriptEngineTimeout,
  977. 0, // Not a period timer
  978. 0 ) ) { // No special flags
  979. hr = AZ_HRESULT(GetLastError());
  980. goto Cleanup;
  981. }
  982. //
  983. // Tell the engine to start processing the script with a call to
  984. // SetScriptState().
  985. //
  986. __try {
  987. hr = m_Engine->SetScriptState( SCRIPTSTATE_STARTED );
  988. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  989. hr = GetExceptionCode();
  990. AzPrint((AZD_CRITICAL, "Script took an exception: 0x%lx\n", hr));
  991. // Dont reuse the engine
  992. m_fCorrupted = TRUE;
  993. }
  994. if (FAILED(hr)) {
  995. AzPrint((AZD_CRITICAL, "Failed to SetScriptState(STARTED): 0x%lx\n", hr));
  996. goto Cleanup;
  997. }
  998. //
  999. // See if IAzBizRuleContext detected an error
  1000. //
  1001. if ( ScriptErrorFromAccessCheck != S_OK ) {
  1002. hr = ScriptErrorFromAccessCheck;
  1003. AzPrint((AZD_CRITICAL, "Script from access check to caller: 0x%lx\n", hr));
  1004. goto Cleanup;
  1005. }
  1006. //
  1007. // See if the script had an error
  1008. //
  1009. if ( m_ScriptError != S_OK ) {
  1010. hr = m_ScriptError;
  1011. AzPrint((AZD_SCRIPT, "Return script error to caller: 0x%lx\n", hr));
  1012. goto Cleanup;
  1013. }
  1014. hr = S_OK;
  1015. Cleanup:
  1016. //
  1017. // Delete any timer
  1018. if ( TimerHandle != INVALID_HANDLE_VALUE ) {
  1019. if ( !DeleteTimerQueueTimer(
  1020. AzAuthorizationStore->ScriptEngineTimerQueue,
  1021. TimerHandle,
  1022. INVALID_HANDLE_VALUE ) ) {
  1023. AzPrint((AZD_CRITICAL, "Cannot DeleteTimerQueurTimer: %ld\n", GetLastError() ));
  1024. }
  1025. }
  1026. //
  1027. // Cleanup from adding interfaces
  1028. //
  1029. if ( AcContext->Interfaces != NULL ) {
  1030. //
  1031. // We didn't capture the array
  1032. // So access it under a try/except
  1033. __try {
  1034. if ( InterfaceNamesLocked ) {
  1035. SafeArrayUnaccessData( AcContext->InterfaceNames );
  1036. }
  1037. if ( InterfaceFlagsLocked ) {
  1038. SafeArrayUnaccessData( AcContext->InterfaceFlags );
  1039. }
  1040. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  1041. AzPrint((AZD_CRITICAL, "RunScript took an exception: 0x%lx\n", hr));
  1042. }
  1043. VariantClear(&varName);
  1044. VariantClear(&varFlag);
  1045. }
  1046. //
  1047. // Tell the access check object to forget about the current access check
  1048. //
  1049. SetAccessCheckContext( static_cast<CAzBizRuleContext *> (m_BizRuleContext), TRUE, NULL, NULL, NULL );
  1050. return hr;
  1051. }
  1052. HRESULT
  1053. CScriptEngine::InterruptScript(
  1054. VOID
  1055. )
  1056. /*++
  1057. Routine Description:
  1058. This routine runs stops a currently running script
  1059. Arguments:
  1060. None
  1061. Return Value:
  1062. HRESULT describing whether the operation worked
  1063. --*/
  1064. {
  1065. HRESULT hr;
  1066. EXCEPINFO excepinfo;
  1067. //tracing purposes only
  1068. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InterruptScript\n"));
  1069. AssertValid();
  1070. //
  1071. // Fill in the excepinfo. This will be passed to OnScriptError
  1072. //
  1073. RtlZeroMemory( &excepinfo, sizeof(EXCEPINFO));
  1074. excepinfo.wCode = ERROR_TIMEOUT;
  1075. m_fTimedOut = TRUE;
  1076. //
  1077. // Blow the script away
  1078. //
  1079. TRYCATCH_HR(m_Engine->InterruptScriptThread(
  1080. SCRIPTTHREADID_BASE, // The thread in which the engine was instantiated
  1081. &excepinfo,
  1082. 0 ),
  1083. hr,
  1084. "IActiveScript::InterruptScriptThread()");
  1085. return hr;
  1086. }
  1087. HRESULT
  1088. CScriptEngine::ResetToUninitialized()
  1089. /*++
  1090. Routine Description:
  1091. When we want to reuse an engine, we reset it to an uninited state
  1092. before putting it on the FreeScript list
  1093. Arguments:
  1094. None
  1095. Return Value:
  1096. HRESULT describing whether the operation worked
  1097. --*/
  1098. {
  1099. HRESULT hr = S_OK;
  1100. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::ResetToUninitialized\n"));
  1101. //
  1102. // Release interfaces, they will need to be re-gotten when
  1103. // the engine is reused
  1104. //
  1105. if (m_Parser) {
  1106. TRYCATCH(m_Parser->Release(), "IActiveScriptParse::Release()");
  1107. m_Parser = NULL;
  1108. }
  1109. //
  1110. // Set the script state to Uninitialized
  1111. // IIS sets the state to uninitialized here. That does not give good performance.
  1112. // Setting it to initialized give better performance as long as the next thread to
  1113. // reuse the engine comes in on the same thread.
  1114. //
  1115. if (m_Engine) {
  1116. TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_INITIALIZED ), hr, "IActiveScript::SetScriptState()");
  1117. if (FAILED(hr)) {
  1118. AzPrint((AZD_CRITICAL, "Failed to SetScriptState(INITIALIZED): 0x%lx\n", hr));
  1119. }
  1120. }
  1121. //
  1122. // Indicate the the bizrule is no longer associated with a particular access check
  1123. // ... Can't do this until the script state is set
  1124. //
  1125. m_AcContext = NULL;
  1126. return (hr);
  1127. }
  1128. HRESULT
  1129. CScriptEngine::ReuseEngine(
  1130. VOID
  1131. )
  1132. /*++
  1133. Routine Description:
  1134. When reusing an engine from the FreeScript list, this routine is called
  1135. to set the script engine to the initialized state.
  1136. Basically, this routine undoes the effects of ResetToUninitialized.
  1137. Arguments:
  1138. None
  1139. Return Value:
  1140. HRESULT describing whether the operation worked
  1141. --*/
  1142. {
  1143. HRESULT hr;
  1144. SCRIPTSTATE nScriptState = SCRIPTSTATE_UNINITIALIZED;
  1145. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::ReuseEngine\n"));
  1146. //
  1147. // IIS enters this routine with an INITIALIZED engine when debugging the script.
  1148. // In that case, it only sets the script site if the state is uninitilized.
  1149. // We'll always be initialized.
  1150. //
  1151. TRYCATCH_HR(m_Engine->GetScriptState(&nScriptState), hr, "IActiveScript::GetScriptState()");
  1152. if (FAILED(hr)) {
  1153. AzPrint((AZD_CRITICAL, "Failed to GetScriptState: 0x%lx\n", hr));
  1154. goto Cleanup;
  1155. }
  1156. //
  1157. // If the script engine has thread state,
  1158. // ditch it.
  1159. //
  1160. if ( nScriptState == SCRIPTSTATE_INITIALIZED ) {
  1161. SCRIPTTHREADID ThreadId = 0;
  1162. //
  1163. // Get the thread id of the current thread
  1164. //
  1165. TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &ThreadId), hr, "IActiveScript::GetCurrentScriptThreadID()");
  1166. if (FAILED(hr)) {
  1167. AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
  1168. goto Cleanup;
  1169. }
  1170. //
  1171. // If this thread isn't the thread that put the engine into initialized state,
  1172. // then go to uninitialized state do clear the engines thread local storage.
  1173. //
  1174. if ( m_BaseThread != ThreadId ) {
  1175. TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_UNINITIALIZED), hr, "IActiveScript::SetScriptState()");
  1176. if (FAILED(hr)) {
  1177. AzPrint((AZD_CRITICAL, "Failed to SetScriptState(UNINITIALIZED): 0x%lx\n", hr));
  1178. goto Cleanup;
  1179. }
  1180. nScriptState = SCRIPTSTATE_UNINITIALIZED;
  1181. m_BaseThread = ThreadId;
  1182. AzPrint((AZD_SCRIPT, "Changed ThreadId to: 0x%lx 0x%lx\n", this, m_BaseThread));
  1183. }
  1184. }
  1185. //
  1186. // The engine needs to know the host it runs on.
  1187. //
  1188. if ( nScriptState == SCRIPTSTATE_UNINITIALIZED ) {
  1189. TRYCATCH_HR(m_Engine->SetScriptSite((IActiveScriptSite *) this), hr, "IActiveScript::SetScriptSite()");
  1190. if (FAILED(hr)) {
  1191. AzPrint((AZD_CRITICAL, "Error calling SetScriptSite: 0x%lx\n", hr));
  1192. goto Cleanup;
  1193. }
  1194. }
  1195. //
  1196. // Query for the IActiveScriptParse interface of the engine
  1197. //
  1198. TRYCATCH_HR(m_Engine->QueryInterface(IID_IActiveScriptParse, (void **)&m_Parser), hr, "IActiveScript::QueryInterface()");
  1199. if (FAILED(hr)) {
  1200. AzPrint((AZD_CRITICAL, "Engine doesn't support IActiveScriptParse: 0x%lx\n", hr));
  1201. goto Cleanup;
  1202. } else if ( m_Parser == NULL ) {
  1203. hr = E_FAIL;
  1204. goto Cleanup;
  1205. }
  1206. hr = S_OK;
  1207. AssertValid();
  1208. Cleanup:
  1209. return (hr);
  1210. }
  1211. BOOL
  1212. CScriptEngine::IsBaseThread(
  1213. VOID
  1214. )
  1215. /*++
  1216. Routine Description:
  1217. Returns TRUE if the calling thread is the base thread that initialized the engine
  1218. Arguments:
  1219. None
  1220. Return Value:
  1221. TRUE - The calling thread is the base thread
  1222. --*/
  1223. {
  1224. HRESULT hr;
  1225. SCRIPTTHREADID ThreadId = 0;
  1226. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::IsBaseThread\n"));
  1227. ASSERT( m_Engine != NULL );
  1228. //
  1229. // Get the thread id of the current thread
  1230. //
  1231. TRYCATCH_HR(m_Engine->GetCurrentScriptThreadID( &ThreadId), hr, "IActiveScript::GetCurrentScriptThreadID()");
  1232. if (FAILED(hr)) {
  1233. AzPrint((AZD_CRITICAL, "Error calling GetCurrentScriptThreadID: 0x%lx\n", hr));
  1234. return FALSE;
  1235. }
  1236. //
  1237. // Check to see if it is the base thread
  1238. //
  1239. return m_BaseThread == ThreadId;
  1240. }
  1241. VOID
  1242. CScriptEngine::FinalRelease(
  1243. VOID
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. Call this when we are done with the object - Like release but
  1248. it removes all of the interfaces we got, so that the ref.
  1249. count will be zero when last external user is done with the engine.
  1250. Arguments:
  1251. None
  1252. Return Value:
  1253. None
  1254. --*/
  1255. {
  1256. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::FinalRelease\n"));
  1257. //
  1258. // Free all of the interfaces used by the script engine
  1259. //
  1260. if (m_BizRuleContext != NULL) {
  1261. m_BizRuleContext->Release();
  1262. m_BizRuleContext = NULL;
  1263. }
  1264. if (m_Parser) {
  1265. TRYCATCH(m_Parser->Release(), "IActiveScriptParse::Release()");
  1266. m_Parser = NULL;
  1267. }
  1268. if (m_Engine) {
  1269. HRESULT hr;
  1270. // Engine needs to be in uninitialized state before closing.
  1271. // This is only needed if a different thread than this one put it in the
  1272. // initialized state.
  1273. //
  1274. // This is a hack. If I can set it from initialized to uninitialized in a different
  1275. // thread, then why can't close do it in a different thread.
  1276. //
  1277. TRYCATCH_HR(m_Engine->SetScriptState( SCRIPTSTATE_UNINITIALIZED), hr, "IActiveScript::SetScriptState()");
  1278. if (FAILED(hr)) {
  1279. AzPrint((AZD_CRITICAL, "Failed to SetScriptState(UNINITIALIZED): 0x%lx\n", hr));
  1280. ASSERT(SUCCEEDED(hr));
  1281. }
  1282. //
  1283. // Close the engine before releasing it.
  1284. //
  1285. TRYCATCH_HR(m_Engine->Close(), hr, "IActiveScript::Close()");
  1286. if (FAILED(hr)) {
  1287. AzPrint((AZD_CRITICAL, "Cannot CloseEngine: 0x%lx\n", hr ));
  1288. ASSERT(SUCCEEDED(hr));
  1289. }
  1290. // Then we can release it
  1291. TRYCATCH(m_Engine->Release(), "IActiveScript::Release()");
  1292. m_Engine = NULL;
  1293. }
  1294. //
  1295. // We must be the last reference
  1296. //
  1297. #if DBG
  1298. ULONG cRefs = Release();
  1299. ASSERT(cRefs == 0);
  1300. #else
  1301. Release();
  1302. #endif //DBG
  1303. return;
  1304. }
  1305. VOID
  1306. CScriptEngine::InsertHeadList(
  1307. IN PLIST_ENTRY ListHead
  1308. )
  1309. /*++
  1310. Routine Description:
  1311. This routine inserts this ScriptEngine object at the head of the list specified.
  1312. The caller must ensure this object is in at most only one list.
  1313. The caller must provide any serialization.
  1314. Arguments:
  1315. ListHead - List to insert the object into.
  1316. Return Value:
  1317. None.
  1318. --*/
  1319. {
  1320. //tracing purposes only
  1321. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InsertHeadList\n"));
  1322. ::InsertHeadList( ListHead, &m_Next.Next );
  1323. // m_Next.This = this;
  1324. }
  1325. VOID
  1326. CScriptEngine::RemoveListEntry(
  1327. VOID
  1328. )
  1329. /*++
  1330. Routine Description:
  1331. This routine removes this ScriptEngine object from whatever list it is in.
  1332. The caller must ensure this object is in a list.
  1333. The caller must provide any serialization.
  1334. Arguments:
  1335. None
  1336. Return Value:
  1337. None.
  1338. --*/
  1339. {
  1340. //tracing purposes only
  1341. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RemoveListEntry\n"));
  1342. ::RemoveEntryList( &m_Next.Next );
  1343. ::InitializeListHead( &m_Next.Next );
  1344. // m_Next.This = this;
  1345. }
  1346. VOID
  1347. CScriptEngine::InsertHeadLruList(
  1348. VOID
  1349. )
  1350. /*++
  1351. Routine Description:
  1352. This routine inserts this ScriptEngine object at the head of the LRU list.
  1353. The caller must provide any serialization.
  1354. Arguments:
  1355. None.
  1356. Return Value:
  1357. None.
  1358. --*/
  1359. {
  1360. PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
  1361. //tracing purposes only
  1362. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::InsertHeadLruList\n"));
  1363. ::InsertHeadList( &AzAuthorizationStore->LruFreeScriptHead, &m_LruNext.Next );
  1364. AzAuthorizationStore->LruFreeScriptCount ++;
  1365. // m_Next.This = this;
  1366. }
  1367. VOID
  1368. CScriptEngine::RemoveLruListEntry(
  1369. VOID
  1370. )
  1371. /*++
  1372. Routine Description:
  1373. This routine removes this ScriptEngine object from whatever LRU list it is in.
  1374. The caller must ensure this object is in a list.
  1375. The caller must provide any serialization.
  1376. Arguments:
  1377. None
  1378. Return Value:
  1379. None.
  1380. --*/
  1381. {
  1382. PAZP_AZSTORE AzAuthorizationStore = m_Task->GenericObject.AzStoreObject;
  1383. //tracing purposes only
  1384. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::RemoveLruListEntry\n"));
  1385. ::RemoveEntryList( &m_LruNext.Next );
  1386. ::InitializeListHead( &m_LruNext.Next );
  1387. AzAuthorizationStore->LruFreeScriptCount --;
  1388. // m_Next.This = this;
  1389. }
  1390. /******************************************************************************
  1391. * IUnknown Interfaces -- All COM objects must implement, either directly or
  1392. * indirectly, the IUnknown interface.
  1393. ******************************************************************************/
  1394. STDMETHODIMP CScriptEngine::QueryInterface(REFIID riid, void **ppvObj)
  1395. /*++
  1396. Routine Description:
  1397. Standard COM QueryInterface routine.
  1398. Determines if this component supports the requested interface.
  1399. Arguments:
  1400. riid - Id of the interface being queried
  1401. ppvObj - On success, returns a pointer to that interface.
  1402. Return Value:
  1403. S_OK: ppvObj is returned
  1404. E_NOINTERFACE: riid is not supported
  1405. --*/
  1406. {
  1407. //tracing purposes only
  1408. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::QueryInterface->"));
  1409. //
  1410. // Only two interfaces are supported
  1411. //
  1412. if (riid == IID_IUnknown) {
  1413. AzPrint((AZD_SCRIPT_MORE, "IUnknown\n"));
  1414. *ppvObj = static_cast < IActiveScriptSite * >(this);
  1415. } else if (riid == IID_IActiveScriptSite) {
  1416. AzPrint((AZD_SCRIPT_MORE, "IActiveScriptSite\n"));
  1417. *ppvObj = static_cast < IActiveScriptSite * >(this);
  1418. } else {
  1419. AzPrint((AZD_SCRIPT_MORE, "Unsupported Interface: "));
  1420. AzpDumpGuid(AZD_SCRIPT_MORE, (GUID *) & riid);
  1421. AzPrint((AZD_SCRIPT_MORE, "\n"));
  1422. *ppvObj = NULL;
  1423. return E_NOINTERFACE;
  1424. }
  1425. static_cast < IUnknown * >(*ppvObj)->AddRef();
  1426. return S_OK;
  1427. }
  1428. /******************************************************************************
  1429. * AddRef() -- In order to allow an object to delete itself when it is no
  1430. * longer needed, it is necessary to maintain a count of all references to
  1431. * this object. When a new reference is created, this function increments
  1432. * the count.
  1433. ******************************************************************************/
  1434. STDMETHODIMP_(ULONG) CScriptEngine::AddRef()
  1435. {
  1436. ULONG cRef;
  1437. cRef = InterlockedIncrement(&m_cRef);
  1438. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::AddRef %ld\n", cRef ));
  1439. return(cRef);
  1440. }
  1441. /******************************************************************************
  1442. * Release() -- When a reference to this object is removed, this function
  1443. * decrements the reference count. If the reference count is 0, then this
  1444. * function deletes this object and returns 0;
  1445. ******************************************************************************/
  1446. STDMETHODIMP_(ULONG) CScriptEngine::Release()
  1447. {
  1448. ULONG cRef = InterlockedDecrement(&m_cRef);
  1449. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::Release %ld\n", cRef));
  1450. if (0 == cRef) {
  1451. delete this;
  1452. }
  1453. return(cRef);
  1454. }
  1455. /******************************************************************************
  1456. * IActiveScriptSite Interfaces -- These interfaces define the exposed methods
  1457. * of ActiveX Script Hosts.
  1458. ******************************************************************************/
  1459. /******************************************************************************
  1460. * GetLCID() -- Gets the identifier of the host's user interface. This method
  1461. * returns S_OK if the identifier was placed in plcid, E_NOTIMPL if this
  1462. * function is not implemented, in which case the system-defined identifier
  1463. * should be used, and E_POINTER if the specified pointer was invalid.
  1464. ******************************************************************************/
  1465. STDMETHODIMP CScriptEngine::GetLCID(LCID * plcid)
  1466. {
  1467. //tracing purposes only
  1468. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetLCID\n"));
  1469. UNREFERENCED_PARAMETER(plcid);
  1470. return E_NOTIMPL;
  1471. }
  1472. /******************************************************************************
  1473. * GetItemInfo() -- Retrieves information about an item that was added to the
  1474. * script engine through a call to AddNamedItem.
  1475. * Parameters: pstrName -- the name of the item, specified in AddNamedItem.
  1476. * dwReturnMask -- Mask indicating what kind of pointer to return
  1477. * SCRIPTINFO_IUNKNOWN or SCRIPTINFO_ITYPEINFO
  1478. * ppunkItem -- return spot for an IUnknown pointer
  1479. * ppTypeInfo -- return spot for an ITypeInfo pointer
  1480. * Returns: S_OK if the call was successful
  1481. * E_INVALIDARG if one of the arguments was invalid
  1482. * E_POINTER if one of the pointers was invalid
  1483. * TYPE_E_ELEMENTNOTFOUND if there wasn't an item of the
  1484. * specified type.
  1485. ******************************************************************************/
  1486. STDMETHODIMP CScriptEngine::GetItemInfo(
  1487. LPCOLESTR pstrName,
  1488. DWORD dwReturnMask,
  1489. IUnknown ** ppunkItem,
  1490. ITypeInfo ** ppTypeInfo
  1491. )
  1492. {
  1493. HRESULT hr;
  1494. VARIANT varName;
  1495. VARIANT varInterface;
  1496. IDispatch *Interface = NULL;
  1497. BOOLEAN InterfaceNamesLocked = FALSE;
  1498. //
  1499. // Initialization
  1500. //
  1501. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetItemInfo: %ws\n", pstrName));
  1502. VariantInit( &varName );
  1503. VariantInit( &varInterface );
  1504. //Use logical ANDs to determine which type(s) of pointer the caller wants,
  1505. //and make sure that that placeholder is currently valid.
  1506. if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
  1507. if (!ppunkItem) {
  1508. hr = E_INVALIDARG;
  1509. goto Cleanup;
  1510. }
  1511. *ppunkItem = NULL;
  1512. }
  1513. if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
  1514. if (!ppTypeInfo) {
  1515. hr = E_INVALIDARG;
  1516. goto Cleanup;
  1517. }
  1518. *ppTypeInfo = NULL;
  1519. }
  1520. //
  1521. // We didn't capture the array
  1522. // So access it under a try/except
  1523. __try {
  1524. //
  1525. // Check for the BizRuleContext interface itself
  1526. //
  1527. if (!_wcsicmp( BIZRULE_CONTEXT_INTERFACE_NAME, pstrName)) {
  1528. //
  1529. // Use the access check interface
  1530. //
  1531. Interface = m_BizRuleContext;
  1532. //
  1533. // If the caller passed interfaces to access check,
  1534. // look there.
  1535. //
  1536. } else if ( m_AcContext != NULL && m_AcContext->Interfaces != NULL ) {
  1537. VARIANT HUGEP *varNames;
  1538. ULONG Index;
  1539. LONG InterfacesIndex;
  1540. //
  1541. // Convert name to an easier form to compare
  1542. //
  1543. V_VT(&varName) = VT_BSTR;
  1544. V_BSTR(&varName) = (BSTR) pstrName;
  1545. //
  1546. // Access the variant array directly
  1547. //
  1548. ASSERT( m_AcContext->InterfaceNames != NULL && m_AcContext->InterfaceFlags != NULL );
  1549. hr = SafeArrayAccessData( m_AcContext->InterfaceNames, (void HUGEP **)&varNames);
  1550. _JumpIfError(hr, Cleanup, "SafeArrayAccessData");
  1551. InterfaceNamesLocked = TRUE;
  1552. //
  1553. // Find an interface name that matches the passed in name
  1554. //
  1555. for ( Index=0; Index<m_AcContext->InterfaceNames->rgsabound[0].cElements; Index++ ) {
  1556. //
  1557. // Stop at the end of the array
  1558. //
  1559. if ( V_VT(&varNames[Index]) == VT_EMPTY ) {
  1560. break;
  1561. }
  1562. if ( VarCmp( &varName, &varNames[Index], LOCALE_USER_DEFAULT, NORM_IGNORECASE ) == (HRESULT)VARCMP_EQ ) {
  1563. //
  1564. // Copy out the array element.
  1565. //
  1566. InterfacesIndex = m_AcContext->InterfaceLower + Index;
  1567. hr = SafeArrayGetElement( m_AcContext->Interfaces, &InterfacesIndex, &varInterface );
  1568. _JumpIfError(hr, Cleanup, "SafeArrayGetElement");
  1569. //
  1570. // Ensure it is an IDispatch interface
  1571. //
  1572. if ( V_VT( &varInterface ) != VT_DISPATCH ) {
  1573. hr = E_INVALIDARG;
  1574. goto Cleanup;
  1575. }
  1576. //
  1577. // Use the passed in interface
  1578. //
  1579. Interface = V_DISPATCH( &varInterface );
  1580. break;
  1581. }
  1582. }
  1583. }
  1584. //
  1585. // If no interface was found,
  1586. // fail
  1587. //
  1588. if ( Interface == NULL ) {
  1589. hr = TYPE_E_ELEMENTNOTFOUND;
  1590. goto Cleanup;
  1591. }
  1592. //
  1593. // If an interface was found,
  1594. // return the requested information about the interface.
  1595. //
  1596. hr = S_OK;
  1597. if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
  1598. Interface->QueryInterface(IID_IUnknown, (void **)ppunkItem);
  1599. }
  1600. if (dwReturnMask & SCRIPTINFO_ITYPEINFO) {
  1601. hr = Interface->GetTypeInfo( 0, 0, ppTypeInfo );
  1602. }
  1603. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  1604. hr = GetExceptionCode();
  1605. AzPrint((AZD_CRITICAL, "GetItemInfo took an exception: 0x%lx\n", hr));
  1606. goto Cleanup;
  1607. }
  1608. Cleanup:
  1609. //
  1610. // Cleanup from finding interfaces
  1611. //
  1612. if ( m_AcContext != NULL && m_AcContext->Interfaces != NULL ) {
  1613. //
  1614. // We didn't capture the array
  1615. // So access it under a try/except
  1616. __try {
  1617. if ( InterfaceNamesLocked ) {
  1618. SafeArrayUnaccessData( m_AcContext->InterfaceNames );
  1619. }
  1620. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  1621. AzPrint((AZD_CRITICAL, "GetItemInfo took an exception: 0x%lx\n", hr));
  1622. }
  1623. VariantClear(&varName);
  1624. VariantClear(&varInterface);
  1625. }
  1626. return hr;
  1627. }
  1628. /******************************************************************************
  1629. * GetDocVersionString() -- It is possible, even likely that a script document
  1630. * can be changed between runs. The host can define a unique version number
  1631. * for the script, which can be saved along with the script. If the version
  1632. * changes, the engine will know to recompile the script on the next run.
  1633. ******************************************************************************/
  1634. STDMETHODIMP CScriptEngine::GetDocVersionString(BSTR * pbstrVersionString)
  1635. {
  1636. //tracing purposes only
  1637. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::GetDocVersionString\n"));
  1638. UNREFERENCED_PARAMETER(pbstrVersionString);
  1639. //For the generic case, this function isn't implemented.
  1640. return E_NOTIMPL;
  1641. }
  1642. /******************************************************************************
  1643. * OnScriptTerminate() -- This method may give the host a chance to react when
  1644. * the script terminates. pvarResult give the result of the script or NULL
  1645. * if the script doesn't give a result, and pexcepinfo gives the location of
  1646. * any exceptions raised by the script. Returns S_OK if the calls succeeds.
  1647. ******************************************************************************/
  1648. STDMETHODIMP CScriptEngine::OnScriptTerminate(const VARIANT * pvarResult,
  1649. const EXCEPINFO * pexcepinfo)
  1650. {
  1651. //tracing purposes only
  1652. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnScriptTerminate\n"));
  1653. UNREFERENCED_PARAMETER(pvarResult);
  1654. UNREFERENCED_PARAMETER(pexcepinfo);
  1655. //If something needs to happen when the script terminates, put it here.
  1656. return S_OK;
  1657. }
  1658. /******************************************************************************
  1659. * OnStateChange() -- This function gives the host a chance to react when the
  1660. * state of the script engine changes. ssScriptState lets the host know the
  1661. * new state of the machine. Returns S_OK if successful.
  1662. ******************************************************************************/
  1663. STDMETHODIMP CScriptEngine::OnStateChange(SCRIPTSTATE ssScriptState)
  1664. {
  1665. //tracing purposes only
  1666. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnStateChange:"));
  1667. //If something needs to happen when the script enters a certain state,
  1668. //put it here.
  1669. switch (ssScriptState) {
  1670. case SCRIPTSTATE_UNINITIALIZED:
  1671. AzPrint((AZD_SCRIPT_MORE, "State: Uninitialized.\n"));
  1672. break;
  1673. case SCRIPTSTATE_INITIALIZED:
  1674. AzPrint((AZD_SCRIPT_MORE, "State: Initialized.\n"));
  1675. break;
  1676. case SCRIPTSTATE_STARTED:
  1677. AzPrint((AZD_SCRIPT_MORE, "State: Started.\n"));
  1678. break;
  1679. case SCRIPTSTATE_CONNECTED:
  1680. AzPrint((AZD_SCRIPT_MORE, "State: Connected.\n"));
  1681. break;
  1682. case SCRIPTSTATE_DISCONNECTED:
  1683. AzPrint((AZD_SCRIPT_MORE, "State: Disconnected.\n"));
  1684. break;
  1685. case SCRIPTSTATE_CLOSED:
  1686. AzPrint((AZD_SCRIPT_MORE, "State: Closed.\n"));
  1687. break;
  1688. default:
  1689. break;
  1690. }
  1691. return S_OK;
  1692. }
  1693. /******************************************************************************
  1694. * OnScriptError() -- This function gives the host a chance to respond when
  1695. * an error occurs while running a script. pase holds a reference to the
  1696. * IActiveScriptError object, which the host can use to get information about
  1697. * the error. Returns S_OK if the error was handled successfully, and an OLE
  1698. * error code if not.
  1699. ******************************************************************************/
  1700. STDMETHODIMP CScriptEngine::OnScriptError(
  1701. IActiveScriptError * pscripterror)
  1702. {
  1703. //tracing purposes only
  1704. AzPrint((AZD_SCRIPT, "CScriptEngine::OnScriptError\n"));
  1705. ASSERT(pscripterror);
  1706. AssertValid();
  1707. if ( m_fTimedOut ) {
  1708. m_ScriptError = AZ_HRESULT(ERROR_TIMEOUT);
  1709. }
  1710. //
  1711. // Only report the first error in this script
  1712. //
  1713. if ( m_ScriptError == S_OK ) {
  1714. m_ScriptError = E_UNEXPECTED;
  1715. if (pscripterror) {
  1716. EXCEPINFO theException;
  1717. HRESULT hr;
  1718. //
  1719. // Get a description of the exception
  1720. //
  1721. hr = pscripterror->GetExceptionInfo(&theException);
  1722. if ( FAILED(hr)) {
  1723. m_ScriptError = hr;
  1724. //
  1725. // Log the exception
  1726. //
  1727. } else {
  1728. m_ScriptError = theException.wCode == 0 ?
  1729. theException.scode :
  1730. theException.wCode;
  1731. AzPrint(( AZD_CRITICAL,
  1732. "Script Error: Code: 0x%lx %ld\n"
  1733. " Src: %ws\n"
  1734. " File: %ws\n"
  1735. " Desc: %ws\n",
  1736. m_ScriptError,
  1737. m_ScriptError,
  1738. theException.bstrSource,
  1739. theException.bstrHelpFile,
  1740. theException.bstrDescription));
  1741. }
  1742. }
  1743. }
  1744. // return S_OK to tell the script engine that we handled the error ok.
  1745. // Returning E_FAIL would not stop the scripting engine, this was a doc error.
  1746. return S_OK;
  1747. }
  1748. /******************************************************************************
  1749. * OnEnterScript() -- This function gives the host a chance to respond when
  1750. * the script begins running. Returns S_OK if the call was successful.
  1751. ******************************************************************************/
  1752. STDMETHODIMP CScriptEngine::OnEnterScript(void)
  1753. {
  1754. //tracing purposes only
  1755. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnEnterScript\n"));
  1756. return S_OK;
  1757. }
  1758. /******************************************************************************
  1759. * OnExitScript() -- This function gives the host a chance to respond when
  1760. * the script finishes running. Returns S_OK if the call was successful.
  1761. ******************************************************************************/
  1762. STDMETHODIMP CScriptEngine::OnLeaveScript(void)
  1763. {
  1764. //tracing purposes only
  1765. AzPrint((AZD_SCRIPT_MORE, "CScriptEngine::OnLeaveScript\n"));
  1766. return S_OK;
  1767. }