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.

1906 lines
42 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name :
  4. wam_process.cxx
  5. Abstract:
  6. Manages OOP ISAPI processes
  7. Author:
  8. Wade Hilmo (wadeh) 10-Oct-2000
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. ULW3.DLL
  13. --*/
  14. #include <initguid.h>
  15. #include "precomp.hxx"
  16. #include "isapi_handler.h"
  17. #include "iwam_i.c"
  18. #include "wam_process.hxx"
  19. WAM_PROCESS::WAM_PROCESS(
  20. LPCWSTR szWamClsid,
  21. WAM_PROCESS_MANAGER * pWamProcessManager,
  22. LPCSTR szIsapiHandlerInstance
  23. )
  24. : _cCurrentRequests( 0 ),
  25. _cTotalRequests( 0 ),
  26. _cRefs( 1 ),
  27. _pWamProcessManager( pWamProcessManager ),
  28. _pIWam( NULL ),
  29. _bstrInstanceId( NULL ),
  30. _cMaxRequests( 0 ),
  31. _dwProcessId( 0 ),
  32. _hProcess( NULL ),
  33. _fGoingAway( FALSE ),
  34. _fCrashed( FALSE )
  35. /*++
  36. Routine Description:
  37. Constructor
  38. Arguments:
  39. szWamClsid - The CLSID of the WAM application to create
  40. pWamProcessManager - A pointer to the WAM process manager
  41. szIsapiHandlerInstance - The instance ID of the ISAPI handler
  42. that's creating this object. Used for
  43. debugging purposes
  44. Return Value:
  45. None
  46. --*/
  47. {
  48. _pWamProcessManager->AddRef();
  49. InitializeListHead( &_RequestListHead );
  50. wcsncpy(
  51. _szWamClsid,
  52. szWamClsid,
  53. SIZE_CLSID_STRING
  54. );
  55. _szWamClsid[SIZE_CLSID_STRING - 1] = L'\0';
  56. strncpy(
  57. _szIsapiHandlerInstance,
  58. szIsapiHandlerInstance,
  59. SIZE_CLSID_STRING
  60. );
  61. _szIsapiHandlerInstance[SIZE_CLSID_STRING - 1] = '\0';
  62. INITIALIZE_CRITICAL_SECTION( &_csRequestList );
  63. };
  64. HRESULT
  65. WAM_PROCESS::Create(
  66. LPCWSTR szApplMdPathW
  67. )
  68. /*++
  69. Routine Description:
  70. Initializes the parts of the WAM_PROCESS object not
  71. appropriate to the constructor. This includes starting
  72. up the remote process via CoCreateInstance and collecting
  73. data about the process once it's running
  74. Arguments:
  75. szApplMdPathW - The metabase path of the application
  76. Return Value:
  77. HRESULT
  78. --*/
  79. {
  80. CLSID clsid;
  81. HRESULT hr = NOERROR;
  82. LPWSTR szIsapiModule;
  83. DWORD cbIsapiModule;
  84. STACK_STRA( strClsid, SIZE_CLSID_STRING );
  85. IF_DEBUG( ISAPI )
  86. {
  87. DBGPRINTF((
  88. DBG_CONTEXT,
  89. "Creating WAM_PROCESS %p, CLSID=%S.\r\n",
  90. this,
  91. _szWamClsid
  92. ));
  93. }
  94. //
  95. // Get some info about the ISAPI module
  96. //
  97. DBG_REQUIRE( ( szIsapiModule = _pWamProcessManager->QueryIsapiModule() ) != NULL );
  98. cbIsapiModule = (DWORD)( wcslen( szIsapiModule ) + 1 ) * sizeof( WCHAR );
  99. //
  100. // Set the name for the WAM_PROCESS
  101. //
  102. if ( szApplMdPathW )
  103. {
  104. hr = _strApplMdPathW.Copy( szApplMdPathW );
  105. }
  106. else
  107. {
  108. hr = _strApplMdPathW.Copy( L"" );
  109. }
  110. if ( FAILED( hr ) )
  111. {
  112. goto ErrorExit;
  113. }
  114. //
  115. // Get the CLSID for this WAM_PROCESS
  116. //
  117. hr = CLSIDFromString(
  118. (LPOLESTR)_szWamClsid,
  119. &clsid
  120. );
  121. if ( FAILED( hr ) )
  122. {
  123. goto ErrorExit;
  124. }
  125. hr = strClsid.CopyW( _szWamClsid );
  126. if ( FAILED( hr ) )
  127. {
  128. goto ErrorExit;
  129. }
  130. //
  131. // CoCreate it
  132. //
  133. hr = CoCreateInstance(
  134. clsid,
  135. NULL,
  136. CLSCTX_LOCAL_SERVER,
  137. IID_IWam,
  138. (void**)&_pIWam
  139. );
  140. if ( FAILED( hr ) )
  141. {
  142. DBGPRINTF((
  143. DBG_CONTEXT,
  144. "WAM_PROCESS %p. Failed to CoCreate WAM.\r\n",
  145. this
  146. ));
  147. _pIWam = NULL;
  148. goto ErrorExit;
  149. }
  150. //
  151. // Set the proxy blanket so that the client can't
  152. // impersonate us.
  153. //
  154. hr = CoSetProxyBlanket(
  155. _pIWam,
  156. RPC_C_AUTHN_DEFAULT,
  157. RPC_C_AUTHZ_DEFAULT,
  158. COLE_DEFAULT_PRINCIPAL,
  159. RPC_C_AUTHN_LEVEL_DEFAULT,
  160. RPC_C_IMP_LEVEL_IDENTIFY,
  161. COLE_DEFAULT_AUTHINFO,
  162. EOAC_DEFAULT
  163. );
  164. if ( FAILED( hr ) )
  165. {
  166. DBGPRINTF((
  167. DBG_CONTEXT,
  168. "WAM_PROCESS %p. Failed to CoCreate WAM.\r\n",
  169. this
  170. ));
  171. goto ErrorExit;
  172. }
  173. //
  174. // Now initialize it.
  175. //
  176. _dwProcessId = 0;
  177. _hProcess = NULL;
  178. hr = _pIWam->WamInitProcess(
  179. (BYTE*)szIsapiModule,
  180. cbIsapiModule,
  181. &_dwProcessId,
  182. strClsid.QueryStr(),
  183. _szIsapiHandlerInstance,
  184. GetCurrentProcessId()
  185. );
  186. if ( FAILED( hr ) )
  187. {
  188. DBGPRINTF((
  189. DBG_CONTEXT,
  190. "WAM_PROCESS %p. WamInitProcess failed.\r\n",
  191. this
  192. ));
  193. //
  194. // If we're failing with ERROR_ALREADY_INITIALIZED,
  195. // then we should try and get the process handle so
  196. // that the error routine below will kill off the
  197. // process. This will allow subsequent attempts
  198. // to instantiate the WAM_PROCESS to succeed.
  199. //
  200. if ( hr == HRESULT_FROM_WIN32( ERROR_ALREADY_INITIALIZED ) &&
  201. _dwProcessId != 0 )
  202. {
  203. _hProcess = OpenProcess(
  204. PROCESS_DUP_HANDLE | PROCESS_TERMINATE,
  205. FALSE,
  206. _dwProcessId
  207. );
  208. }
  209. goto ErrorExit;
  210. }
  211. //
  212. // Get a handle to the new process
  213. //
  214. _hProcess = OpenProcess(
  215. PROCESS_DUP_HANDLE | PROCESS_TERMINATE,
  216. FALSE,
  217. _dwProcessId
  218. );
  219. if ( !_hProcess )
  220. {
  221. hr = HRESULT_FROM_WIN32( GetLastError() );
  222. DBGPRINTF((
  223. DBG_CONTEXT,
  224. "WAM_PROCESS %p. Failed to get process handle.\r\n",
  225. this
  226. ));
  227. goto ErrorExit;
  228. }
  229. hr = _pWamProcessManager->QueryCatalog()->GetApplicationInstanceIDFromProcessID(
  230. _dwProcessId,
  231. &_bstrInstanceId
  232. );
  233. if ( FAILED( hr ) )
  234. {
  235. //
  236. // If we've made it this far, then it's not reasonable for the
  237. // above GetApplicationInstanceIDFromProcessID call to fail.
  238. //
  239. DBG_ASSERT( FALSE && "GetApplicationInstanceIDFromProcessID failed on running app." );
  240. DBGPRINTF((
  241. DBG_CONTEXT,
  242. "WAM_PROCESS %p. Failed to get instance ID.\r\n",
  243. this
  244. ));
  245. goto ErrorExit;
  246. }
  247. IF_DEBUG( ISAPI )
  248. {
  249. DBGPRINTF((
  250. DBG_CONTEXT,
  251. "WAM_PROCESS %p created successfully.\r\n",
  252. this
  253. ));
  254. }
  255. return hr;
  256. ErrorExit:
  257. DBG_ASSERT( FAILED( hr ) );
  258. DBGPRINTF((
  259. DBG_CONTEXT,
  260. "Attempt to create WAM_PROCESS %p failed. CLSID=%S, HRESULT=%08x.\r\n",
  261. this,
  262. _szWamClsid,
  263. hr
  264. ));
  265. //
  266. // Log the failed attempt. We want to put a rich error
  267. // message into the event log to aid administrators in
  268. // debugging, so we'll FormatMessage the error.
  269. //
  270. const WCHAR * pszEventLog[2];
  271. WCHAR szErrorDescription[MAX_MESSAGE_TEXT];
  272. WCHAR * pszErrorDescription = NULL;
  273. HANDLE hMetabase = GetModuleHandleW( L"METADATA.DLL" );
  274. DWORD dwFacility;
  275. DWORD dwError;
  276. //
  277. // Check the facility code for the failed HRESULT. If it's really
  278. // a win32 error, then we should call FormatMessage on the win32
  279. // error directly. This makes prettier messages in the event log.
  280. //
  281. dwFacility = ( hr >> 16 ) & 0x0fff;
  282. if ( dwFacility == FACILITY_WIN32 )
  283. {
  284. dwError = WIN32_FROM_HRESULT( hr );
  285. }
  286. else
  287. {
  288. dwError = hr;
  289. }
  290. if( FormatMessageW(
  291. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  292. FORMAT_MESSAGE_FROM_HMODULE |
  293. FORMAT_MESSAGE_FROM_SYSTEM |
  294. FORMAT_MESSAGE_IGNORE_INSERTS,
  295. hMetabase,
  296. dwError,
  297. MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
  298. (LPTSTR)&pszErrorDescription,
  299. sizeof( szErrorDescription ) - 1,
  300. NULL
  301. ) && pszErrorDescription )
  302. {
  303. wcsncpy(szErrorDescription, pszErrorDescription, MAX_MESSAGE_TEXT );
  304. szErrorDescription[MAX_MESSAGE_TEXT-1] = L'\0';
  305. LocalFree(pszErrorDescription);
  306. }
  307. else
  308. {
  309. wsprintfW( szErrorDescription, L"%08x", hr );
  310. }
  311. pszEventLog[0] = _strApplMdPathW.QueryStr();
  312. pszEventLog[1] = szErrorDescription;
  313. g_pW3Server->LogEvent(
  314. W3_EVENT_FAIL_LOADWAM,
  315. 2,
  316. pszEventLog,
  317. 0
  318. );
  319. //
  320. // Clean up instance ID and IWam goo
  321. //
  322. if ( _bstrInstanceId )
  323. {
  324. SysFreeString( _bstrInstanceId );
  325. _bstrInstanceId = NULL;
  326. }
  327. if ( _pIWam )
  328. {
  329. _pIWam->Release();
  330. _pIWam = NULL;
  331. }
  332. //
  333. // Since COM can sometimes return bogus failures when
  334. // we try and get the instance ID, we need to check to
  335. // see if the process was really created and terminate
  336. // it if so.
  337. //
  338. if ( _hProcess )
  339. {
  340. TerminateProcess( _hProcess, 0 );
  341. CloseHandle( _hProcess );
  342. _hProcess = NULL;
  343. _dwProcessId = 0;
  344. }
  345. return hr;
  346. }
  347. HRESULT
  348. WAM_PROCESS::ProcessRequest(
  349. ISAPI_REQUEST * pIsapiRequest,
  350. ISAPI_CORE_DATA * pIsapiCoreData,
  351. DWORD * pdwHseResult
  352. )
  353. /*++
  354. Routine Description:
  355. Processes a request by passing the necessary data
  356. to the OOP host.
  357. Arguments:
  358. pIsapiRequest - The ISAPI_REQUEST for this request
  359. pIsapiCoreData - The core data for the request
  360. pdwHseResult - Upon return, the return code from HttpExtensionProc
  361. Return Value:
  362. HRESULT
  363. --*/
  364. {
  365. HRESULT hr = NOERROR;
  366. DWORD dwHseResult;
  367. //
  368. // Bump the counters for this process and
  369. // Add a reference for the request to both
  370. // the process object and to the ISAPI request
  371. // object.
  372. //
  373. InterlockedIncrement( &_cTotalRequests );
  374. InterlockedIncrement( &_cCurrentRequests );
  375. /*
  376. //
  377. // We need to keep a list of outstanding requests so that we
  378. // can clean everything up in the case of shutdown or an OOP
  379. // crash.
  380. //
  381. LockRequestList();
  382. InsertHeadList( &_RequestListHead, &pIsapiRequest->_leRequest );
  383. UnlockRequestList();
  384. */
  385. //
  386. // Associate the WAM process with the ISAPI_REQUEST and call
  387. // out to the OOP host. This essentially causes the ISAPI_REQUEST
  388. // to take a reference on this object until it's destroyed.
  389. //
  390. pIsapiRequest->SetAssociatedWamProcess( this );
  391. if ( ETW_IS_TRACE_ON(ETW_LEVEL_CP) )
  392. {
  393. g_pEtwTracer->EtwTraceEvent( &IISEventGuid,
  394. ETW_TYPE_IIS_OOP_ISAPI_REQUEST,
  395. &pIsapiCoreData->RequestId,
  396. sizeof(ULONGLONG),
  397. &_dwProcessId,
  398. sizeof(DWORD),
  399. &_cTotalRequests,
  400. sizeof(DWORD),
  401. &_cCurrentRequests,
  402. sizeof(DWORD),
  403. NULL,
  404. 0 );
  405. }
  406. hr = _pIWam->WamProcessIsapiRequest(
  407. (BYTE*)pIsapiCoreData,
  408. pIsapiCoreData->cbSize,
  409. pIsapiRequest,
  410. &dwHseResult
  411. );
  412. //
  413. // Check for failure.
  414. //
  415. if ( FAILED( hr ) )
  416. {
  417. //
  418. // Need to check for special case failures that result
  419. // from OOP process crashes.
  420. //
  421. // RPC_S_CALL_FAILED - indicates OOP process crashed
  422. // during the call.
  423. // RPC_S_CALL_FAILED_DNE - indicates OOP process crashed
  424. // before the call.
  425. // RPC_S_SERVER_UNAVAILABLE - The OOP process was just not there
  426. // (ie. previously crashed, etc.)
  427. // RPC_E_SERVERFAULT - The ISAPI AVed in HttpExtensionProc()
  428. //
  429. if ( WIN32_FROM_HRESULT( hr ) == RPC_S_CALL_FAILED ||
  430. WIN32_FROM_HRESULT( hr ) == RPC_S_CALL_FAILED_DNE ||
  431. WIN32_FROM_HRESULT( hr ) == RPC_S_SERVER_UNAVAILABLE ||
  432. WIN32_FROM_HRESULT( hr ) == RPC_E_SERVERFAULT )
  433. {
  434. //
  435. // WARNING - HandleCrash can potentially cause this object
  436. // to be destroyed. Don't do anything except
  437. // return after calling it!
  438. //
  439. HandleCrash();
  440. return hr;
  441. }
  442. }
  443. *pdwHseResult = dwHseResult;
  444. return hr;
  445. }
  446. HRESULT
  447. WAM_PROCESS::ProcessCompletion(
  448. ISAPI_REQUEST * pIsapiRequest,
  449. DWORD64 IsapiContext,
  450. DWORD cbCompletion,
  451. DWORD dwCompletionStatus
  452. )
  453. /*++
  454. Routine Description:
  455. Processes a completion by passing the necessary data
  456. to the OOP host.
  457. Arguments:
  458. pIsapiRequest - The ISAPI_REQUEST for this request
  459. IsapiContext - The ISAPI_CONTEXT that identifies the request (opaque)
  460. cbCompletion - The number of bytes associated with the completion
  461. dwCompletionStatus - The result code for the completion
  462. Return Value:
  463. HRESULT
  464. --*/
  465. {
  466. HRESULT hr = NOERROR;
  467. DBG_ASSERT( pIsapiRequest );
  468. DBG_ASSERT( IsapiContext != 0 );
  469. hr = pIsapiRequest->PreprocessIoCompletion( cbCompletion );
  470. pIsapiRequest->ResetIsapiContext();
  471. //
  472. // Let the OOP process handle the completion
  473. //
  474. hr = _pIWam->WamProcessIsapiCompletion(
  475. IsapiContext,
  476. cbCompletion,
  477. dwCompletionStatus
  478. );
  479. //
  480. // This release balances the release taken when the ISAPI_REQUEST
  481. // made the async call into the server core.
  482. //
  483. pIsapiRequest->Release();
  484. //
  485. // Check for failure.
  486. //
  487. if ( FAILED( hr ) )
  488. {
  489. //
  490. // Need to check for special case failures that result
  491. // from OOP process crashes.
  492. //
  493. // RPC_S_CALL_FAILED - indicates OOP process crashed
  494. // during the call.
  495. // RPC_S_CALL_FAILED_DNE - indicates OOP process crashed
  496. // before the call.
  497. // RPC_S_SERVER_UNAVAILABLE - The OOP process was just not there
  498. // (ie. previously crashed, etc.)
  499. // RPC_E_SERVERFAULT - The ISAPI AVed in HttpExtensionProc()
  500. //
  501. if ( WIN32_FROM_HRESULT( hr ) == RPC_S_CALL_FAILED ||
  502. WIN32_FROM_HRESULT( hr ) == RPC_S_CALL_FAILED_DNE ||
  503. WIN32_FROM_HRESULT( hr ) == RPC_S_SERVER_UNAVAILABLE ||
  504. WIN32_FROM_HRESULT( hr ) == RPC_E_SERVERFAULT )
  505. {
  506. //
  507. // WARNING - HandleCrash can potentially cause this object
  508. // to be destroyed. Don't do anything except
  509. // return after calling it!
  510. //
  511. HandleCrash();
  512. return hr;
  513. }
  514. }
  515. return hr;
  516. }
  517. VOID
  518. WAM_PROCESS::DecrementRequestCount(
  519. VOID
  520. )
  521. /*++
  522. Routine Description:
  523. Hmmm. Let me think for a minute...
  524. Oh yeah, this function decrements the request count.
  525. Arguments:
  526. None
  527. Return Value:
  528. None
  529. --*/
  530. {
  531. /*
  532. //
  533. // Remove this request from the active request list
  534. //
  535. LockRequestList();
  536. RemoveEntryList( &pIsapiRequest->_leRequest );
  537. UnlockRequestList();
  538. //
  539. // Release the ISAPI_REQUEST object.
  540. //
  541. pIsapiRequest->Release();
  542. //
  543. // Release this requests reference on the WAM_PROCESS object.
  544. //
  545. Release();
  546. */
  547. InterlockedDecrement( &_cCurrentRequests );
  548. }
  549. VOID
  550. WAM_PROCESS::HandleCrash(
  551. VOID
  552. )
  553. /*++
  554. Routine Description:
  555. Handles a crashed request
  556. Arguments:
  557. None
  558. Return Value:
  559. None
  560. --*/
  561. {
  562. //
  563. // If we get here, then we have to assume that the OOP host
  564. // process has crashed. When this happens, COM is going to
  565. // release the references on any ISAPI_REQUEST objects held
  566. // by the process. As these ISAPI_REQUEST's drop to zero,
  567. // they will be releasing references on this object.
  568. //
  569. // At this time, there's not much we can do, except call
  570. // TerminateProcess on it to make sure it's dead and pull
  571. // it off the hash table so no new requests are routed to
  572. // it.
  573. //
  574. // The Disable call that pulls it from the hash table could
  575. // very well release the final reference on this object.
  576. // We cannot touch any members after that.
  577. //
  578. // We need to make sure that we only call Disable once!
  579. //
  580. _fCrashed = TRUE;
  581. if ( InterlockedExchange( &_fGoingAway, 1 ) == 0 )
  582. {
  583. //
  584. // Log the crash
  585. //
  586. _pWamProcessManager->RegisterCrash( _szWamClsid );
  587. const WCHAR *pszEventLog[1];
  588. pszEventLog[0] = _strApplMdPathW.QueryStr();
  589. g_pW3Server->LogEvent(
  590. W3_EVENT_OOPAPP_VANISHED,
  591. 1,
  592. pszEventLog,
  593. 0
  594. );
  595. TerminateProcess( _hProcess, 0 );
  596. //
  597. // Don't leak the handle...
  598. //
  599. CloseHandle( _hProcess );
  600. _hProcess = NULL;
  601. //
  602. // Force COM to release any references it's holding
  603. // to ISAPI_REQUEST objects.
  604. //
  605. DisconnectIsapiRequests();
  606. Disable( TRUE );
  607. }
  608. }
  609. HRESULT
  610. WAM_PROCESS::Disable(
  611. BOOL fRemoveFromProcessHash
  612. )
  613. /*++
  614. Routine Description:
  615. Disables the WAM_PROCESS. Any new requests for the application
  616. associated with this process will cause a new process to start
  617. after this function is called.
  618. Arguments:
  619. fRemoveFromProcessHash - If TRUE, then this function should remove
  620. the WAM_PROCESS from the hash table. This
  621. flag will be FALSE when this function is
  622. called from the WAM process manager's
  623. shutdown. In that case, the LKRHash
  624. DeleteIf function will handle removing the
  625. object from the hash.
  626. Return Value:
  627. HRESULT
  628. --*/
  629. {
  630. HRESULT hr = NOERROR;
  631. _fGoingAway = TRUE;
  632. //
  633. // Remove the process from the hash if directed.
  634. //
  635. if ( fRemoveFromProcessHash )
  636. {
  637. _pWamProcessManager->LockWamProcessHash();
  638. hr = _pWamProcessManager->RemoveWamProcessFromHash( this );
  639. }
  640. //
  641. // Once we remove the process from the hash, we should recycle it.
  642. // This will force COM to start a new process in the case where
  643. // new requests arrive for this AppWamClsid before the shutdown
  644. // code has a chance to kill off this instance.
  645. //
  646. VARIANT varInstanceId = {0};
  647. varInstanceId.vt = VT_BSTR;
  648. varInstanceId.bstrVal = _bstrInstanceId;
  649. hr = _pWamProcessManager->QueryCatalog()->RecycleApplicationInstances( &varInstanceId, 0 );
  650. //
  651. // This code could potentially fail under a number of circumstances.
  652. // For example, if we are disabling this application because it
  653. // crashed, we don't really expect to be able to recycle it.
  654. //
  655. // This is not a big deal. We'll just do some debug spew and get
  656. // on with it.
  657. //
  658. if ( FAILED( hr ) )
  659. {
  660. DBGPRINTF((
  661. DBG_CONTEXT,
  662. "Error 0x%08x occured attempting to recycle disabled "
  663. "application %S\r\n",
  664. hr, _szWamClsid
  665. ));
  666. }
  667. if ( fRemoveFromProcessHash )
  668. {
  669. _pWamProcessManager->UnlockWamProcessHash();
  670. }
  671. return hr;
  672. }
  673. HRESULT
  674. WAM_PROCESS::CleanupRequests(
  675. DWORD dwDrainTime
  676. )
  677. /*++
  678. Routine Description:
  679. This function is basically just a timer that allows time
  680. to pass until either the supplied timeout is reached, or
  681. all requests are completed, whichever is first.
  682. Arguments:
  683. dwDrainTime - The timeout in milliseconds
  684. Return Value:
  685. HRESULT
  686. --*/
  687. {
  688. DWORD dwWaitSoFar = 0;
  689. //
  690. // We will loop here until either the specified time has
  691. // passed, or until all requests are done.
  692. while ( _cCurrentRequests )
  693. {
  694. DBGPRINTF((
  695. DBG_CONTEXT,
  696. "WAM_PROCESS %p waiting for %d requests.\r\n",
  697. this,
  698. _cCurrentRequests
  699. ));
  700. Sleep( 200 );
  701. if ( dwDrainTime < ( dwWaitSoFar += 200 ) )
  702. {
  703. break;
  704. }
  705. }
  706. DBGPRINTF((
  707. DBG_CONTEXT,
  708. "WAM_PROCESS %p done draining with %d requests "
  709. "still outstanding.\r\n",
  710. this,
  711. _cCurrentRequests
  712. ));
  713. return NOERROR;
  714. }
  715. HRESULT
  716. WAM_PROCESS::Shutdown(
  717. VOID
  718. )
  719. /*++
  720. Routine Description:
  721. Shuts down the WAM_PROCESS and associated OOP host process
  722. Note that we make the assumption that the caller of this
  723. function has taken steps to ensure that new requests are
  724. properly routed. In the typical case, this means that
  725. somebody has called Disable to pull us off the hash table.
  726. In the special case where LKRHash calls this function when
  727. the WAM process has is being deleted, we assume that the
  728. web service is in shutdown state and no new requests are
  729. arriving.
  730. Also note that it's up to the caller to hold a reference
  731. if necessary to ensure that this object doesn't get
  732. destroyed while this function is running.
  733. Arguments:
  734. None
  735. Return Value:
  736. HRESULT
  737. --*/
  738. {
  739. HRESULT hr = NOERROR;
  740. //
  741. // If we get here, we expect that this object has been pulled off
  742. // the hash table (and that the _fGoingAway flag is set).
  743. //
  744. DBG_ASSERT( _fGoingAway );
  745. //
  746. // If there are no requests running, then we can gracefully unload
  747. // any extensions in the OOP host. Otherwise, we're basically
  748. // going to crash the OOP host by terminating it with requests in
  749. // flight.
  750. //
  751. // This latter case should only happen if we are being demand
  752. // unloaded, through the UI, via ADSI, or via IMSAdminBase.
  753. //
  754. if ( _cCurrentRequests == 0 )
  755. {
  756. _pIWam->WamUninitProcess();
  757. }
  758. //
  759. // Time to kill the process. We could potentially use the COM
  760. // ShutdownProcess API, but it doesn't really buy us anything.
  761. // We're just gonna terminate it.
  762. //
  763. DBG_ASSERT( _hProcess );
  764. if ( _hProcess )
  765. {
  766. TerminateProcess( _hProcess, 0 );
  767. CloseHandle( _hProcess );
  768. _hProcess = NULL;
  769. }
  770. //
  771. // Force COM to release any references it's holding
  772. // to ISAPI_REQUEST objects.
  773. //
  774. DisconnectIsapiRequests();
  775. //
  776. // Now that the process is gone, COM will clean up any
  777. // References on ISAPI_REQUEST objects that were held in
  778. // it. It is also possible that there are threads
  779. // unwinding in the core that are doing work on behalf
  780. // of the OOP host, and there may even be I/O completions
  781. // that will occur for this process.
  782. //
  783. // As these objects reach zero references, they will
  784. // release their references on this object. This object
  785. // will be destroyed when the last reference is released.
  786. //
  787. return hr;
  788. }
  789. HRESULT
  790. WAM_PROCESS::Unload(
  791. DWORD dwDrainTime
  792. )
  793. /*++
  794. Routine Description:
  795. Unloads the WAM_PROCESS, allowing for a timeout that allows
  796. outstanding requests to complete before killing them off.
  797. Arguments:
  798. dwDrainTime - The timeout in milliseconds before requests are killed
  799. Return Value:
  800. HRESULT
  801. --*/
  802. {
  803. HRESULT hr = NOERROR;
  804. //
  805. // We'd better not do this more than once.
  806. //
  807. if ( InterlockedExchange( &_fGoingAway , 1 ) == 0 )
  808. {
  809. //
  810. // WARNING - Calling Disable can potentially cause this object
  811. // to be destroyed. We need to take a reference until
  812. // the Shutdown function returns.
  813. //
  814. AddRef();
  815. hr = Disable();
  816. hr = CleanupRequests( dwDrainTime );
  817. hr = Shutdown();
  818. Release();
  819. }
  820. return hr;
  821. }
  822. VOID
  823. WAM_PROCESS::AddIsapiRequestToList(
  824. ISAPI_REQUEST * pIsapiRequest
  825. )
  826. {
  827. pIsapiRequest->AddRef();
  828. LockRequestList();
  829. InsertHeadList(
  830. &_RequestListHead,
  831. &pIsapiRequest->_leRequest
  832. );
  833. UnlockRequestList();
  834. }
  835. VOID
  836. WAM_PROCESS::RemoveIsapiRequestFromList(
  837. ISAPI_REQUEST * pIsapiRequest
  838. )
  839. {
  840. LockRequestList();
  841. RemoveEntryList( &pIsapiRequest->_leRequest );
  842. InitializeListHead( &pIsapiRequest->_leRequest );
  843. UnlockRequestList();
  844. pIsapiRequest->Release();
  845. }
  846. VOID
  847. WAM_PROCESS::DisconnectIsapiRequests(
  848. VOID
  849. )
  850. {
  851. LIST_ENTRY * pleTemp;
  852. ISAPI_REQUEST * pIsapiRequest;
  853. HRESULT hr;
  854. LockRequestList();
  855. pleTemp = _RequestListHead.Flink;
  856. while ( pleTemp != &_RequestListHead )
  857. {
  858. pIsapiRequest = CONTAINING_RECORD(
  859. pleTemp,
  860. ISAPI_REQUEST,
  861. _leRequest
  862. );
  863. DBG_ASSERT( pIsapiRequest );
  864. pIsapiRequest->AddRef();
  865. hr = CoDisconnectObject( pIsapiRequest, 0 );
  866. if ( FAILED( hr ) )
  867. {
  868. DBGPRINTF((
  869. DBG_CONTEXT,
  870. "Failed to disconnect ISAPI_REQUEST %p. HRESULT=%08x.\r\n",
  871. pIsapiRequest,
  872. hr
  873. ));
  874. DBG_ASSERT( FALSE && "Error disconnecting ISAPI_REQUEST." );
  875. }
  876. pleTemp = pleTemp->Flink;
  877. pIsapiRequest->Release();
  878. }
  879. UnlockRequestList();
  880. }
  881. WAM_PROCESS::~WAM_PROCESS()
  882. /*++
  883. Routine Description:
  884. Destructor
  885. Arguments:
  886. None
  887. Return Value:
  888. None
  889. --*/
  890. {
  891. DBG_ASSERT( _cCurrentRequests == 0 );
  892. if (_bstrInstanceId)
  893. {
  894. SysFreeString(_bstrInstanceId);
  895. _bstrInstanceId = NULL;
  896. }
  897. DeleteCriticalSection( &_csRequestList );
  898. IF_DEBUG( ISAPI )
  899. {
  900. DBGPRINTF((
  901. DBG_CONTEXT,
  902. "WAM_PROCESS %p has been destroyed.\r\n",
  903. this
  904. ));
  905. }
  906. _pWamProcessManager->Release();
  907. }
  908. HRESULT
  909. WAM_PROCESS_MANAGER::Create(
  910. VOID
  911. )
  912. /*++
  913. Routine Description:
  914. Initializes the parts of the WAM_PROCESS_MANAGER object not
  915. appropriate to the constructor. This includes instantiating
  916. the COM+ admin catalog interface.
  917. Arguments:
  918. None
  919. Return Value:
  920. HRESULT
  921. --*/
  922. {
  923. HRESULT hr = NOERROR;
  924. IF_DEBUG( ISAPI )
  925. {
  926. DBGPRINTF((
  927. DBG_CONTEXT,
  928. "Creating WAM_PROCESS_MANAGER %p.\r\n",
  929. this
  930. ));
  931. }
  932. //
  933. // Need to get the COM admin interface
  934. //
  935. hr = CoCreateInstance(
  936. CLSID_COMAdminCatalog,
  937. NULL,
  938. CLSCTX_INPROC_SERVER,
  939. IID_ICOMAdminCatalog2,
  940. (void**)&_pCatalog
  941. );
  942. if ( FAILED( hr ) )
  943. {
  944. DBGPRINTF((
  945. DBG_CONTEXT,
  946. "WAM_PROCESS_MANAGER %p. Failed to CoCreate ICOMAdminCatalog2.\r\n",
  947. this
  948. ));
  949. goto ErrorExit;
  950. }
  951. IF_DEBUG( ISAPI )
  952. {
  953. DBGPRINTF((
  954. DBG_CONTEXT,
  955. "WAM_PROCESS_MANAGER %p created successfully.\r\n",
  956. this
  957. ));
  958. }
  959. return hr;
  960. ErrorExit:
  961. DBG_ASSERT( FAILED( hr ) );
  962. DBGPRINTF((
  963. DBG_CONTEXT,
  964. "Attempt to create WAM_PROCESS_MANAGER %p has failed. HRESULT=%08x.\r\n",
  965. this,
  966. hr
  967. ));
  968. return hr;
  969. }
  970. HRESULT
  971. WAM_PROCESS_MANAGER::GetWamProcess(
  972. LPCWSTR szWamClsid,
  973. LPCWSTR szApplMdPathW,
  974. DWORD * pdwWamSubError,
  975. WAM_PROCESS ** ppWamProcess,
  976. LPCSTR szIsapiHandlerInstance
  977. )
  978. /*++
  979. Routine Description:
  980. Returns a WAM_PROCESS pointer associated with the specified
  981. CLSID. If no corresponding WAM_PROCESS exists, one will be
  982. started.
  983. Arguments:
  984. szWamClsid - The CLSID of the desired WAM_PROCESS
  985. szApplMdPathW - The metabase path of the application
  986. pdwWamSubError - Upon failed return, contains the WAM sub error
  987. (which is used to generate the text of the
  988. error response sent to the client)
  989. ppWamProcess - Upon return, contains the WAM_PROCESS pointer
  990. szIsapiHandlerInstance - The instance ID of the W3_ISAPI_HANDLER
  991. that's looking for a WAM_PROCESS. This
  992. is used for debugging purposes.
  993. Return Value:
  994. HRESULT
  995. --*/
  996. {
  997. HRESULT hr = NOERROR;
  998. WAM_PROCESS * pWamProcess = NULL;
  999. WAM_APP_INFO * pWamAppInfo = NULL;
  1000. DWORD dwNumPreviousCrashes;
  1001. DBG_ASSERT( szWamClsid );
  1002. DBG_ASSERT( szApplMdPathW );
  1003. DBG_ASSERT( pdwWamSubError );
  1004. DBG_ASSERT( ppWamProcess );
  1005. //
  1006. // Look to see if the WAM_PROCESS has already been
  1007. // loaded. If so, then we can just return it.
  1008. //
  1009. // This is the common path, and we want to avoid
  1010. // taking a lock for this case.
  1011. //
  1012. _WamProcessHash.FindKey( szWamClsid, &pWamProcess );
  1013. if ( pWamProcess != NULL )
  1014. {
  1015. *ppWamProcess = pWamProcess;
  1016. return hr;
  1017. }
  1018. //
  1019. // Ok, so we didn't already find it. Now, let's
  1020. // lock the hash table and load it.
  1021. //
  1022. LockWamProcessHash();
  1023. //
  1024. // Better check once more now that we have the lock just
  1025. // in case someone got to it since our initial check
  1026. // above.
  1027. //
  1028. _WamProcessHash.FindKey( szWamClsid, &pWamProcess );
  1029. if ( pWamProcess != NULL )
  1030. {
  1031. *ppWamProcess = pWamProcess;
  1032. goto ExitDone;
  1033. }
  1034. //
  1035. // Check to see if we have exceeded the AppOopRecoverLimit.
  1036. //
  1037. hr = GetWamProcessInfo(
  1038. szApplMdPathW,
  1039. &pWamAppInfo,
  1040. NULL
  1041. );
  1042. if ( FAILED( hr ) )
  1043. {
  1044. goto ExitDone;
  1045. }
  1046. if ( pWamAppInfo->_dwAppOopRecoverLimit != 0 )
  1047. {
  1048. hr = QueryCrashHistory(
  1049. szWamClsid,
  1050. &dwNumPreviousCrashes
  1051. );
  1052. if ( FAILED( hr ) )
  1053. {
  1054. goto ExitDone;
  1055. }
  1056. if ( dwNumPreviousCrashes >= pWamAppInfo->_dwAppOopRecoverLimit )
  1057. {
  1058. //
  1059. // If we've exceeded the recover limit,
  1060. // then we should fail now.
  1061. //
  1062. hr = E_FAIL;
  1063. *pdwWamSubError = IDS_WAM_NOMORERECOVERY_ERROR;
  1064. goto ExitDone;
  1065. }
  1066. }
  1067. //
  1068. // Try and create the new WAM_PROCESS...
  1069. //
  1070. pWamProcess = new WAM_PROCESS(
  1071. szWamClsid,
  1072. this,
  1073. szIsapiHandlerInstance
  1074. );
  1075. if ( !pWamProcess )
  1076. {
  1077. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1078. goto ExitDone;
  1079. }
  1080. //
  1081. // Create the WAM_PROCESS object. This will start the
  1082. // host process.
  1083. //
  1084. hr = pWamProcess->Create( szApplMdPathW );
  1085. if ( FAILED( hr ) )
  1086. {
  1087. DBGPRINTF((
  1088. DBG_CONTEXT,
  1089. "Failed to create WAM_PROCESS %p. Error 0x%08x\r\n",
  1090. pWamProcess, hr
  1091. ));
  1092. pWamProcess->Release();
  1093. *pdwWamSubError = IDS_WAM_FAILTOLOAD_ERROR;
  1094. goto ExitDone;
  1095. }
  1096. DBGPRINTF((
  1097. DBG_CONTEXT,
  1098. "WAM_PROCESS_MANAGER has created WAM_PROCESS %p.\r\n",
  1099. pWamProcess
  1100. ));
  1101. //
  1102. // Add the newly created WAM_PROCESS to the hash table
  1103. //
  1104. if ( _WamProcessHash.InsertRecord( pWamProcess ) != LK_SUCCESS )
  1105. {
  1106. pWamProcess->Release();
  1107. hr = E_FAIL; // CODEWORK - Consider passing lkreturn to caller
  1108. goto ExitDone;
  1109. }
  1110. //
  1111. // Finally, set the return object for the caller
  1112. //
  1113. *ppWamProcess = pWamProcess;
  1114. ExitDone:
  1115. UnlockWamProcessHash();
  1116. if ( pWamAppInfo != NULL )
  1117. {
  1118. pWamAppInfo->Release();
  1119. pWamAppInfo = NULL;
  1120. }
  1121. return hr;
  1122. }
  1123. HRESULT
  1124. WAM_PROCESS_MANAGER::GetWamProcessInfo(
  1125. LPCWSTR szAppPath,
  1126. WAM_APP_INFO ** ppWamAppInfo,
  1127. BOOL * pfIsLoaded
  1128. )
  1129. /*++
  1130. Routine Description:
  1131. Retrieves the app info associated with the provided metabase path
  1132. and indicates whether the application is currently loaded or not.
  1133. Note that this function is expensive because it reads the metabase
  1134. on each call. It is currently only called when an application is
  1135. unloaded or changed via the UI or an administrative API.
  1136. Don't call this function if you care about performance.
  1137. Arguments:
  1138. szAppPath - The metabase path (ie. "/LM/W3SVC/1/ROOT") to retrieve
  1139. ppWamAppInfo - Upon return, contains a pointer to the app info
  1140. pfIsLoaded - Upon return, indicates if the application is loaded
  1141. Return Value:
  1142. HRESULT
  1143. --*/
  1144. {
  1145. MB mb( g_pW3Server->QueryMDObject() );
  1146. WAM_APP_INFO * pWamAppInfo = NULL;
  1147. WAM_PROCESS * pWamProcess = NULL;
  1148. DWORD dwRequiredLen;
  1149. DWORD dwAppIsolated;
  1150. DWORD dwAppOopRecoverLimit;
  1151. WCHAR szClsid[MAX_PATH];
  1152. HRESULT hr = NOERROR;
  1153. DBG_ASSERT( szAppPath );
  1154. DBG_ASSERT( ppWamAppInfo );
  1155. //
  1156. // Get the info from the metabase
  1157. //
  1158. if ( !mb.Open( szAppPath ) )
  1159. {
  1160. hr = HRESULT_FROM_WIN32( GetLastError() );
  1161. goto Done;
  1162. }
  1163. //
  1164. // Get the AppIsolated value
  1165. //
  1166. if (!mb.GetDword(L"", MD_APP_ISOLATED, IIS_MD_UT_WAM, &dwAppIsolated, METADATA_INHERIT))
  1167. {
  1168. hr = HRESULT_FROM_WIN32(GetLastError());
  1169. goto Done;
  1170. }
  1171. //
  1172. // Set the CLSID
  1173. //
  1174. switch ( dwAppIsolated )
  1175. {
  1176. case APP_ISOLATED:
  1177. //
  1178. // Read it from the metabase
  1179. //
  1180. dwRequiredLen= SIZE_CLSID_STRING * sizeof(WCHAR);
  1181. if (!mb.GetString(L"", MD_APP_WAM_CLSID, IIS_MD_UT_WAM, szClsid, &dwRequiredLen, METADATA_INHERIT))
  1182. {
  1183. DBG_ASSERT(dwRequiredLen <= SIZE_CLSID_STRING * sizeof(WCHAR));
  1184. hr = HRESULT_FROM_WIN32( GetLastError() );
  1185. goto Done;
  1186. }
  1187. break;
  1188. case APP_POOL:
  1189. //
  1190. // Set it from the hardcoded pool clsid
  1191. //
  1192. wcsncpy( szClsid, POOL_WAM_CLSID, SIZE_CLSID_STRING );
  1193. break;
  1194. case APP_INPROC:
  1195. default:
  1196. //
  1197. // Set it to an empty string
  1198. //
  1199. szClsid[0] = L'\0';
  1200. break;
  1201. }
  1202. //
  1203. // Get the AppOopRecoverLimit
  1204. //
  1205. if (!mb.GetDword(L"", MD_APP_OOP_RECOVER_LIMIT, IIS_MD_UT_WAM, &dwAppOopRecoverLimit, METADATA_INHERIT))
  1206. {
  1207. //
  1208. // Ok, so we didn't get the value. The default is dependent
  1209. // on whether this is the pool or isolated (and in the case
  1210. // of inproc, we'll just default to zero.)
  1211. //
  1212. if ( dwAppIsolated == APP_ISOLATED )
  1213. {
  1214. dwAppOopRecoverLimit = DEFAULT_RECOVER_LIMIT;
  1215. }
  1216. else
  1217. {
  1218. dwAppOopRecoverLimit = 0;
  1219. }
  1220. }
  1221. //
  1222. // Allocate a new WAM_APP_INFO
  1223. //
  1224. pWamAppInfo = new WAM_APP_INFO( (LPWSTR)szAppPath, szClsid, dwAppIsolated );
  1225. if ( !pWamAppInfo )
  1226. {
  1227. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1228. goto Done;
  1229. }
  1230. pWamAppInfo->_dwAppOopRecoverLimit = dwAppOopRecoverLimit;
  1231. //
  1232. // Return the new object
  1233. //
  1234. *ppWamAppInfo = pWamAppInfo;
  1235. //
  1236. // Finally, check to see if the application is loaded
  1237. //
  1238. if ( pfIsLoaded != NULL )
  1239. {
  1240. if ( pWamAppInfo->_dwAppIsolated != APP_INPROC )
  1241. {
  1242. _WamProcessHash.FindKey( pWamAppInfo->_szClsid,
  1243. &pWamProcess );
  1244. }
  1245. if ( pWamProcess != NULL )
  1246. {
  1247. *pfIsLoaded = TRUE;
  1248. pWamProcess->Release();
  1249. pWamProcess = NULL;
  1250. }
  1251. else
  1252. {
  1253. *pfIsLoaded = FALSE;
  1254. }
  1255. }
  1256. Done:
  1257. mb.Close();
  1258. return hr;
  1259. }
  1260. HRESULT
  1261. WAM_PROCESS_MANAGER::RemoveWamProcessFromHash(
  1262. WAM_PROCESS * pWamProcess
  1263. )
  1264. /*++
  1265. Routine Description:
  1266. Removes a WAM_PROCESS from the hash
  1267. Arguments:
  1268. pWamProcess - The WAM_PROCESS to remove
  1269. Return Value:
  1270. HRESULT
  1271. --*/
  1272. {
  1273. HRESULT hr = S_OK;
  1274. LK_RETCODE lkret;
  1275. lkret = _WamProcessHash.DeleteRecord( pWamProcess );
  1276. if ( lkret != LK_SUCCESS )
  1277. {
  1278. DBGPRINTF((
  1279. DBG_CONTEXT,
  1280. "Failed to delete WAM_PROCESS %p from hash. LK_RETCODE=0x%p.\r\n",
  1281. pWamProcess,
  1282. lkret
  1283. ));
  1284. hr = E_FAIL; // CODEWORK - Consider passing lkreturn to caller
  1285. }
  1286. else
  1287. {
  1288. IF_DEBUG( ISAPI )
  1289. {
  1290. DBGPRINTF((
  1291. DBG_CONTEXT,
  1292. "Removed WAM_PROCESS %p from hash.\r\n",
  1293. pWamProcess
  1294. ));
  1295. }
  1296. }
  1297. return hr;
  1298. }
  1299. // static
  1300. LK_PREDICATE
  1301. WAM_PROCESS_MANAGER::UnloadWamProcess(
  1302. WAM_PROCESS * pWamProcess,
  1303. void *
  1304. )
  1305. /*++
  1306. Routine Description:
  1307. Unloads a WAM_PROCESS. This function is exclusively called by
  1308. LKRHash's DeleteIf function during WAM_PROCESS_MANAGER shutdown
  1309. just before the hash table is deleted.
  1310. Arguments:
  1311. pWamProcess - The WAM_PROCESS to shut down
  1312. pvState - Required by LKRHash - we don't use it
  1313. Return Value:
  1314. HRESULT
  1315. --*/
  1316. {
  1317. HRESULT hr = NOERROR;
  1318. DBG_ASSERT( pWamProcess );
  1319. DBGPRINTF((
  1320. DBG_CONTEXT,
  1321. "Unloading application %S.\r\n",
  1322. pWamProcess->QueryClsid()
  1323. ));
  1324. hr = pWamProcess->Disable( FALSE );
  1325. hr = pWamProcess->CleanupRequests( 0 );
  1326. hr = pWamProcess->Shutdown();
  1327. DBG_ASSERT( SUCCEEDED( hr ) );
  1328. return LKP_PERFORM;
  1329. }
  1330. HRESULT
  1331. WAM_PROCESS_MANAGER::Shutdown(
  1332. VOID
  1333. )
  1334. /*++
  1335. Routine Description:
  1336. Shuts down the WAM_PROCESS_MANAGER
  1337. Arguments:
  1338. None
  1339. Return Value:
  1340. HRESULT
  1341. --*/
  1342. {
  1343. HRESULT hr = NOERROR;
  1344. DBGPRINTF((
  1345. DBG_CONTEXT,
  1346. "Shutting down WAM_PROCESS_MANAGER.\r\n"
  1347. ));
  1348. _WamProcessHash.DeleteIf( UnloadWamProcess, NULL );
  1349. return hr;
  1350. }
  1351. VOID
  1352. WAM_PROCESS_MANAGER::RegisterCrash(
  1353. LPCWSTR szWamClsid
  1354. )
  1355. /*++
  1356. Routine Description:
  1357. Updates the crash count for the specified application
  1358. Arguments:
  1359. szWamClsid - The CLSID of the WAM whose process crashed
  1360. Return Value:
  1361. None
  1362. --*/
  1363. {
  1364. WAM_CRASH_RECORD * pWamCrashRecord = NULL;
  1365. LIST_ENTRY * pleTemp = NULL;
  1366. LockCrashHistoryList();
  1367. pleTemp = _CrashHistoryList.Flink;
  1368. while ( pleTemp != &_CrashHistoryList )
  1369. {
  1370. pWamCrashRecord = CONTAINING_RECORD(
  1371. pleTemp,
  1372. WAM_CRASH_RECORD,
  1373. leCrashHistory
  1374. );
  1375. DBG_ASSERT( pWamCrashRecord );
  1376. if ( _wcsicmp( pWamCrashRecord->szClsid, szWamClsid ) == 0 )
  1377. {
  1378. break;
  1379. }
  1380. pleTemp = pleTemp->Flink;
  1381. pWamCrashRecord = NULL;
  1382. }
  1383. UnlockCrashHistoryList();
  1384. //
  1385. // If pWamCrashRecord is non-NULL, then we need to increment
  1386. // its crash history. If it is NULL, then we won't worry about
  1387. // it.
  1388. //
  1389. if ( pWamCrashRecord != NULL )
  1390. {
  1391. pWamCrashRecord->dwNumCrashes++;
  1392. }
  1393. }
  1394. HRESULT
  1395. WAM_PROCESS_MANAGER::QueryCrashHistory(
  1396. LPCWSTR szWamClsid,
  1397. DWORD * pdwNumCrashes
  1398. )
  1399. /*++
  1400. Routine Description:
  1401. Queries the number of times a particular WAM has crashed.
  1402. Arguments:
  1403. szWamClsid - The CLSID of the WAM whose process crashed
  1404. pdwNumCrashes - On successful return, contains the data
  1405. Return Value:
  1406. HRESULT
  1407. --*/
  1408. {
  1409. WAM_CRASH_RECORD * pWamCrashRecord = NULL;
  1410. LIST_ENTRY * pleTemp = NULL;
  1411. HRESULT hr = NOERROR;
  1412. LockCrashHistoryList();
  1413. pleTemp = _CrashHistoryList.Flink;
  1414. while ( pleTemp != &_CrashHistoryList )
  1415. {
  1416. pWamCrashRecord = CONTAINING_RECORD(
  1417. pleTemp,
  1418. WAM_CRASH_RECORD,
  1419. leCrashHistory
  1420. );
  1421. DBG_ASSERT( pWamCrashRecord );
  1422. if ( _wcsicmp( pWamCrashRecord->szClsid, szWamClsid ) == 0 )
  1423. {
  1424. break;
  1425. }
  1426. pleTemp = pleTemp->Flink;
  1427. pWamCrashRecord = NULL;
  1428. }
  1429. //
  1430. // If pWamCrashRecord is non-NULL, then we need to return
  1431. // it's crash history.
  1432. //
  1433. // If it is NULL, then we didn't find it, and we need to
  1434. // create a new record.
  1435. //
  1436. if ( pWamCrashRecord != NULL )
  1437. {
  1438. *pdwNumCrashes = pWamCrashRecord->dwNumCrashes;
  1439. }
  1440. else
  1441. {
  1442. pWamCrashRecord = new WAM_CRASH_RECORD;
  1443. if ( pWamCrashRecord == NULL )
  1444. {
  1445. hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
  1446. }
  1447. else
  1448. {
  1449. wcsncpy( pWamCrashRecord->szClsid, szWamClsid, SIZE_CLSID_STRING );
  1450. pWamCrashRecord->szClsid[SIZE_CLSID_STRING-1] = L'\0';
  1451. InsertHeadList(
  1452. &_CrashHistoryList,
  1453. &pWamCrashRecord->leCrashHistory
  1454. );
  1455. }
  1456. *pdwNumCrashes = 0;
  1457. }
  1458. UnlockCrashHistoryList();
  1459. return hr;
  1460. }