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.

1777 lines
41 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. main.c
  5. Abstract:
  6. This is the main routine for the Internet Services suite.
  7. Author:
  8. David Treadwell (davidtr) 7-27-93
  9. Revision History:
  10. Murali Krishnan ( Muralik) 16-Nov-1994 Added Gopher service
  11. Murali Krishnan ( Muralik) 3-July-1995 Removed non-internet info + trims
  12. Sophia Chung (sophiac) 09-Oct-1995 Splitted internet services into
  13. access and info services
  14. Murali Krishnan ( Muralik) 20-Feb-1996 Enabled to run on NT Workstation
  15. --*/
  16. //
  17. // INCLUDES
  18. //
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #include <windows.h>
  23. #include <winsvc.h> // Service control APIs
  24. #include <rpc.h>
  25. #include <stdlib.h>
  26. #include <inetsvcs.h>
  27. #include <iis64.h>
  28. #include "inetimsg.h"
  29. #include "iisadmin.hxx"
  30. #include <pwsdata.hxx>
  31. #include <objbase.h>
  32. #define SERVICES_KEY \
  33. "System\\CurrentControlSet\\Services"
  34. #define INETINFO_PARAM_KEY \
  35. "System\\CurrentControlSet\\Services\\InetInfo\\Parameters"
  36. #define DISPATCH_ENTRIES_KEY "DispatchEntries"
  37. #define DLL_PATH_NAME_VALUE "IISDllPath"
  38. #define PRELOAD_DLLS_VALUE "PreloadDlls"
  39. //
  40. // Modifications to this name should also be made in tsunami.hxx
  41. //
  42. #define INETA_W3ONLY_NO_AUTH TEXT("W3OnlyNoAuth")
  43. //
  44. // Functions used to start/stop the RPC server
  45. //
  46. typedef DWORD ( *PFN_INETINFO_START_RPC_SERVER) ( VOID );
  47. typedef DWORD ( *PFN_INETINFO_STOP_RPC_SERVER) ( VOID );
  48. //
  49. // Local function used by the above to load and invoke a service DLL.
  50. //
  51. VOID
  52. InetinfoStartService (
  53. IN DWORD argc,
  54. IN LPSTR argv[]
  55. );
  56. VOID
  57. StartDispatchTable(
  58. VOID
  59. );
  60. VOID
  61. W95RegisterService(
  62. VOID
  63. );
  64. BOOL
  65. StartMessageThread(
  66. VOID
  67. );
  68. LRESULT
  69. CALLBACK
  70. MessageProc(
  71. HWND hWnd,
  72. UINT message,
  73. WPARAM wParam,
  74. LPARAM lParam
  75. );
  76. //
  77. // Functions used to preload dlls into the inetinfo process
  78. //
  79. BOOL
  80. LoadPreloadDlls(
  81. HMODULE * * ppPreloadDllHandles
  82. );
  83. VOID
  84. UnloadPreloadDlls(
  85. HMODULE * * ppPreloadDllHandles
  86. );
  87. //
  88. // Used if the services Dll or entry point can't be found
  89. //
  90. VOID
  91. AbortService(
  92. LPSTR ServiceName,
  93. DWORD Error
  94. );
  95. //
  96. // Dispatch table for all services. Passed to NetServiceStartCtrlDispatcher.
  97. //
  98. // Add new service entries here and in the DLL name list.
  99. // Also add an entry in the following table InetServiceDllTable
  100. //
  101. SERVICE_TABLE_ENTRY InetServiceDispatchTable[] = {
  102. { "GopherSvc", InetinfoStartService },
  103. { "MSFtpSvc", InetinfoStartService },
  104. { "W3Svc", InetinfoStartService },
  105. { "IISADMIN", InetinfoStartService },
  106. { NULL, NULL },
  107. };
  108. SERVICE_TABLE_ENTRY W3ServiceDispatchTable[] = {
  109. { "W3Svc", InetinfoStartService },
  110. { NULL, NULL },
  111. };
  112. //
  113. // DLL names for all services.
  114. // (should correspond exactly with above InetServiceDispatchTable)
  115. //
  116. struct SERVICE_DLL_TABLE_ENTRY {
  117. LPSTR lpServiceName;
  118. LPSTR lpDllName;
  119. CRITICAL_SECTION csLoadLock;
  120. } InetServiceDllTable[] = {
  121. { "GopherSvc", "gopherd.dll" },
  122. { "MSFtpSvc", "ftpsvc2.dll" },
  123. { "W3Svc", "w3svc.dll" },
  124. { "IISADMIN", "iisadmin.dll" },
  125. { NULL, NULL }
  126. };
  127. //
  128. // Global parameter data passed to each service.
  129. //
  130. TCPSVCS_GLOBAL_DATA InetinfoGlobalData;
  131. #include <initguid.h>
  132. DEFINE_GUID(IisExeGuid,
  133. 0x784d8901, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
  134. #ifndef _NO_TRACING_
  135. #include "pudebug.h"
  136. DECLARE_DEBUG_PRINTS_OBJECT()
  137. #endif
  138. //
  139. // A global variable that remembers that we'll refuse to start.
  140. //
  141. BOOL RefuseStartup = FALSE;
  142. BOOL g_fRunAsExe = FALSE;
  143. BOOL g_fWindows95 = FALSE;
  144. BOOL g_fW3svcNoAuth = FALSE;
  145. BOOL g_fOleInitialized = TRUE;
  146. DWORD __cdecl
  147. main(
  148. IN DWORD argc,
  149. IN LPSTR argv[]
  150. )
  151. /*++
  152. Routine Description:
  153. This is the main routine for the LANMan services. It starts up the
  154. main thread that is going to handle the control requests from the
  155. service controller.
  156. It basically sets up the ControlDispatcher and, on return, exits
  157. from this main thread. The call to NetServiceStartCtrlDispatcher
  158. does not return until all services have terminated, and this process
  159. can go away.
  160. The ControlDispatcher thread will start/stop/pause/continue any
  161. services. If a service is to be started, it will create a thread
  162. and then call the main routine of that service. The "main routine"
  163. for each service is actually an intermediate function implemented in
  164. this module that loads the DLL containing the server being started
  165. and calls its entry point.
  166. Arguments:
  167. None.
  168. Return Value:
  169. None.
  170. --*/
  171. {
  172. HMODULE dllHandle = NULL;
  173. HINSTANCE hRpcRef = NULL;
  174. HMODULE * pPreloadDllHandles = NULL;
  175. DWORD dwIndex;
  176. struct SERVICE_DLL_TABLE_ENTRY * pEntry;
  177. DWORD err = ERROR_SUCCESS;
  178. //
  179. // Initialize OLE
  180. //
  181. #ifndef _NO_TRACING_
  182. HRESULT hr;
  183. CREATE_DEBUG_PRINT_OBJECT("Inetinfo.exe", IisExeGuid);
  184. CREATE_INITIALIZE_DEBUG();
  185. hr = CoInitializeEx(NULL, COINIT_MULTITHREADED );
  186. #else
  187. HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED );
  188. #endif
  189. if ( FAILED(hr)) {
  190. if ( hr != E_INVALIDARG ) {
  191. IIS_PRINTF((buff,"CoInitialize failed with %x\n",hr));
  192. g_fOleInitialized = FALSE;
  193. }
  194. }
  195. //
  196. // are we chicago?
  197. //
  198. {
  199. OSVERSIONINFO osInfo;
  200. osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  201. if ( GetVersionEx( &osInfo ) ) {
  202. g_fWindows95 = (osInfo.dwPlatformId != VER_PLATFORM_WIN32_NT);
  203. }
  204. }
  205. //
  206. // Initialize Global Data.
  207. //
  208. if ( !g_fWindows95 ) {
  209. //
  210. // Use the rpcref library, so that multiple services can
  211. // independently "start" the rpc server
  212. //
  213. hRpcRef = LoadLibrary("rpcref.dll");
  214. if ( hRpcRef != NULL )
  215. {
  216. InetinfoGlobalData.StartRpcServerListen =
  217. (PFN_INETINFO_START_RPC_SERVER)
  218. GetProcAddress(hRpcRef,"InetinfoStartRpcServerListen");
  219. InetinfoGlobalData.StopRpcServerListen =
  220. (PFN_INETINFO_STOP_RPC_SERVER)
  221. GetProcAddress(hRpcRef,"InetinfoStopRpcServerListen");
  222. }
  223. else
  224. {
  225. IIS_PRINTF((buff,
  226. "Error %d loading rpcref.dll\n",
  227. GetLastError() ));
  228. #ifndef _NO_TRACING_
  229. DELETE_DEBUG_PRINT_OBJECT()
  230. DELETE_INITIALIZE_DEBUG()
  231. #endif
  232. return GetLastError();
  233. }
  234. }
  235. //
  236. // Initialize service entry locks
  237. //
  238. for ( dwIndex = 0 ; ; dwIndex++ )
  239. {
  240. pEntry = &( InetServiceDllTable[ dwIndex ] );
  241. if ( pEntry->lpServiceName == NULL )
  242. {
  243. break;
  244. }
  245. InitializeCriticalSection( &( pEntry->csLoadLock ) );
  246. }
  247. //
  248. // Disable hard-error popups.
  249. //
  250. SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX );
  251. //
  252. // Preload Dlls specified in the registry
  253. //
  254. if ( !LoadPreloadDlls( &pPreloadDllHandles ) )
  255. {
  256. IIS_PRINTF(( buff, "Error pre-loading DLLs\n" ));
  257. }
  258. if ( (argc > 2) && !_stricmp( argv[1], "-e" ))
  259. {
  260. PCHAR pszName = argv[2];
  261. HANDLE hAsExeEvent;
  262. HANDLE hStartW3svc = NULL;
  263. PIISADMIN_SERVICE_DLL_EXEENTRY IisAdminExeEntry = NULL;
  264. PIISADMIN_SERVICE_DLL_EXEEXIT IisAdminExeExit = NULL;
  265. BOOL IsIisAdmin = TRUE;
  266. //
  267. // Create the start w3svc event for win95. This is used
  268. // to inform the 1st instance that w3 need to start the web
  269. // server.
  270. //
  271. if ( g_fWindows95 ) {
  272. hStartW3svc = CreateEvent( NULL,
  273. FALSE,
  274. FALSE,
  275. "inetinfo_start_w3svc");
  276. err = GetLastError();
  277. if ( hStartW3svc == NULL ) {
  278. IIS_PRINTF((buff,"Cannot create %s event. err %d\n",
  279. "inetinfo_start_w3svc", err));
  280. goto Finished;
  281. }
  282. if ( err != ERROR_SUCCESS ) {
  283. IIS_PRINTF((buff,
  284. "Error %d in CreateEvent of start w3svc\n", err));
  285. } else {
  286. //
  287. // Register outself as a fake service
  288. //
  289. W95RegisterService( );
  290. //
  291. // Start the thread that will contain the windows message loop
  292. //
  293. if ( !StartMessageThread( ) ) {
  294. err = GetLastError();
  295. CloseHandle(hStartW3svc);
  296. goto Finished;
  297. }
  298. }
  299. }
  300. //
  301. // Create a named event. The common internet services code attempts
  302. // to create a semaphore with the same name, if it fails then the
  303. // service is being run as an exe
  304. //
  305. g_fRunAsExe = TRUE;
  306. hAsExeEvent = CreateEvent( NULL,
  307. FALSE,
  308. FALSE,
  309. IIS_AS_EXE_OBJECT_NAME);
  310. err = GetLastError();
  311. if ( hAsExeEvent == NULL ) {
  312. IIS_PRINTF((buff,"Cannot create %s event. err %d\n",
  313. IIS_AS_EXE_OBJECT_NAME, err));
  314. goto Finished;
  315. }
  316. if ( err != ERROR_SUCCESS ) {
  317. CloseHandle(hAsExeEvent);
  318. IIS_PRINTF((buff,
  319. "Error %d in CreateEvent[%s]\n",
  320. err, IIS_AS_EXE_OBJECT_NAME));
  321. if ( (_stricmp("W3Svc", pszName) == 0) && g_fWindows95 ) {
  322. //
  323. // someone's trying to start the w3svc. Set the start w3svc
  324. // event.
  325. //
  326. IIS_PRINTF((buff,"Setting start w3svc event\n"));
  327. SetEvent(hStartW3svc);
  328. CloseHandle(hStartW3svc);
  329. err = 0;
  330. }
  331. goto Finished;
  332. }
  333. //
  334. // All services are dependent on IISADMIN
  335. // so if it's not IISADMIN or we are running win95, call it
  336. //
  337. if ( (_stricmp(IISADMIN_NAME, pszName) != 0) || g_fWindows95 ) {
  338. //
  339. // Not IISADMIN
  340. //
  341. IsIisAdmin = FALSE;
  342. dllHandle = LoadLibrary(IISADMIN_NAME);
  343. if ( dllHandle == NULL ) {
  344. err = GetLastError();
  345. IIS_PRINTF((buff,
  346. "Inetinfo: Failed to load DLL %s: %ld\n",
  347. IISADMIN_NAME, err));
  348. goto Finished;
  349. }
  350. //
  351. // Get the address of the service's main entry point. This
  352. // entry point has a well-known name.
  353. //
  354. IisAdminExeEntry = (PIISADMIN_SERVICE_DLL_EXEENTRY)GetProcAddress(
  355. dllHandle,
  356. IISADMIN_EXEENTRY_STRING
  357. );
  358. if (IisAdminExeEntry == NULL ) {
  359. err = GetLastError();
  360. IIS_PRINTF((buff,
  361. "Inetinfo: Can't find entry %s in DLL %s: %ld\n",
  362. IISADMIN_EXEENTRY_STRING, IISADMIN_NAME, err));
  363. goto Finished;
  364. }
  365. IisAdminExeExit = (PIISADMIN_SERVICE_DLL_EXEEXIT)GetProcAddress(
  366. dllHandle,
  367. IISADMIN_EXEEXIT_STRING
  368. );
  369. if (IisAdminExeExit == NULL ) {
  370. err = GetLastError();
  371. IIS_PRINTF((buff,
  372. "Inetinfo: Can't find entry %s in DLL %s: %ld\n",
  373. IISADMIN_EXEEXIT_STRING, IISADMIN_NAME, err);
  374. );
  375. goto Finished;
  376. }
  377. if (!IisAdminExeEntry( TRUE, FALSE, TRUE)) {
  378. IIS_PRINTF((buff,"IISadmin init failed\n"));
  379. err = 1;
  380. goto Finished;
  381. }
  382. }
  383. //
  384. // Offset argv so that the first entry points to the dll name to
  385. // load
  386. //
  387. if (g_fWindows95) {
  388. BOOL bStartW3svc = FALSE;
  389. HANDLE hEvents[2];
  390. hEvents[0] = hAsExeEvent;
  391. hEvents[1] = hStartW3svc;
  392. if (_stricmp(IISADMIN_NAME, pszName) != 0) {
  393. bStartW3svc = TRUE;
  394. }
  395. while (TRUE) {
  396. if (bStartW3svc) {
  397. PCHAR tmpArgv[1];
  398. tmpArgv[0] = "W3Svc";
  399. IIS_PRINTF((buff,"Starting W3svc\n"));
  400. InetinfoStartService( 1, &tmpArgv[0] );
  401. IIS_PRINTF((buff,"Exiting W3svc\n"));
  402. }
  403. err = MsgWaitForMultipleObjects(
  404. 2,
  405. hEvents,
  406. FALSE, // don't wait all
  407. INFINITE,
  408. 0); // windows event mask
  409. if ( err == (WAIT_OBJECT_0+1) ) {
  410. bStartW3svc = TRUE;
  411. } else {
  412. IIS_PRINTF((buff,"Exiting IISAdmin\n"));
  413. break;
  414. }
  415. }
  416. }
  417. else {
  418. IIS_PRINTF((buff,"Starting %s\n", pszName));
  419. InetinfoStartService( 1, &argv[2] );
  420. }
  421. if (!IsIisAdmin) {
  422. IisAdminExeExit();
  423. }
  424. if ( hStartW3svc != NULL ) {
  425. CloseHandle( hStartW3svc );
  426. }
  427. CloseHandle( hAsExeEvent );
  428. } else {
  429. if ( !g_fWindows95 ) {
  430. StartDispatchTable( );
  431. } else {
  432. IIS_PRINTF((buff,"No arguments supplied. Exiting\n"));
  433. }
  434. }
  435. Finished:
  436. //
  437. // Unload pre-loaded Dlls
  438. //
  439. UnloadPreloadDlls( &pPreloadDllHandles );
  440. //
  441. // Cleanup OLE
  442. //
  443. if ( g_fOleInitialized ) {
  444. CoUninitialize();
  445. g_fOleInitialized = FALSE;
  446. }
  447. //
  448. // Free the admin service dll
  449. // Note: this must happen after CoUninitialize or it causes
  450. // a crash on Win95
  451. //
  452. if (dllHandle != NULL) {
  453. FreeLibrary( dllHandle );
  454. }
  455. if ( hRpcRef != NULL ) {
  456. FreeLibrary( hRpcRef );
  457. hRpcRef = NULL;
  458. }
  459. //
  460. // Terminate service entry locks
  461. //
  462. for ( dwIndex = 0 ; ; dwIndex++ )
  463. {
  464. pEntry = &( InetServiceDllTable[ dwIndex ] );
  465. if ( pEntry->lpServiceName == NULL )
  466. {
  467. break;
  468. }
  469. DeleteCriticalSection( &( pEntry->csLoadLock ) );
  470. }
  471. IIS_PRINTF((buff,"Exiting inetinfo.exe\n"));
  472. #ifndef _NO_TRACING_
  473. DELETE_DEBUG_PRINT_OBJECT()
  474. DELETE_INITIALIZE_DEBUG()
  475. #endif
  476. return err;
  477. } // main
  478. DWORD
  479. FindEntryFromDispatchTable(
  480. IN LPSTR pszService
  481. )
  482. {
  483. SERVICE_TABLE_ENTRY * pService;
  484. for(pService = InetServiceDispatchTable;
  485. pService->lpServiceName != NULL;
  486. pService++) {
  487. if ( !lstrcmpi( pService->lpServiceName, pszService)) {
  488. return DIFF(pService - InetServiceDispatchTable);
  489. }
  490. }
  491. //
  492. // We have not found the entry. Set error and return
  493. //
  494. SetLastError( ERROR_INVALID_PARAMETER);
  495. return 0xFFFFFFFF;
  496. } // FindEntryFromDispatchTable()
  497. BOOL
  498. GetDLLNameForDispatchEntryService(
  499. IN LPSTR pszService,
  500. IN OUT CHAR * pszDllName,
  501. IN DWORD cbDllName
  502. )
  503. /*++
  504. Routine Description:
  505. If the image name is not in the static dispatch table, then it might be
  506. in the registry under the value "IISDllName" under the key for the
  507. service. This routine reads the registry for the setting (if existing).
  508. This code allows the exchange folks to install their service DLLs in a
  509. location other than "%systemroot%\inetsrv".
  510. Arguments:
  511. pszService - Service name
  512. pszDllName - Filled with image name
  513. cbDllName - Size of buffer pointed to by pszDllName
  514. Return Value:
  515. TRUE if successful, else FALSE.
  516. --*/
  517. {
  518. HKEY hkey = NULL, hkeyService = NULL;
  519. DWORD err;
  520. DWORD valType;
  521. DWORD nBytes;
  522. BOOL ret = FALSE;
  523. err = RegOpenKeyEx(
  524. HKEY_LOCAL_MACHINE,
  525. SERVICES_KEY,
  526. 0,
  527. KEY_READ,
  528. &hkeyService
  529. );
  530. if (err != ERROR_SUCCESS) {
  531. IIS_PRINTF((buff,
  532. "Inetinfo: Failed to open service key: %ld\n", err));
  533. goto Cleanup;
  534. }
  535. err = RegOpenKeyEx(
  536. hkeyService,
  537. pszService,
  538. 0,
  539. KEY_READ,
  540. &hkey
  541. );
  542. if (err != ERROR_SUCCESS) {
  543. IIS_PRINTF((buff,
  544. "Inetinfo: Failed to open service key for %s: %ld\n",
  545. pszService, err));
  546. goto Cleanup;
  547. }
  548. nBytes = cbDllName;
  549. err = RegQueryValueEx(
  550. hkey,
  551. DLL_PATH_NAME_VALUE,
  552. NULL,
  553. &valType,
  554. (LPBYTE)pszDllName,
  555. &nBytes);
  556. if ( err == ERROR_SUCCESS &&
  557. ( valType == REG_SZ || valType == REG_EXPAND_SZ ) )
  558. {
  559. IIS_PRINTF((buff,
  560. "Service Dll is %s", pszDllName));
  561. ret = TRUE;
  562. }
  563. Cleanup:
  564. if (hkey != NULL) {
  565. RegCloseKey( hkey );
  566. }
  567. if (hkeyService != NULL) {
  568. RegCloseKey( hkeyService );
  569. }
  570. return ret;
  571. }
  572. VOID
  573. InetinfoStartService (
  574. IN DWORD argc,
  575. IN LPSTR argv[]
  576. )
  577. /*++
  578. Routine Description:
  579. This routine loads the DLL that contains a service and calls its
  580. main routine.
  581. Arguments:
  582. DllName - name of the DLL
  583. argc, argv - Passed through to the service
  584. Return Value:
  585. None.
  586. --*/
  587. {
  588. HMODULE dllHandle;
  589. PINETSVCS_SERVICE_DLL_ENTRY serviceEntry;
  590. BOOL ok;
  591. DWORD Error;
  592. CHAR tmpDllName[MAX_PATH+1];
  593. LPSTR DllName;
  594. DWORD dwIndex;
  595. //
  596. // If we need to refuse to start services, do so.
  597. //
  598. if ( RefuseStartup ) {
  599. AbortService(argv[0], ERROR_INVALID_PARAMETER);
  600. return;
  601. }
  602. //
  603. // Find the Dll Name for requested service
  604. //
  605. dwIndex = FindEntryFromDispatchTable( argv[0] );
  606. if ( dwIndex == 0xFFFFFFFF ) {
  607. if ( GetDLLNameForDispatchEntryService( argv[0],
  608. tmpDllName,
  609. sizeof( tmpDllName ) ) ) {
  610. IIS_PRINTF((buff,
  611. "Service %s has path set in registry. Assuming %s\n",
  612. argv[0],
  613. tmpDllName));
  614. DllName = tmpDllName;
  615. }
  616. else if ( strlen(argv[0]) < (MAX_PATH-4) ) {
  617. strcpy(tmpDllName, argv[0]);
  618. strcat(tmpDllName, ".dll");
  619. IIS_PRINTF((buff,"Service %s not on primary list. Assuming %s\n",
  620. argv[0], tmpDllName));
  621. DllName = tmpDllName;
  622. } else {
  623. Error = ERROR_INSUFFICIENT_BUFFER;
  624. IIS_PRINTF((buff,
  625. "Inetinfo: Failed To Find Dll For %s : %ld\n",
  626. argv[0], Error));
  627. AbortService( argv[0], Error);
  628. return;
  629. }
  630. }
  631. else
  632. {
  633. DllName = InetServiceDllTable[ dwIndex ].lpDllName;
  634. }
  635. //
  636. // Load the DLL that contains the service.
  637. //
  638. if ( dwIndex != 0xFFFFFFFF )
  639. {
  640. EnterCriticalSection( &( InetServiceDllTable[ dwIndex ].csLoadLock ) );
  641. }
  642. dllHandle = LoadLibraryEx( DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
  643. if ( dllHandle == NULL ) {
  644. Error = GetLastError();
  645. IIS_PRINTF((buff,
  646. "Inetinfo: Failed to load DLL %s: %ld\n",
  647. DllName, Error));
  648. AbortService(argv[0], Error);
  649. LeaveCriticalSection( &( InetServiceDllTable[ dwIndex ].csLoadLock ) );
  650. return;
  651. }
  652. //
  653. // Get the address of the service's main entry point. This
  654. // entry point has a well-known name.
  655. //
  656. serviceEntry = (PINETSVCS_SERVICE_DLL_ENTRY)GetProcAddress(
  657. dllHandle,
  658. INETSVCS_ENTRY_POINT_STRING
  659. );
  660. if ( serviceEntry == NULL ) {
  661. Error = GetLastError();
  662. IIS_PRINTF((buff,
  663. "Inetinfo: Can't find entry %s in DLL %s: %ld\n",
  664. INETSVCS_ENTRY_POINT_STRING, DllName, Error));
  665. AbortService(argv[0], Error);
  666. } else {
  667. //
  668. // Call the service's main entry point. This call doesn't return
  669. // until the service exits.
  670. //
  671. serviceEntry( argc, argv, &InetinfoGlobalData );
  672. }
  673. if ( dwIndex != 0xFFFFFFFF )
  674. {
  675. LeaveCriticalSection( &( InetServiceDllTable[ dwIndex ].csLoadLock ) );
  676. }
  677. //
  678. // wait for the control dispatcher routine to return. This
  679. // works around a problem where simptcp was crashing because the
  680. // FreeLibrary() was happenning before the control routine returned.
  681. //
  682. Sleep( 500 );
  683. //
  684. // Unload the DLL.
  685. //
  686. ok = FreeLibrary( dllHandle );
  687. if ( !ok ) {
  688. IIS_PRINTF((buff,
  689. "INETSVCS: Can't unload DLL %s: %ld\n",
  690. DllName, GetLastError()));
  691. }
  692. return;
  693. } // InetinfoStartService
  694. VOID
  695. DummyCtrlHandler(
  696. DWORD Opcode
  697. )
  698. /*++
  699. Routine Description:
  700. This is a dummy control handler which is only used if we can't load
  701. a services DLL entry point. Then we need this so we can send the
  702. status back to the service controller saying we are stopped, and why.
  703. Arguments:
  704. OpCode - Ignored
  705. Return Value:
  706. None.
  707. --*/
  708. {
  709. return;
  710. } // DummyCtrlHandler
  711. VOID
  712. AbortService(
  713. LPSTR ServiceName,
  714. DWORD Error)
  715. /*++
  716. Routine Description:
  717. This is called if we can't load the entry point for a service. It
  718. gets a handle so it can call SetServiceStatus saying we are stopped
  719. and why.
  720. Arguments:
  721. ServiceName - the name of the service that couldn't be started
  722. Error - the reason it couldn't be started
  723. Return Value:
  724. None.
  725. --*/
  726. {
  727. SERVICE_STATUS_HANDLE GenericServiceStatusHandle;
  728. SERVICE_STATUS GenericServiceStatus;
  729. if (!g_fRunAsExe) {
  730. GenericServiceStatus.dwServiceType = SERVICE_WIN32;
  731. GenericServiceStatus.dwCurrentState = SERVICE_STOPPED;
  732. GenericServiceStatus.dwControlsAccepted = SERVICE_CONTROL_STOP;
  733. GenericServiceStatus.dwCheckPoint = 0;
  734. GenericServiceStatus.dwWaitHint = 0;
  735. GenericServiceStatus.dwWin32ExitCode = Error;
  736. GenericServiceStatus.dwServiceSpecificExitCode = 0;
  737. GenericServiceStatusHandle = RegisterServiceCtrlHandler(
  738. ServiceName,
  739. DummyCtrlHandler);
  740. if (GenericServiceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
  741. IIS_PRINTF((buff,
  742. "[Inetinfo]RegisterServiceCtrlHandler[%s] failed %d\n",
  743. ServiceName, GetLastError()));
  744. } else if (!SetServiceStatus (GenericServiceStatusHandle,
  745. &GenericServiceStatus)) {
  746. IIS_PRINTF((buff,
  747. "[Inetinfo]SetServiceStatus[%s] error %ld\n",
  748. ServiceName, GetLastError()));
  749. }
  750. }
  751. return;
  752. }
  753. VOID
  754. StartDispatchTable(
  755. VOID
  756. )
  757. /*++
  758. Routine Description:
  759. Returns the dispatch table to use. It uses the default if
  760. the DispatchEntries value key does not exist
  761. Arguments:
  762. None.
  763. Return Value:
  764. Pointer to the dispatch table to use.
  765. --*/
  766. {
  767. LPSERVICE_TABLE_ENTRY dispatchTable = InetServiceDispatchTable;
  768. LPSERVICE_TABLE_ENTRY tableEntry = NULL;
  769. LPBYTE buffer;
  770. HKEY hKey = NULL;
  771. DWORD i;
  772. DWORD err;
  773. DWORD valType;
  774. DWORD nBytes = 0;
  775. DWORD nEntries = 0;
  776. PCHAR entry;
  777. BOOL IsIisAdmin = TRUE;
  778. HMODULE dllHandle;
  779. PIISADMIN_SERVICE_DLL_EXEENTRY IisAdminExeEntry = NULL;
  780. PIISADMIN_SERVICE_DLL_EXEEXIT IisAdminExeExit = NULL;
  781. DWORD dwW3svcNoAuth;
  782. //
  783. // See if need to augment the dispatcher table
  784. //
  785. err = RegOpenKeyEx(
  786. HKEY_LOCAL_MACHINE,
  787. INETINFO_PARAM_KEY,
  788. 0,
  789. KEY_READ,
  790. &hKey
  791. );
  792. if ( err != ERROR_SUCCESS ) {
  793. hKey = NULL;
  794. goto start_dispatch;
  795. }
  796. //
  797. // See if mono service
  798. //
  799. nBytes = sizeof(dwW3svcNoAuth);
  800. if ( RegQueryValueEx(
  801. hKey,
  802. INETA_W3ONLY_NO_AUTH,
  803. NULL,
  804. &valType,
  805. (LPBYTE)&dwW3svcNoAuth,
  806. &nBytes
  807. ) == ERROR_SUCCESS && valType == REG_DWORD ) {
  808. g_fW3svcNoAuth = !!dwW3svcNoAuth;
  809. }
  810. if ( g_fW3svcNoAuth ) {
  811. dllHandle = LoadLibrary(IISADMIN_NAME);
  812. if ( dllHandle == NULL ) {
  813. err = GetLastError();
  814. IIS_PRINTF((buff,
  815. "Inetinfo: Failed to load DLL %s: %ld\n",
  816. IISADMIN_NAME, err));
  817. return;
  818. }
  819. //
  820. // Get the address of the service's main entry point. This
  821. // entry point has a well-known name.
  822. //
  823. IisAdminExeEntry = (PIISADMIN_SERVICE_DLL_EXEENTRY)GetProcAddress(
  824. dllHandle,
  825. IISADMIN_EXEENTRY_STRING
  826. );
  827. if (IisAdminExeEntry == NULL ) {
  828. err = GetLastError();
  829. IIS_PRINTF((buff,
  830. "Inetinfo: Can't find entry %s in DLL %s: %ld\n",
  831. IISADMIN_EXEENTRY_STRING, IISADMIN_NAME, err));
  832. return;
  833. }
  834. IisAdminExeExit = (PIISADMIN_SERVICE_DLL_EXEEXIT)GetProcAddress(
  835. dllHandle,
  836. IISADMIN_EXEEXIT_STRING
  837. );
  838. if (IisAdminExeExit == NULL ) {
  839. err = GetLastError();
  840. IIS_PRINTF((buff,
  841. "Inetinfo: Can't find entry %s in DLL %s: %ld\n",
  842. IISADMIN_EXEEXIT_STRING, IISADMIN_NAME, err);
  843. );
  844. return;
  845. }
  846. if (!IisAdminExeEntry( FALSE, TRUE, TRUE )) {
  847. IIS_PRINTF((buff,"IISadmin init failed\n"));
  848. return;
  849. }
  850. IsIisAdmin = FALSE;
  851. dispatchTable = W3ServiceDispatchTable;
  852. goto start_dispatch;
  853. }
  854. //
  855. // See if the value exists and get the size of the buffer needed
  856. //
  857. nBytes = 0;
  858. err = RegQueryValueEx(
  859. hKey,
  860. DISPATCH_ENTRIES_KEY,
  861. NULL,
  862. &valType,
  863. NULL,
  864. &nBytes
  865. );
  866. if ( (err != ERROR_SUCCESS) || (nBytes <= sizeof(CHAR)) ) {
  867. goto start_dispatch;
  868. }
  869. //
  870. // Allocate nBytes to query the buffer
  871. //
  872. buffer = (LPBYTE)LocalAlloc(0, nBytes);
  873. if ( buffer == NULL ) {
  874. goto start_dispatch;
  875. }
  876. //
  877. // Get the values
  878. //
  879. err = RegQueryValueEx(
  880. hKey,
  881. DISPATCH_ENTRIES_KEY,
  882. NULL,
  883. &valType,
  884. buffer,
  885. &nBytes
  886. );
  887. if ( (err != ERROR_SUCCESS) || (valType != REG_MULTI_SZ) ) {
  888. LocalFree(buffer);
  889. goto start_dispatch;
  890. }
  891. //
  892. // Walk the list and see how many entries we have. Remove the list
  893. // terminator from the byte count
  894. //
  895. nBytes -= sizeof(CHAR);
  896. for ( i = 0, entry = (PCHAR)buffer;
  897. i < nBytes;
  898. i += sizeof(CHAR) ) {
  899. if ( *entry++ == '\0' ) {
  900. nEntries++;
  901. }
  902. }
  903. if ( nEntries == 0 ) {
  904. LocalFree(buffer);
  905. goto start_dispatch;
  906. }
  907. //
  908. // Add the number of entries in the default list (including the NULL entry)
  909. //
  910. nEntries += sizeof(InetServiceDispatchTable) / sizeof(SERVICE_TABLE_ENTRY);
  911. //
  912. // Now we need to allocate the new dispatch table
  913. //
  914. tableEntry = (LPSERVICE_TABLE_ENTRY)
  915. LocalAlloc(0, nEntries * sizeof(SERVICE_TABLE_ENTRY));
  916. if ( tableEntry == NULL ) {
  917. LocalFree(buffer);
  918. goto start_dispatch;
  919. }
  920. //
  921. // set the dispatch table pointer to the new table
  922. //
  923. dispatchTable = tableEntry;
  924. //
  925. // Populate the table starting with the defaults
  926. //
  927. for (i=0; InetServiceDispatchTable[i].lpServiceName != NULL; i++ ) {
  928. tableEntry->lpServiceName =
  929. InetServiceDispatchTable[i].lpServiceName;
  930. tableEntry->lpServiceProc =
  931. InetServiceDispatchTable[i].lpServiceProc;
  932. tableEntry++;
  933. }
  934. //
  935. // Now let's add the ones specified in the registry
  936. //
  937. entry = (PCHAR)buffer;
  938. tableEntry->lpServiceName = entry;
  939. tableEntry->lpServiceProc = InetinfoStartService;
  940. tableEntry++;
  941. //
  942. // Skip the first char and the last NULL terminator.
  943. // This is needed because we already added the first one.
  944. //
  945. for ( i = 2*sizeof(CHAR); i < nBytes; i += sizeof(CHAR) ) {
  946. if ( *entry++ == '\0' ) {
  947. tableEntry->lpServiceName = entry;
  948. tableEntry->lpServiceProc = InetinfoStartService;
  949. tableEntry++;
  950. }
  951. }
  952. //
  953. // setup sentinel entry
  954. //
  955. tableEntry->lpServiceName = NULL;
  956. tableEntry->lpServiceProc = NULL;
  957. start_dispatch:
  958. if ( hKey != NULL ) {
  959. RegCloseKey(hKey);
  960. }
  961. //
  962. // Call StartServiceCtrlDispatcher to set up the control interface.
  963. // The API won't return until all services have been terminated. At that
  964. // point, we just exit.
  965. //
  966. if (! StartServiceCtrlDispatcher (
  967. dispatchTable
  968. )) {
  969. //
  970. // Log an event for failing to start control dispatcher
  971. //
  972. IIS_PRINTF((buff,
  973. "Inetinfo: Failed to start control dispatcher %lu\n",
  974. GetLastError()));
  975. }
  976. //
  977. // free table if allocated
  978. //
  979. if ( dispatchTable != InetServiceDispatchTable &&
  980. dispatchTable != W3ServiceDispatchTable ) {
  981. LocalFree( dispatchTable );
  982. LocalFree( buffer );
  983. }
  984. if (!IsIisAdmin) {
  985. IisAdminExeExit();
  986. FreeLibrary( dllHandle );
  987. }
  988. return;
  989. } // StartDispatchTable
  990. // define the message window class name
  991. #define PWS_WATCHER_WINDOW_CLASS "INETINFO_MESSAGES"
  992. // message thread to get windows95 close message
  993. // most helpful for proper cleanup during shutdown
  994. LRESULT
  995. CALLBACK
  996. MessageProc(
  997. HWND hWnd,
  998. UINT message,
  999. WPARAM wParam,
  1000. LPARAM lParam
  1001. )
  1002. {
  1003. HANDLE hEvent;
  1004. DWORD i;
  1005. switch( message ) {
  1006. case WM_ENDSESSION:
  1007. if ( lParam == 0 ) {
  1008. IIS_PRINTF((buff,"Got EndSession message with shutdown\n"));
  1009. } else {
  1010. IIS_PRINTF((buff,
  1011. "Got EndSession message with Logoff[%x]\n",message));
  1012. break;
  1013. }
  1014. case WM_CLOSE:
  1015. //
  1016. // shut down w3svc
  1017. //
  1018. hEvent = CreateEvent(NULL, TRUE, FALSE, PWS_SHUTDOWN_EVENT);
  1019. if ( hEvent ) {
  1020. if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
  1021. SetEvent( hEvent );
  1022. }
  1023. CloseHandle(hEvent);
  1024. }
  1025. //
  1026. // shutdown code courtesy johnsona
  1027. // shut down iisadmin
  1028. //
  1029. hEvent = CreateEvent(NULL, TRUE, FALSE, IIS_AS_EXE_OBJECT_NAME);
  1030. if ( hEvent ) {
  1031. if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
  1032. SetEvent( hEvent );
  1033. }
  1034. CloseHandle(hEvent);
  1035. for (i=0; i < 20; i++) {
  1036. hEvent = CreateEvent(NULL,
  1037. TRUE,
  1038. FALSE,
  1039. IIS_AS_EXE_OBJECT_NAME);
  1040. if ( hEvent != NULL ) {
  1041. DWORD err = GetLastError();
  1042. CloseHandle(hEvent);
  1043. if ( err == ERROR_ALREADY_EXISTS ) {
  1044. Sleep(500);
  1045. continue;
  1046. }
  1047. }
  1048. }
  1049. break;
  1050. }
  1051. break;
  1052. }
  1053. // do the default processing
  1054. return(DefWindowProc(hWnd, message, wParam, lParam ));
  1055. }
  1056. DWORD
  1057. PwsMessageThread(
  1058. PVOID pv
  1059. )
  1060. {
  1061. WNDCLASS wndclass;
  1062. MSG msg;
  1063. HWND hwnd;
  1064. HINSTANCE hInst = GetModuleHandle( NULL );
  1065. // prepare and register the window class
  1066. wndclass.style = 0;
  1067. wndclass.lpfnWndProc = MessageProc;
  1068. wndclass.cbClsExtra = 0;
  1069. wndclass.cbWndExtra = 0;
  1070. wndclass.hInstance = hInst;
  1071. wndclass.hIcon = NULL;
  1072. wndclass.hCursor = NULL;
  1073. wndclass.hbrBackground = NULL;
  1074. wndclass.lpszMenuName = NULL;
  1075. wndclass.lpszClassName = PWS_WATCHER_WINDOW_CLASS;
  1076. if ( !RegisterClass( &wndclass ) )
  1077. return GetLastError();
  1078. // create the window
  1079. hwnd = CreateWindow(
  1080. PWS_WATCHER_WINDOW_CLASS, // pointer to registered class name
  1081. "", // pointer to window name
  1082. 0, // window style
  1083. 0, // horizontal position of window
  1084. 0, // vertical position of window
  1085. 0, // window width
  1086. 0, // window height
  1087. NULL, // handle to parent or owner window
  1088. NULL, // handle to menu or child-window identifier
  1089. hInst, // handle to application instance
  1090. NULL // pointer to window-creation data
  1091. );
  1092. if ( !hwnd )
  1093. return GetLastError();
  1094. // run the message loop
  1095. while (GetMessage(&msg, NULL, 0, 0))
  1096. {
  1097. TranslateMessage(&msg);
  1098. DispatchMessage( &msg);
  1099. }
  1100. return(1);
  1101. } // PwsMessageThread
  1102. BOOL
  1103. StartMessageThread(
  1104. VOID
  1105. )
  1106. {
  1107. HANDLE hThread;
  1108. DWORD dwThread;
  1109. DWORD err;
  1110. hThread = CreateThread( NULL,
  1111. 0,
  1112. (LPTHREAD_START_ROUTINE) PwsMessageThread,
  1113. NULL,
  1114. 0,
  1115. &dwThread );
  1116. err = GetLastError();
  1117. CloseHandle( hThread );
  1118. if ( hThread == NULL ) {
  1119. IIS_PRINTF((buff,"Error %d on CreateThread\n",err));
  1120. SetLastError(err);
  1121. return FALSE;
  1122. }
  1123. return(TRUE);
  1124. } // StartMessageThread
  1125. typedef
  1126. DWORD
  1127. (*PREGISTER_SERVICE_PROCESS)(
  1128. DWORD dwProcessId,
  1129. DWORD dwServiceType
  1130. );
  1131. #define RSP_UNREGISTER_SERVICE 0x00000000
  1132. #define RSP_SIMPLE_SERVICE 0x00000001
  1133. VOID
  1134. W95RegisterService(
  1135. VOID
  1136. )
  1137. {
  1138. HMODULE dllHandle;
  1139. DWORD err;
  1140. PREGISTER_SERVICE_PROCESS pRegisterService;
  1141. dllHandle = LoadLibrary("kernel32.dll");
  1142. if ( dllHandle == NULL ) {
  1143. err = GetLastError();
  1144. IIS_PRINTF((buff,"Unable to load kernel32.dll. err %d\n",
  1145. err ));
  1146. return;
  1147. }
  1148. pRegisterService = (PREGISTER_SERVICE_PROCESS)GetProcAddress(
  1149. dllHandle,
  1150. "RegisterServiceProcess"
  1151. );
  1152. if ( pRegisterService != NULL ) {
  1153. err = pRegisterService(
  1154. GetCurrentProcessId(),
  1155. RSP_SIMPLE_SERVICE
  1156. );
  1157. IIS_PRINTF((buff,"Register Service returns %d\n",err));
  1158. }
  1159. CloseHandle(dllHandle);
  1160. return;
  1161. } // W95RegisterService
  1162. BOOL
  1163. LoadPreloadDlls(
  1164. HMODULE * * ppPreloadDllHandles
  1165. )
  1166. /*++
  1167. Routine Description:
  1168. Force pre-loading of any DLLs listed in the associated registry key.
  1169. This is to support DLLs that have code which must run before other parts
  1170. of inetinfo start.
  1171. Arguments:
  1172. On input, an (uninitialized) pointer to an array of module handles. This
  1173. array is allocated, filled out, and returned to the caller by this
  1174. function. The caller must eventually call UnloadPreloadDlls on this array
  1175. in order to close the handles and release the memory.
  1176. Return Value:
  1177. TRUE on success, FALSE on failure.
  1178. --*/
  1179. {
  1180. BOOL bSuccess = TRUE;
  1181. HKEY hKey = NULL;
  1182. DWORD err;
  1183. DWORD cbBufferLen;
  1184. DWORD valType;
  1185. LPBYTE pbBuffer = NULL;
  1186. DWORD i;
  1187. PCHAR pszTemp = NULL;
  1188. DWORD nEntries;
  1189. PCHAR pszEntry = NULL;
  1190. DWORD curEntry;
  1191. *ppPreloadDllHandles = NULL;
  1192. err = RegOpenKeyEx(
  1193. HKEY_LOCAL_MACHINE,
  1194. INETINFO_PARAM_KEY,
  1195. 0,
  1196. KEY_QUERY_VALUE,
  1197. &hKey
  1198. );
  1199. if ( err != ERROR_SUCCESS ) {
  1200. // Note: not considered an error if the key is not there
  1201. hKey = NULL;
  1202. goto Exit;
  1203. }
  1204. //
  1205. // See if the value exists and get the size of the buffer needed
  1206. //
  1207. cbBufferLen = 0;
  1208. err = RegQueryValueEx(
  1209. hKey,
  1210. PRELOAD_DLLS_VALUE,
  1211. NULL,
  1212. &valType,
  1213. NULL,
  1214. &cbBufferLen
  1215. );
  1216. //
  1217. // Check for no value or an empty value (double null terminated).
  1218. //
  1219. if ( ( err != ERROR_SUCCESS ) || ( cbBufferLen <= 2 * sizeof(CHAR) ) )
  1220. {
  1221. // Note: not considered an error if the value is not there
  1222. goto Exit;
  1223. }
  1224. //
  1225. // Allocate cbBufferLen in order to fetch the data
  1226. //
  1227. pbBuffer = (LPBYTE)LocalAlloc(
  1228. 0,
  1229. cbBufferLen
  1230. );
  1231. if ( pbBuffer == NULL )
  1232. {
  1233. bSuccess = FALSE;
  1234. goto Exit;
  1235. }
  1236. //
  1237. // Get the values
  1238. //
  1239. err = RegQueryValueEx(
  1240. hKey,
  1241. PRELOAD_DLLS_VALUE,
  1242. NULL,
  1243. &valType,
  1244. pbBuffer,
  1245. &cbBufferLen
  1246. );
  1247. if ( ( err != ERROR_SUCCESS ) || ( valType != REG_MULTI_SZ ) )
  1248. {
  1249. bSuccess = FALSE;
  1250. goto Exit;
  1251. }
  1252. //
  1253. // Walk the list and see how many entries we have. Ignore the list
  1254. // terminator in the last byte of the buffer.
  1255. //
  1256. nEntries = 0;
  1257. pszTemp = (PCHAR)pbBuffer;
  1258. for ( i = 0; i < ( cbBufferLen - sizeof(CHAR) ) ; i += sizeof(CHAR) )
  1259. {
  1260. if ( *pszTemp == '\0' )
  1261. {
  1262. nEntries++;
  1263. }
  1264. pszTemp++;
  1265. }
  1266. //
  1267. // Allocate the array of handles, with room for a sentinel entry
  1268. //
  1269. *ppPreloadDllHandles = (HMODULE *)LocalAlloc(
  1270. 0,
  1271. ( nEntries + 1 ) * sizeof(HMODULE)
  1272. );
  1273. if ( *ppPreloadDllHandles == NULL )
  1274. {
  1275. bSuccess = FALSE;
  1276. goto Exit;
  1277. }
  1278. //
  1279. // Now attempt to load each DLL, and save the handle in the array
  1280. //
  1281. pszTemp = (PCHAR)pbBuffer;
  1282. pszEntry = (PCHAR)pbBuffer;
  1283. curEntry = 0;
  1284. for ( i = 0; i < ( cbBufferLen - sizeof(CHAR) ) ; i += sizeof(CHAR) )
  1285. {
  1286. if ( *pszTemp == '\0' )
  1287. {
  1288. //
  1289. // We've hit the end of one of the SZs in the Multi-SZ;
  1290. // Load the DLL
  1291. //
  1292. (*ppPreloadDllHandles)[curEntry] = LoadLibrary( pszEntry );
  1293. if ( (*ppPreloadDllHandles)[curEntry] == NULL )
  1294. {
  1295. IIS_PRINTF(( buff, "Preloading FAILED for DLL: %s\n", pszEntry ));
  1296. }
  1297. else
  1298. {
  1299. IIS_PRINTF(( buff, "Preloaded DLL: %s\n", pszEntry ));
  1300. // Only move to the next slot if we got a valid handle
  1301. curEntry++;
  1302. }
  1303. // Set the next entry pointer past the current null char
  1304. pszEntry = pszTemp + sizeof(CHAR);
  1305. }
  1306. pszTemp++;
  1307. }
  1308. // Put in a sentinel at the end of the array
  1309. (*ppPreloadDllHandles)[curEntry] = NULL;
  1310. Exit:
  1311. if ( hKey != NULL )
  1312. {
  1313. RegCloseKey( hKey );
  1314. }
  1315. if ( pbBuffer != NULL )
  1316. {
  1317. LocalFree( pbBuffer );
  1318. }
  1319. return bSuccess;
  1320. } // LoadPreloadDlls
  1321. VOID
  1322. UnloadPreloadDlls(
  1323. HMODULE * * ppPreloadDllHandles
  1324. )
  1325. /*++
  1326. Routine Description:
  1327. Unload any DLLs which were preloaded by LoadPreloadDlls.
  1328. Arguments:
  1329. Pointer to an array of module handles. Each handle will be freed,
  1330. and then the memory of the array will be LocalFree()ed by this function.
  1331. Return Value:
  1332. None.
  1333. --*/
  1334. {
  1335. HMODULE * pHandleArray = *ppPreloadDllHandles;
  1336. if ( pHandleArray != NULL )
  1337. {
  1338. IIS_PRINTF(( buff, "Unloading Preloaded DLLs\n" ));
  1339. while ( *pHandleArray != NULL )
  1340. {
  1341. FreeLibrary( *pHandleArray );
  1342. pHandleArray++;
  1343. }
  1344. LocalFree( *ppPreloadDllHandles );
  1345. *ppPreloadDllHandles = NULL;
  1346. }
  1347. return;
  1348. } // UnloadPreloadDlls