Source code of Windows XP (NT5)
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.

685 lines
17 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. net\routing\ipx\sap\sapmain.c
  5. Abstract:
  6. SAP DLL main module and thread container.
  7. Author:
  8. Vadim Eydelman 05-15-1995
  9. Revision History:
  10. --*/
  11. #include "sapp.h"
  12. // Time limit for shutdown broadcast
  13. ULONG ShutdownTimeout=SAP_SHUTDOWN_TIMEOUT_DEF;
  14. // Indeces of synchronization objects used to control asynchronous
  15. // subsystems of SAP agent
  16. // Main thread signalling event
  17. #define STOP_EVENT_IDX 0
  18. #define RECV_COMPLETED_IDX (STOP_EVENT_IDX+1)
  19. // Timer queue requires attention
  20. #define TIMER_WAKEUP_IDX (RECV_COMPLETED_IDX+1)
  21. // Server table aging queue requires processing
  22. #define SERVER_TABLE_TIMER_IDX (TIMER_WAKEUP_IDX+1)
  23. // Server table sorted list requires update
  24. #define SERVER_TABLE_UPDATE_IDX (SERVER_TABLE_TIMER_IDX+1)
  25. // Adapter change signalled by network driver (for standalone SAP only)
  26. #define ADAPTER_CHG_IDX (SERVER_TABLE_UPDATE_IDX+1)
  27. // Number of syncronization objects
  28. #define ROUTING_NUM_OF_OBJECTS (SERVER_TABLE_UPDATE_IDX+1)
  29. #define STANDALONE_NUM_OF_OBJECTS (ADAPTER_CHG_IDX+1)
  30. #define MAX_OBJECTS STANDALONE_NUM_OF_OBJECTS
  31. /* Global Data */
  32. // DLL module instance handle
  33. HANDLE hDLLInstance;
  34. // Handle of main thread
  35. HANDLE MainThreadHdl;
  36. // Operational state of the agent
  37. ULONG OperationalState=OPER_STATE_DOWN;
  38. // Lock that protects changes in the state and state transitions
  39. CRITICAL_SECTION OperationalStateLock;
  40. // TRUE between start and stop service calls
  41. volatile BOOLEAN ServiceIfActive=FALSE;
  42. // TRUE between start and stop protocol calls
  43. volatile BOOLEAN RouterIfActive=FALSE;
  44. // TRUE if sap is part of the router, FALSE for standalone SAP agent
  45. // It is computed based on two values above with RouterIfActive having
  46. // precedence. In stays where it was during transition periods and changes
  47. // only when transition is completed (it can only be changed by the main
  48. // thread).
  49. volatile BOOLEAN Routing=FALSE;
  50. /* Local static data */
  51. // Async subsystem synchronization objects
  52. HANDLE WaitObjects[MAX_OBJECTS] = {NULL};
  53. // Time we will die at when told to shutdown
  54. ULONG StopTime;
  55. TCHAR ModuleName[MAX_PATH+1];
  56. // Local prototypes
  57. BOOL WINAPI DllMain(
  58. HINSTANCE hinstDLL,
  59. DWORD fdwReason,
  60. LPVOID lpvReserved
  61. );
  62. DWORD WINAPI
  63. MainThread (
  64. LPVOID param
  65. );
  66. VOID
  67. ReadRegistry (
  68. VOID
  69. );
  70. /*++
  71. *******************************************************************
  72. D l l M a i n
  73. Routine Description:
  74. Dll entry point to be called from CRTstartup dll entry point (it
  75. will be actually an entry point for this dll)
  76. Arguments:
  77. hinstDLL - handle of DLL module
  78. fdwReason - reason for calling function
  79. lpvReserved - reserved
  80. Return Value:
  81. TRUE - process initialization was performed OK
  82. FALSE - intialization failed
  83. *******************************************************************
  84. --*/
  85. BOOL WINAPI DllMain(
  86. HINSTANCE hinstDLL,
  87. DWORD fdwReason,
  88. LPVOID lpvReserved
  89. ) {
  90. STARTUPINFO info;
  91. switch (fdwReason) {
  92. case DLL_PROCESS_ATTACH: // We are being attached to a new process
  93. hDLLInstance = hinstDLL;
  94. GetModuleFileName (hinstDLL, ModuleName,
  95. sizeof (ModuleName)/sizeof (ModuleName[0]));
  96. InitializeCriticalSection (&OperationalStateLock);
  97. return TRUE;
  98. case DLL_PROCESS_DETACH: // The process is exiting
  99. ASSERT (OperationalState==OPER_STATE_DOWN);
  100. DeleteCriticalSection (&OperationalStateLock);
  101. default: // Not interested in all other cases
  102. return TRUE;
  103. }
  104. }
  105. /*++
  106. *******************************************************************
  107. C r e a t e A l l C o m p o n e n t s
  108. Routine Description:
  109. Calls all sap componenets with initialization call and compiles an
  110. array of synchronization objects from objects returned from each
  111. individual component
  112. Arguments:
  113. None
  114. Return Value:
  115. NO_ERROR - component initialization was performed OK
  116. other - operation failed (windows error code)
  117. *******************************************************************
  118. --*/
  119. DWORD
  120. CreateAllComponents (
  121. HANDLE RMNotificationEvent
  122. ) {
  123. DWORD status;
  124. DbgInitialize (hDLLInstance);
  125. ReadRegistry ();
  126. status = CreateServerTable (
  127. &WaitObjects[SERVER_TABLE_UPDATE_IDX],
  128. &WaitObjects[SERVER_TABLE_TIMER_IDX]);
  129. if (status==NO_ERROR) {
  130. status = IpxSapCreateTimerQueue (&WaitObjects[TIMER_WAKEUP_IDX]);
  131. if (status==NO_ERROR) {
  132. status = CreateInterfaceTable ();
  133. if (status==NO_ERROR) {
  134. status = CreateIOQueue (&WaitObjects[RECV_COMPLETED_IDX]);
  135. if (status==NO_ERROR) {
  136. status = InitializeLPCStuff ();
  137. if (status==NO_ERROR) {
  138. status = CreateFilterTable ();
  139. if (status==NO_ERROR) {
  140. status = InitializeWorkers (WaitObjects[RECV_COMPLETED_IDX]);
  141. if (status==NO_ERROR) {
  142. WaitObjects[STOP_EVENT_IDX] =
  143. CreateEvent (NULL,
  144. FALSE, //Autoreset
  145. FALSE, // non-signalled
  146. NULL);
  147. if (WaitObjects[STOP_EVENT_IDX]!=NULL) {
  148. if (RMNotificationEvent == NULL)
  149. status = CreateAdapterPort (&WaitObjects[ADAPTER_CHG_IDX]);
  150. else
  151. status = CreateResultQueue (RMNotificationEvent);
  152. if (status==NO_ERROR)
  153. return NO_ERROR;
  154. }
  155. else {
  156. status = GetLastError ();
  157. Trace (DEBUG_FAILURES, "File: %s, line %ld."
  158. " Could not create stop event (gle:%ld).",
  159. __FILE__, __LINE__, status);
  160. }
  161. DeleteWorkers ();
  162. }
  163. DeleteFilterTable ();
  164. }
  165. DeleteLPCStuff();
  166. }
  167. DeleteIOQueue ();
  168. }
  169. DeleteInterfaceTable ();
  170. }
  171. IpxSapDeleteTimerQueue ();
  172. }
  173. DeleteServerTable ();
  174. }
  175. return status;
  176. }
  177. /*++
  178. *******************************************************************
  179. D e l e t e A l l C o m p o n e n t s
  180. Routine Description:
  181. Releases all resources allocated by SAP agent
  182. Arguments:
  183. None
  184. Return Value:
  185. NO_ERROR - SAP agent was unloaded OK
  186. other - operation failed (windows error code)
  187. *******************************************************************
  188. --*/
  189. DWORD
  190. DeleteAllComponents (
  191. void
  192. ) {
  193. UINT i;
  194. DWORD status;
  195. EnterCriticalSection (&OperationalStateLock);
  196. OperationalState = OPER_STATE_DOWN;
  197. LeaveCriticalSection (&OperationalStateLock);
  198. // Stop now
  199. StopTime = GetTickCount ();
  200. CloseHandle (WaitObjects[STOP_EVENT_IDX]);
  201. DeleteFilterTable ();
  202. DeleteLPCStuff ();
  203. DeleteIOQueue ();
  204. DeleteInterfaceTable ();
  205. IpxSapDeleteTimerQueue ();
  206. DeleteServerTable ();
  207. DeleteWorkers ();
  208. DbgStop ();
  209. return NO_ERROR;
  210. }
  211. /*++
  212. *******************************************************************
  213. S t a r t S A P
  214. Routine Description:
  215. Starts SAP threads
  216. Arguments:
  217. None
  218. Return Value:
  219. NO_ERROR - threads started OK
  220. other (windows error code) - start failed
  221. *******************************************************************
  222. --*/
  223. DWORD
  224. StartSAP (
  225. VOID
  226. ) {
  227. DWORD status;
  228. status = StartLPC ();
  229. if (status==NO_ERROR) {
  230. status = StartIO ();
  231. if (status==NO_ERROR) {
  232. DWORD threadID;
  233. MainThreadHdl = CreateThread (NULL,
  234. 0,
  235. &MainThread,
  236. NULL,
  237. 0,
  238. &threadID);
  239. if (MainThreadHdl!=NULL) {
  240. OperationalState = OPER_STATE_UP;
  241. return NO_ERROR;
  242. }
  243. else {
  244. status = GetLastError ();
  245. Trace (DEBUG_FAILURES, "File: %s, line %ld."
  246. " Failed to launch IO thread (gle:%ld).",
  247. __FILE__, __LINE__, status);
  248. }
  249. StopIO ();
  250. }
  251. ShutdownLPC ();
  252. }
  253. OperationalState = OPER_STATE_DOWN;
  254. return status;
  255. }
  256. /*++
  257. *******************************************************************
  258. S t o p S A P
  259. Routine Description:
  260. Signals SAP threads to stop
  261. Arguments:
  262. No used
  263. Return Value:
  264. None
  265. *******************************************************************
  266. --*/
  267. VOID
  268. StopSAP (
  269. void
  270. ) {
  271. BOOL res;
  272. OperationalState = OPER_STATE_STOPPING;
  273. StopTime = GetTickCount ()+ShutdownTimeout*1000;
  274. res = SetEvent (WaitObjects[STOP_EVENT_IDX]);
  275. ASSERTERRMSG ("Could not set stop event in StopSAP ", res);
  276. }
  277. /*++
  278. *******************************************************************
  279. R e s u l t R e t r e i v e d C B
  280. Routine Description:
  281. Async result manager call back routine that signals IO thread when
  282. stop message is retreived by router manager
  283. Arguments:
  284. No used
  285. Return Value:
  286. None
  287. *******************************************************************
  288. --*/
  289. VOID
  290. ResultRetreivedCB (
  291. PAR_PARAM_BLOCK rslt
  292. ) {
  293. BOOL res;
  294. UNREFERENCED_PARAMETER(rslt);
  295. res = SetEvent (WaitObjects[STOP_EVENT_IDX]);
  296. ASSERTERRMSG ("Could not set stop event in result retreived CB", res);
  297. }
  298. /*++
  299. *******************************************************************
  300. M a i n T h r e a d
  301. Routine Description:
  302. Thread in which context we'll perform async IO and maintain timer
  303. queues.
  304. It is also used to launch and control other threads of SAP agent
  305. Arguments:
  306. None
  307. Return Value:
  308. None
  309. *******************************************************************
  310. --*/
  311. DWORD WINAPI
  312. MainThread (
  313. LPVOID param
  314. ) {
  315. DWORD status;
  316. UINT i;
  317. DWORD nObjects;
  318. HANDLE enumHdl;
  319. HINSTANCE hModule;
  320. hModule = LoadLibrary (ModuleName);
  321. Restart:
  322. Routing = RouterIfActive;
  323. if (Routing) {
  324. nObjects = ROUTING_NUM_OF_OBJECTS;
  325. }
  326. else {
  327. nObjects = STANDALONE_NUM_OF_OBJECTS;
  328. }
  329. while ((status = WaitForMultipleObjectsEx (
  330. nObjects,
  331. WaitObjects,
  332. FALSE, // Wait any
  333. INFINITE,
  334. TRUE))!=WAIT_OBJECT_0+STOP_EVENT_IDX) {
  335. switch (status) {
  336. case WAIT_OBJECT_0+RECV_COMPLETED_IDX:
  337. InitReqItem ();
  338. break;
  339. case WAIT_OBJECT_0+TIMER_WAKEUP_IDX:
  340. ProcessTimerQueue ();
  341. break;
  342. case WAIT_OBJECT_0+SERVER_TABLE_TIMER_IDX:
  343. ProcessExpirationQueue ();
  344. break;
  345. case WAIT_OBJECT_0+SERVER_TABLE_UPDATE_IDX:
  346. UpdateSortedList ();
  347. break;
  348. case WAIT_OBJECT_0+ADAPTER_CHG_IDX:
  349. if (!RouterIfActive)
  350. ProcessAdapterEvents ();
  351. break;
  352. case WAIT_IO_COMPLETION:
  353. break;
  354. default:
  355. ASSERTMSG ("Unexpected return code from WaitFromObjects"
  356. " in IO thread ", FALSE);
  357. break;
  358. }
  359. }
  360. enumHdl = CreateListEnumerator (SDB_HASH_TABLE_LINK,
  361. 0xFFFF,
  362. NULL,
  363. INVALID_INTERFACE_INDEX,
  364. 0xFFFFFFFF,
  365. SDB_DISABLED_NODE_FLAG);
  366. if (ServiceIfActive || RouterIfActive) {
  367. if (enumHdl!=NULL)
  368. EnumerateServers (enumHdl, DeleteNonLocalServersCB, enumHdl);
  369. }
  370. else {
  371. ShutdownLPC ();
  372. if (enumHdl!=NULL)
  373. EnumerateServers (enumHdl, DeleteAllServersCB, enumHdl);
  374. }
  375. if (enumHdl)
  376. {
  377. DeleteListEnumerator (enumHdl);
  378. }
  379. if (!RouterIfActive) {
  380. ShutdownInterfaces (WaitObjects[STOP_EVENT_IDX]);
  381. ExpireTimerQueue ();
  382. while ((status = WaitForMultipleObjectsEx (
  383. ROUTING_NUM_OF_OBJECTS,
  384. WaitObjects,
  385. FALSE, // Wait any
  386. INFINITE,
  387. TRUE))!=WAIT_OBJECT_0+STOP_EVENT_IDX) {
  388. switch (status) {
  389. case WAIT_OBJECT_0+RECV_COMPLETED_IDX:
  390. // No more recv requests
  391. break;
  392. case WAIT_OBJECT_0+TIMER_WAKEUP_IDX:
  393. ProcessTimerQueue ();
  394. break;
  395. case WAIT_OBJECT_0+SERVER_TABLE_TIMER_IDX:
  396. ProcessExpirationQueue ();
  397. break;
  398. case WAIT_OBJECT_0+SERVER_TABLE_UPDATE_IDX:
  399. UpdateSortedList ();
  400. break;
  401. case WAIT_IO_COMPLETION:
  402. break;
  403. default:
  404. ASSERTMSG ("Unexpected return code from WaitForObjects"
  405. " in IO thread", FALSE);
  406. }
  407. }
  408. if (!ServiceIfActive) {
  409. StopIO ();
  410. StopInterfaces ();
  411. ExpireTimerQueue ();
  412. ShutdownWorkers (WaitObjects[STOP_EVENT_IDX]);
  413. while ((status=WaitForSingleObjectEx (
  414. WaitObjects[STOP_EVENT_IDX],
  415. INFINITE,
  416. TRUE))!=WAIT_OBJECT_0) {
  417. switch (status) {
  418. case WAIT_IO_COMPLETION:
  419. break;
  420. default:
  421. ASSERTMSG (
  422. "Unexpected status when waiting for worker shutdown ",
  423. FALSE);
  424. break;
  425. }
  426. }
  427. }
  428. }
  429. if (Routing) {
  430. // Signal completion of stop operation to
  431. // router manager
  432. static AR_PARAM_BLOCK ar;
  433. ar.event = ROUTER_STOPPED;
  434. ar.freeRsltCB = ResultRetreivedCB;
  435. EnqueueResult (&ar);
  436. while ((status = WaitForSingleObjectEx (
  437. WaitObjects[STOP_EVENT_IDX],
  438. INFINITE,
  439. TRUE))!=WAIT_OBJECT_0) {
  440. switch (status) {
  441. case WAIT_IO_COMPLETION:
  442. break;
  443. default:
  444. ASSERTMSG (
  445. "Unexpected status when waiting for router callback ",
  446. FALSE);
  447. break;
  448. }
  449. }
  450. DeleteResultQueue ();
  451. if (ServiceIfActive) {
  452. status = CreateAdapterPort (&WaitObjects[ADAPTER_CHG_IDX]);
  453. if (status==NO_ERROR) {
  454. EnterCriticalSection (&OperationalStateLock);
  455. OperationalState = OPER_STATE_UP;
  456. LeaveCriticalSection (&OperationalStateLock);
  457. goto Restart;
  458. }
  459. else
  460. ServiceIfActive = FALSE;
  461. }
  462. EnterCriticalSection (&OperationalStateLock);
  463. CloseHandle (MainThreadHdl);
  464. MainThreadHdl = NULL;
  465. LeaveCriticalSection (&OperationalStateLock);
  466. }
  467. else {
  468. DeleteAdapterPort ();
  469. WaitObjects [ADAPTER_CHG_IDX] = NULL;
  470. if (RouterIfActive) {
  471. EnterCriticalSection (&OperationalStateLock);
  472. OperationalState = OPER_STATE_UP;
  473. LeaveCriticalSection (&OperationalStateLock);
  474. goto Restart;
  475. }
  476. }
  477. // Make sure all threads get a chance to complete
  478. Sleep (1000);
  479. DeleteAllComponents ();
  480. FreeLibraryAndExitThread (hModule, 0);
  481. return 0;
  482. }
  483. #define MYTEXTW1(str) L##str
  484. #define MYTEXTW2(str) MYTEXTW1(str)
  485. #define REGISTRY_PARAM_ENTRY(name,val) { \
  486. NULL, \
  487. RTL_QUERY_REGISTRY_DIRECT, \
  488. MYTEXTW2(name##_STR), \
  489. &val, \
  490. REG_DWORD, \
  491. &val, \
  492. sizeof (DWORD) \
  493. }
  494. #define REGISTRY_CHECK(name,val) { \
  495. if (val<name##_MIN) { \
  496. Trace (DEBUG_FAILURES, name##_STR " is to small %ld!", val); \
  497. val = name##_MIN; \
  498. } \
  499. else if (val>name##_MAX) { \
  500. Trace (DEBUG_FAILURES, name##_STR " is to big %ld!", val); \
  501. val = name##_MAX; \
  502. } \
  503. if (val!=name##_DEF) \
  504. Trace (DEBUG_FAILURES, name##_STR" is set to %ld.", val); \
  505. }
  506. #define REGISTRY_CHECK_DEF(name,val) { \
  507. if (val<name##_MIN) { \
  508. Trace (DEBUG_FAILURES, name##_STR " is to small %ld!", val); \
  509. val = name##_DEF; \
  510. } \
  511. else if (val>name##_MAX) { \
  512. Trace (DEBUG_FAILURES, name##_STR " is to big %ld!", val); \
  513. val = name##_DEF; \
  514. } \
  515. if (val!=name##_DEF) \
  516. Trace (DEBUG_FAILURES, name##_STR " is set to %ld.", val); \
  517. }
  518. VOID
  519. ReadRegistry (
  520. VOID
  521. ) {
  522. DWORD rc;
  523. HKEY hKey;
  524. static RTL_QUERY_REGISTRY_TABLE ParamTable[] = {
  525. { NULL,
  526. RTL_QUERY_REGISTRY_SUBKEY,
  527. L"Parameters" },
  528. REGISTRY_PARAM_ENTRY (SAP_UPDATE_INTERVAL, UpdateInterval),
  529. REGISTRY_PARAM_ENTRY (SAP_AGING_TIMEOUT, ServerAgingTimeout),
  530. REGISTRY_PARAM_ENTRY (SAP_WAN_UPDATE_MODE, WanUpdateMode),
  531. REGISTRY_PARAM_ENTRY (SAP_WAN_UPDATE_INTERVAL,WanUpdateInterval),
  532. REGISTRY_PARAM_ENTRY (SAP_MAX_UNPROCESSED_REQUESTS,
  533. MaxUnprocessedRequests),
  534. REGISTRY_PARAM_ENTRY (SAP_RESPOND_FOR_INTERNAL,
  535. RespondForInternalServers),
  536. REGISTRY_PARAM_ENTRY (SAP_DELAY_RESPONSE_TO_GENERAL,
  537. DelayResponseToGeneral),
  538. REGISTRY_PARAM_ENTRY (SAP_DELAY_CHANGE_BROADCAST,
  539. DelayChangeBroadcast),
  540. REGISTRY_PARAM_ENTRY (SAP_SDB_MAX_HEAP_SIZE,SDBMaxHeapSize),
  541. REGISTRY_PARAM_ENTRY (SAP_SDB_SORT_LATENCY, SDBSortLatency),
  542. REGISTRY_PARAM_ENTRY (SAP_SDB_MAX_UNSORTED, SDBMaxUnsortedServers),
  543. REGISTRY_PARAM_ENTRY (SAP_TRIGGERED_UPDATE_CHECK_INTERVAL,
  544. TriggeredUpdateCheckInterval),
  545. REGISTRY_PARAM_ENTRY (SAP_MAX_TRIGGERED_UPDATE_REQUESTS,
  546. MaxTriggeredUpdateRequests),
  547. REGISTRY_PARAM_ENTRY (SAP_SHUTDOWN_TIMEOUT, ShutdownTimeout),
  548. REGISTRY_PARAM_ENTRY (SAP_REQUESTS_PER_INTF,NewRequestsPerInterface),
  549. REGISTRY_PARAM_ENTRY (SAP_MIN_REQUESTS, MinPendingRequests),
  550. {
  551. NULL
  552. }
  553. };
  554. rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  555. TEXT (SAP_ROUTER_REGISTRY_KEY_STR),
  556. 0,
  557. KEY_READ,
  558. &hKey
  559. );
  560. if ((rc!=NO_ERROR) && !Routing) {
  561. rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  562. TEXT (SAP_SERVICE_REGISTRY_KEY_STR),
  563. 0,
  564. KEY_READ,
  565. &hKey
  566. );
  567. }
  568. if (rc==NO_ERROR) {
  569. NTSTATUS status;
  570. status = RtlQueryRegistryValues(
  571. RTL_REGISTRY_HANDLE,
  572. (PWSTR)hKey,
  573. ParamTable,
  574. NULL,
  575. NULL);
  576. if (NT_SUCCESS (status)) {
  577. REGISTRY_CHECK (SAP_UPDATE_INTERVAL, UpdateInterval);
  578. REGISTRY_CHECK (SAP_AGING_TIMEOUT, ServerAgingTimeout);
  579. REGISTRY_CHECK_DEF (SAP_WAN_UPDATE_MODE, (int)WanUpdateMode);
  580. REGISTRY_CHECK (SAP_WAN_UPDATE_INTERVAL,WanUpdateInterval);
  581. REGISTRY_CHECK (SAP_MAX_UNPROCESSED_REQUESTS,
  582. MaxUnprocessedRequests);
  583. REGISTRY_CHECK_DEF (SAP_RESPOND_FOR_INTERNAL,
  584. (int) RespondForInternalServers);
  585. REGISTRY_CHECK (SAP_DELAY_RESPONSE_TO_GENERAL,
  586. (int) DelayResponseToGeneral);
  587. REGISTRY_CHECK (SAP_DELAY_CHANGE_BROADCAST,
  588. (int) DelayChangeBroadcast);
  589. REGISTRY_CHECK (SAP_SDB_MAX_HEAP_SIZE, SDBMaxHeapSize);
  590. REGISTRY_CHECK (SAP_SDB_SORT_LATENCY, SDBSortLatency);
  591. REGISTRY_CHECK (SAP_SDB_MAX_UNSORTED, SDBMaxUnsortedServers);
  592. REGISTRY_CHECK (SAP_TRIGGERED_UPDATE_CHECK_INTERVAL,
  593. TriggeredUpdateCheckInterval);
  594. REGISTRY_CHECK (SAP_MAX_TRIGGERED_UPDATE_REQUESTS,
  595. MaxTriggeredUpdateRequests);
  596. REGISTRY_CHECK (SAP_SHUTDOWN_TIMEOUT, ShutdownTimeout);
  597. REGISTRY_CHECK (SAP_REQUESTS_PER_INTF, NewRequestsPerInterface);
  598. REGISTRY_CHECK (SAP_MIN_REQUESTS, MinPendingRequests);
  599. }
  600. RegCloseKey (hKey);
  601. }
  602. }