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.

603 lines
17 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 1995.
  5. //
  6. // File: dllmain.hxx
  7. //
  8. // Contents: DLL initialization entrypoint and global variables
  9. //
  10. // History: 4-Apr-95 BruceFo Created
  11. //
  12. //--------------------------------------------------------------------------
  13. #include "headers.hxx"
  14. #pragma hdrstop
  15. #include <locale.h>
  16. #include "resource.h"
  17. #include "critsec.hxx"
  18. #include "cache.hxx"
  19. #include "strhash.hxx"
  20. #include "dllmain.hxx"
  21. #include "util.hxx"
  22. //--------------------------------------------------------------------------
  23. // Globals used elsewhere
  24. UINT g_NonOLEDLLRefs = 0;
  25. HINSTANCE g_hInstance = NULL;
  26. BOOL g_fSharingEnabled = FALSE; // until proven otherwise
  27. UINT g_uiMaxUsers = 0; // max number of users based on product type
  28. WCHAR g_szAdminShare[] = L"ADMIN$";
  29. WCHAR g_szIpcShare[] = L"IPC$";
  30. //--------------------------------------------------------------------------
  31. // Globals used only in this file
  32. CRITICAL_SECTION g_csOneTimeInit;
  33. BOOL g_fOneTimeInitDone = FALSE;
  34. // Note: the total wait time is:
  35. // min( (the wait hint / WAIT_FRACTION), MAX_WAIT_PERIOD ) * MAX_WAIT_COUNT
  36. // In the case of the server, with a 30 second hint, about 2 minutes, 30 sec.
  37. #define WAIT_FRACTION 4 // the fraction of the hint to wait
  38. #define MAX_WAIT_COUNT 20 // the maximum number of wait failures to tolerate before quiting
  39. #define MAX_WAIT_PERIOD 15000L // 15 seconds
  40. #define WAIT_TO_BEGIN_GRANULARITY 4000 // 4 seconds
  41. #define MAX_WAIT_TO_BEGIN 90000L // 90 seconds: time to wait before giving up that it will ever start
  42. //--------------------------------------------------------------------------
  43. // Debugging
  44. DECLARE_INFOLEVEL(Sharing)
  45. //--------------------------------------------------------------------------
  46. VOID
  47. InitializeShareCache(
  48. VOID
  49. );
  50. DWORD WINAPI
  51. WaitForServerThread(
  52. IN LPVOID ThreadParameter
  53. );
  54. BOOL
  55. CheckServiceController(
  56. VOID
  57. );
  58. BOOL
  59. ServerConfiguredToStart(
  60. SC_HANDLE hScManager
  61. );
  62. BOOL
  63. WaitForServerToBeginStarting(
  64. SC_HANDLE hService,
  65. LPSERVICE_STATUS pServiceStatus // so we don't need to re-query on successful return
  66. );
  67. //--------------------------------------------------------------------------
  68. //+--------------------------------------------------------------------------
  69. //
  70. // Function: DllMain
  71. //
  72. // Synopsis: Win32 DLL initialization function
  73. //
  74. // Arguments: [hInstance] - Handle to this dll
  75. // [dwReason] - Reason this function was called. Can be
  76. // Process/Thread Attach/Detach.
  77. //
  78. // Returns: BOOL - TRUE if no error. FALSE otherwise
  79. //
  80. // History: 4-Apr-95 BruceFo Created
  81. //
  82. //---------------------------------------------------------------------------
  83. extern "C"
  84. BOOL
  85. DllMain(
  86. HINSTANCE hInstance,
  87. DWORD dwReason,
  88. LPVOID lpReserved
  89. )
  90. {
  91. switch (dwReason)
  92. {
  93. case DLL_PROCESS_ATTACH:
  94. {
  95. #if DBG == 1
  96. InitializeDebugging();
  97. // SharingInfoLevel = DEB_ERROR | DEB_TRACE;
  98. SharingInfoLevel = DEB_ERROR;
  99. SetWin4AssertLevel(ASSRT_BREAK | ASSRT_MESSAGE);
  100. #endif // DBG == 1
  101. appDebugOut((DEB_TRACE, "ntshrui.dll: DllMain enter\n"));
  102. // Disable thread notification from OS
  103. DisableThreadLibraryCalls(hInstance);
  104. g_hInstance = hInstance;
  105. // Be specific about where to get your manifest from - it's in the dll, at the
  106. // default resource ID for the shell, which is 123.
  107. SHFusionInitializeFromModuleID(hInstance, SHFUSION_DEFAULT_RESOURCE_ID);
  108. InitCommonControls(); // get up/down control
  109. // eting removal of MSVCRT setlocale(LC_CTYPE, ""); // set the C runtime library locale, for string operations
  110. InitializeCriticalSection(&g_csOneTimeInit);
  111. break;
  112. }
  113. case DLL_PROCESS_DETACH:
  114. appDebugOut((DEB_TRACE, "ntshrui.dll: DllMain leave\n"));
  115. SHFusionUninitialize();
  116. DeleteCriticalSection(&g_csOneTimeInit);
  117. break;
  118. }
  119. return TRUE;
  120. }
  121. extern HRESULT SharePropDummyFunction();
  122. HRESULT Linkage()
  123. {
  124. return SharePropDummyFunction();
  125. }
  126. //+-------------------------------------------------------------------------
  127. //
  128. // Function: OneTimeInit
  129. //
  130. // Synopsis: Initialization code: check if SMB server is running
  131. //
  132. // History: 21-Apr-95 BruceFo Created
  133. //
  134. // Returns:
  135. // Note: We don't want to do this in the DLL initialization code, so
  136. // we call it for every entrypoint, namely DllGetClassObject,
  137. // DllCanUnloadNow, and IsPathShared.
  138. //
  139. //--------------------------------------------------------------------------
  140. VOID
  141. OneTimeInit(
  142. IN BOOL bDialog // TRUE if for dialog API
  143. )
  144. {
  145. // quick check; no critical section
  146. if (g_fOneTimeInitDone)
  147. {
  148. return;
  149. }
  150. {
  151. CTakeCriticalSection t(&g_csOneTimeInit); // scope it
  152. // Since there wasn't a critical section on the above check, multiple
  153. // threads might have fallen through to the critical section taking,
  154. // and wait. After the one-time initialization is complete, the
  155. // first thread sets g_fOneTimeInitDone to TRUE and leaves the
  156. // critical section. At this point, the other threads will wake up
  157. // here. Do the check again, and return if another thread did the
  158. // initialization.
  159. if (g_fOneTimeInitDone)
  160. {
  161. return;
  162. }
  163. // Now, do the actual initialization
  164. if (!bDialog)
  165. {
  166. // First, determine if the server is running. If not, see if
  167. // we should wait for it. If we wait and it still isn't
  168. // started, then give up.
  169. InitializeShareCache();
  170. }
  171. // if it is a dialog call, then we don't load up the cache because
  172. // that's the first thing the dialog code does.
  173. // Determine the maximum number of users
  174. g_uiMaxUsers = IsWorkstationProduct()
  175. ? MAX_USERS_ON_WORKSTATION
  176. : MAX_USERS_ON_SERVER
  177. ;
  178. g_fOneTimeInitDone = TRUE; // set this *last*
  179. }
  180. }
  181. VOID
  182. InitializeShareCache(
  183. VOID
  184. )
  185. /*++
  186. Routine Description:
  187. This routine initializes the share cache. It determines if the LanMan
  188. server is running. If it is, great.
  189. If it isn't, determine if it is starting. If so, wait for a while. After
  190. a while, if it still hasn't started, then give up and assume it's hung.
  191. If the server isn't starting, then determine if the configuration says it
  192. is going to start (set to autostart). If so, wait to see if it ever goes
  193. into the "start pending" state. Give up after a reasonable period. If it
  194. does go into this state, then wait for it to finish starting, as described
  195. before.
  196. Both LanMan and Service Controller APIs are used in this endeavor.
  197. Arguments:
  198. None
  199. Return Value:
  200. Returns TRUE if the server service has been started; otherwise
  201. returns FALSE. Any API errors return FALSE, and hence assume
  202. the server isn't started or won't start.
  203. --*/
  204. {
  205. appDebugOut((DEB_TRACE, "InitializeShareCache: enter\n"));
  206. g_ShareCache.Refresh(); // sets g_fSharingEnabled
  207. if (g_fSharingEnabled)
  208. {
  209. // well, we've got a cache so no need to start up a thread and wait
  210. // for the server to start
  211. return;
  212. }
  213. // The server isn't currently started. Create a thread that waits for it
  214. // to start, and if it does, refreshes the shell.
  215. DWORD threadId;
  216. HANDLE hThread = CreateThread(
  217. NULL,
  218. 0,
  219. WaitForServerThread,
  220. NULL,
  221. 0,
  222. &threadId);
  223. if (NULL == hThread)
  224. {
  225. appDebugOut((DEB_ERROR, "Error creating thread\n"));
  226. }
  227. else
  228. {
  229. CloseHandle(hThread); // No reason to keep handle around
  230. }
  231. }
  232. //+-------------------------------------------------------------------------
  233. //
  234. // Function: WaitForServerThread
  235. //
  236. // Synopsis: Thread procedure for the thread that waits for the server
  237. // to start.
  238. //
  239. // History: 25-Apr-95 BruceFo Created
  240. //
  241. //--------------------------------------------------------------------------
  242. DWORD WINAPI
  243. WaitForServerThread(
  244. IN LPVOID ThreadParameter
  245. )
  246. {
  247. appDebugOut((DEB_TRACE, "Created thread to wait for server to start\n"));
  248. if (CheckServiceController())
  249. {
  250. // the server has started
  251. appDebugOut((DEB_TRACE, "The server finally started, after waiting in a thread. Refresh all!\n"));
  252. g_ShareCache.Refresh(); // sets g_fSharingEnabled
  253. }
  254. return 0;
  255. }
  256. //+-------------------------------------------------------------------------
  257. //
  258. // Function: CheckServiceController
  259. //
  260. // Synopsis: Returns TRUE if the server starts, based on consulting the
  261. // service controller and waiting for the server to start.
  262. //
  263. // History: 25-Apr-95 BruceFo Created
  264. //
  265. //--------------------------------------------------------------------------
  266. BOOL
  267. CheckServiceController(
  268. VOID
  269. )
  270. {
  271. // See if it is currently starting.
  272. SC_HANDLE hScManager;
  273. SC_HANDLE hService;
  274. SERVICE_STATUS serviceStatus;
  275. hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  276. if (hScManager == NULL)
  277. {
  278. appDebugOut((DEB_ERROR,
  279. "CheckServiceController: OpenSCManager failed: 0x%08lx\n",
  280. GetLastError()));
  281. return FALSE;
  282. }
  283. appDebugOut((DEB_TRACE, "CheckServiceController: opening server service\n"));
  284. hService = OpenService(hScManager, SERVICE_SERVER, SERVICE_QUERY_STATUS);
  285. if (hService == NULL)
  286. {
  287. appDebugOut((DEB_ERROR,
  288. "CheckServiceController: OpenService failed: 0x%08lx\n",
  289. GetLastError()));
  290. CloseServiceHandle(hScManager);
  291. return FALSE;
  292. }
  293. // Now we've got a handle to the server service. See if it's started.
  294. appDebugOut((DEB_TRACE, "CheckServiceController: querying server service\n"));
  295. if (!QueryServiceStatus(hService, &serviceStatus))
  296. {
  297. appDebugOut((DEB_ERROR,
  298. "CheckServiceController: QueryServiceStatus failed: 0x%08lx\n",
  299. GetLastError()));
  300. CloseServiceHandle(hScManager);
  301. CloseServiceHandle(hService);
  302. return FALSE;
  303. }
  304. if (serviceStatus.dwCurrentState == SERVICE_RUNNING)
  305. {
  306. appDebugOut((DEB_TRACE, "CheckServiceController: Server is running!\n"));
  307. // we've off to the races!
  308. CloseServiceHandle(hScManager);
  309. CloseServiceHandle(hService);
  310. return TRUE;
  311. }
  312. if (serviceStatus.dwCurrentState != SERVICE_START_PENDING)
  313. {
  314. appDebugOut((DEB_TRACE, "CheckServiceController: Server is not running nor is it starting! State = %d\n", serviceStatus.dwCurrentState));
  315. // The server is not in the process of starting. Go check its
  316. // configuration and see if it is even configured to start.
  317. if (!ServerConfiguredToStart(hScManager))
  318. {
  319. // the service is not in the process of starting, nor is it
  320. // configured to start, so we give up.
  321. CloseServiceHandle(hScManager);
  322. CloseServiceHandle(hService);
  323. return FALSE;
  324. }
  325. if (!WaitForServerToBeginStarting(hService, &serviceStatus))
  326. {
  327. // The server is configured to start, but we already waited for
  328. // it to commence starting, and it never did. So give up on
  329. // it.
  330. CloseServiceHandle(hScManager);
  331. CloseServiceHandle(hService);
  332. return FALSE;
  333. }
  334. // the server is configured to start, we waited for it to commence
  335. // its startup sequence, and it actually did commence starting!
  336. }
  337. // In this case, the service is trying to start. Wait until it either
  338. // starts or we think it's hung.
  339. appDebugOut((DEB_TRACE, "CheckServiceController: Server is starting\n"));
  340. //
  341. // record the current check point. The service should "periodically
  342. // increment" this if it is making progress.
  343. //
  344. DWORD lastCheckPoint = serviceStatus.dwCheckPoint;
  345. DWORD waitCount = 0;
  346. while (serviceStatus.dwCurrentState == SERVICE_START_PENDING)
  347. {
  348. if (lastCheckPoint == serviceStatus.dwCheckPoint)
  349. {
  350. ++waitCount;
  351. if (waitCount > MAX_WAIT_COUNT)
  352. {
  353. appDebugOut((DEB_TRACE,
  354. "CheckServiceController: Server service is HUNG\n"));
  355. CloseServiceHandle(hScManager);
  356. CloseServiceHandle(hService);
  357. return FALSE;
  358. }
  359. }
  360. else
  361. {
  362. waitCount = 0;
  363. lastCheckPoint = serviceStatus.dwCheckPoint;
  364. }
  365. // Ideally, we would wait the wait hint and be done with it. However,
  366. // We don't want to be waiting if the service gives us an overly
  367. // generous wait hint and finishes while we're still waiting. So,
  368. // wait a fraction of the wait hint. The exact fraction is
  369. // 1/WAIT_FRACTION. The effect is that we wait no more than
  370. // MAX_WAIT_COUNT / WAIT_FRACTION times the wait hint before
  371. // giving up. We make sure we wait at least 1 second between checks.
  372. // Finally, cap the wait hint in case it is far to large, possibly
  373. // in error.
  374. DWORD dwWait = serviceStatus.dwWaitHint / WAIT_FRACTION;
  375. dwWait = (dwWait > MAX_WAIT_PERIOD) ? MAX_WAIT_PERIOD : dwWait;
  376. dwWait = (dwWait < 1000) ? 1000 : dwWait; // at least 1 second
  377. appDebugOut((DEB_TRACE,
  378. "CheckServiceController: sleeping. hint = %d, actually waiting %d\n",
  379. serviceStatus.dwWaitHint, dwWait));
  380. Sleep(dwWait);
  381. if (!QueryServiceStatus(hService, &serviceStatus))
  382. {
  383. appDebugOut((DEB_ERROR,
  384. "CheckServiceController: QueryServiceStatus failed: 0x%08lx\n",
  385. GetLastError()));
  386. CloseServiceHandle(hScManager);
  387. CloseServiceHandle(hService);
  388. return FALSE;
  389. }
  390. }
  391. CloseServiceHandle(hScManager);
  392. CloseServiceHandle(hService);
  393. if (serviceStatus.dwCurrentState == SERVICE_RUNNING)
  394. {
  395. appDebugOut((DEB_TRACE, "CheckServiceController: service finally started\n"));
  396. // This magic line refreshes *all* the explorer windows. Unfortunately,
  397. // it causes a share cache refresh for each one as well...
  398. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
  399. return TRUE; // Finally! It's running!
  400. }
  401. else
  402. {
  403. appDebugOut((DEB_TRACE, "CheckServiceController: service never started\n"));
  404. return FALSE;
  405. }
  406. }
  407. BOOL
  408. ServerConfiguredToStart(
  409. SC_HANDLE hScManager
  410. )
  411. {
  412. // We re-open the service because we want a different type of permission
  413. SC_HANDLE hService = OpenService(hScManager, SERVICE_SERVER, SERVICE_QUERY_CONFIG);
  414. if (hService == NULL)
  415. {
  416. appDebugOut((DEB_ERROR,
  417. "ServerConfiguredToStart: OpenService failed: 0x%08lx\n",
  418. GetLastError()));
  419. return FALSE;
  420. }
  421. BOOL b;
  422. DWORD cbBytesNeeded;
  423. BYTE buffer[1024]; // a large buffer...
  424. LPQUERY_SERVICE_CONFIG lpqscServConfig = (LPQUERY_SERVICE_CONFIG)buffer;
  425. b = QueryServiceConfig(
  426. hService,
  427. lpqscServConfig,
  428. sizeof(buffer),
  429. &cbBytesNeeded);
  430. if (!b)
  431. {
  432. appDebugOut((DEB_ERROR,
  433. "ServerConfiguredToStart: QueryServiceConfig failed: 0x%08lx\n",
  434. GetLastError()));
  435. return FALSE;
  436. }
  437. b = (lpqscServConfig->dwStartType == SERVICE_AUTO_START);
  438. CloseServiceHandle(hService);
  439. appDebugOut((DEB_TRACE,
  440. "ServerConfiguredToStart: configured to start? %s\n",
  441. b ? "yes" : "no"));
  442. return b;
  443. }
  444. BOOL
  445. WaitForServerToBeginStarting(
  446. SC_HANDLE hService,
  447. LPSERVICE_STATUS pServiceStatus // so we don't need to re-query on successful return
  448. )
  449. {
  450. // Here's the algorithm:
  451. // wait WAIT_TO_BEGIN_GRANULARITY milliseconds
  452. // query status
  453. // if the service is running then return TRUE
  454. // if we've waited MAX_WAIT_TO_BEGIN ms, return FALSE
  455. // go back and wait again...
  456. DWORD dwWaitedMilliseconds;
  457. for (dwWaitedMilliseconds = 0;
  458. dwWaitedMilliseconds < MAX_WAIT_TO_BEGIN;
  459. dwWaitedMilliseconds += WAIT_TO_BEGIN_GRANULARITY)
  460. {
  461. appDebugOut((DEB_TRACE,
  462. "WaitForServerToBeginStarting: sleeping\n"));
  463. Sleep(WAIT_TO_BEGIN_GRANULARITY);
  464. if (!QueryServiceStatus(hService, pServiceStatus))
  465. {
  466. appDebugOut((DEB_ERROR,
  467. "WaitForServerToBeginStarting: QueryServiceStatus failed: 0x%08lx\n",
  468. GetLastError()));
  469. return FALSE;
  470. }
  471. if ( pServiceStatus->dwCurrentState == SERVICE_RUNNING
  472. || pServiceStatus->dwCurrentState == SERVICE_START_PENDING
  473. )
  474. {
  475. appDebugOut((DEB_TRACE,
  476. "WaitForServerToBeginStarting: server commenced startup\n"));
  477. return TRUE;
  478. }
  479. }
  480. appDebugOut((DEB_TRACE,
  481. "WaitForServerToBeginStarting: waited %d milliseconds for server to commence startup, then gave up\n",
  482. MAX_WAIT_TO_BEGIN));
  483. return FALSE;
  484. }