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.

1006 lines
24 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. service.cxx
  5. Abstract:
  6. Process init and service controller interaction
  7. Author:
  8. Mario Goertzel [MarioGo]
  9. Revision History:
  10. MarioGo 06-14-95 Cloned RPCSS from the old endpoint mapper.
  11. jroberts 06-29-00 Cloned BITS from RPCSS
  12. --*/
  13. #include "qmgrlib.h"
  14. #if !defined(BITS_V12_ON_NT4)
  15. #include "service.tmh"
  16. #endif
  17. #define CHECK_DLL_VERSION 1
  18. //
  19. // We delay the expensive setup tasks for a few minutes, to avoid slowing the
  20. // boot or logon process. The delay time is expressed in milliseconds.
  21. //
  22. // #define STARTUP_DELAY (5 * 60 * 1000)
  23. #define STARTUP_DELAY (5 * 1000)
  24. // Array of service status blocks and pointers to service control
  25. // functions for each component service.
  26. #define SERVICE_NAME _T("BITS")
  27. #define DEVICE_PREFIX _T("\\\\.\\")
  28. VOID WINAPI ServiceMain(DWORD, LPTSTR*);
  29. VOID UpdateState(DWORD dwNewState);
  30. extern BOOL CatalogDllMain (
  31. HINSTANCE hInst,
  32. DWORD dwReason,
  33. LPVOID lpReserved
  34. );
  35. SERVICE_TABLE_ENTRY gaServiceEntryTable[] = {
  36. { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
  37. { NULL, NULL }
  38. };
  39. HINSTANCE g_hInstance;
  40. SERVICE_STATUS gServiceStatus;
  41. SERVICE_STATUS_HANDLE ghServiceHandle;
  42. // This event is set when we receive a SERVICE_CONTROL_STOP/SHUTDOWN
  43. HANDLE g_hServiceStopEvent = NULL;
  44. //
  45. // When TRUE, the process is running outside the service controller.
  46. //
  47. BOOL g_fVisible = FALSE;
  48. typedef SERVICE_STATUS_HANDLE (*PREGISTER_FUNC)(
  49. LPCTSTR lpServiceName,
  50. LPHANDLER_FUNCTION_EX lpHandlerProc,
  51. LPVOID lpContext );
  52. typedef VOID (*PSERVICE_MAIN_FUNC)(
  53. DWORD argc,
  54. LPTSTR *lpszArgv,
  55. PREGISTER_FUNC RegisterFunc );
  56. extern "C"
  57. VOID
  58. BITSServiceMain(
  59. DWORD argc,
  60. LPTSTR *argv,
  61. PREGISTER_FUNC lpRegisterFunc
  62. );
  63. #if CHECK_DLL_VERSION
  64. BOOL
  65. GetModuleVersion64(
  66. HMODULE hDll,
  67. ULONG64 * pVer
  68. );
  69. BOOL
  70. GetFileVersion64(
  71. LPTSTR szFullPath,
  72. ULONG64 * pVer
  73. );
  74. #endif
  75. LPHANDLER_FUNCTION_EX g_RealHandler = NULL;
  76. HINSTANCE g_RealLibrary = NULL;
  77. LONG g_RealLibraryRefs = 0;
  78. ULONG
  79. ServiceHandlerThunk(
  80. DWORD dwCode,
  81. DWORD dwEventType,
  82. PVOID EventData,
  83. PVOID pData )
  84. {
  85. InterlockedIncrement( &g_RealLibraryRefs );
  86. ULONG Result =
  87. g_RealHandler( dwCode, dwEventType, EventData, pData );
  88. if (!InterlockedDecrement( &g_RealLibraryRefs ) )
  89. {
  90. FreeLibrary( g_RealLibrary );
  91. g_RealLibrary = NULL;
  92. }
  93. return Result;
  94. }
  95. SERVICE_STATUS_HANDLE
  96. RegisterServiceHandlerThunk(
  97. LPCTSTR lpServiceName,
  98. LPHANDLER_FUNCTION_EX lpHandlerProc,
  99. LPVOID lpContext )
  100. {
  101. g_RealHandler = lpHandlerProc;
  102. return
  103. RegisterServiceCtrlHandlerEx( lpServiceName,
  104. ServiceHandlerThunk,
  105. lpContext
  106. );
  107. }
  108. bool
  109. JumpToRealDLL(
  110. DWORD argc,
  111. LPTSTR *argv )
  112. {
  113. #define MAX_DLLNAME (MAX_PATH+1)
  114. HKEY BitsKey = NULL;
  115. LONG Result =
  116. RegOpenKey( HKEY_LOCAL_MACHINE, C_QMGR_REG_KEY, &BitsKey );
  117. if ( Result )
  118. goto noload;
  119. static TCHAR DLLName[MAX_DLLNAME];
  120. DWORD Type;
  121. DWORD NameSize = sizeof(DLLName);
  122. Result = RegQueryValueEx(
  123. BitsKey,
  124. C_QMGR_SERVICEDLL,
  125. NULL,
  126. &Type,
  127. (LPBYTE)DLLName,
  128. &NameSize );
  129. if ( Result ||
  130. (( Type != REG_SZ ) && (Type != REG_EXPAND_SZ)) )
  131. {
  132. goto noload;
  133. }
  134. RegCloseKey( BitsKey );
  135. BitsKey = NULL;
  136. if (Type == REG_EXPAND_SZ)
  137. {
  138. static TCHAR ExpandedDLLName[MAX_DLLNAME];
  139. DWORD size = ExpandEnvironmentStrings( DLLName, ExpandedDLLName, MAX_DLLNAME );
  140. if (size == 0)
  141. {
  142. // out of resources
  143. return true;
  144. }
  145. HRESULT hr;
  146. hr = StringCchCopy( DLLName, RTL_NUMBER_OF(DLLName), ExpandedDLLName );
  147. if (FAILED(hr))
  148. {
  149. // too long; must be badly formatted. Ignore it.
  150. goto noload;
  151. }
  152. }
  153. #if CHECK_DLL_VERSION
  154. //
  155. // At this point, we know that the registry specifies an alternate DLL.
  156. // See whether it has a later version than the current one.
  157. //
  158. ULONG64 AlternateDllVersion = 0;
  159. ULONG64 MyDllVersion = 0;
  160. if (!QMgrFileExists( DLLName ))
  161. {
  162. goto noload;
  163. }
  164. if (!GetFileVersion64( DLLName, &AlternateDllVersion ))
  165. {
  166. // can't ascertain the version. Don't start the service at all.
  167. return true;
  168. }
  169. if (!GetModuleVersion64( g_hInstance, &MyDllVersion ))
  170. {
  171. // can't ascertain the version. Don't start the service at all.
  172. return true;
  173. }
  174. if (MyDllVersion >= AlternateDllVersion)
  175. {
  176. goto noload;
  177. }
  178. #endif
  179. g_RealLibrary = LoadLibrary( DLLName );
  180. if ( !g_RealLibrary )
  181. goto noload;
  182. PSERVICE_MAIN_FUNC ServiceMainFunc =
  183. (PSERVICE_MAIN_FUNC)GetProcAddress( g_RealLibrary, "BITSServiceMain" );
  184. if ( !ServiceMainFunc )
  185. goto noload;
  186. g_RealLibraryRefs = 1;
  187. // Ok to call into real library now.
  188. ( *ServiceMainFunc ) ( argc, argv, RegisterServiceHandlerThunk );
  189. if (!InterlockedDecrement( &g_RealLibraryRefs ) )
  190. {
  191. FreeLibrary( g_RealLibrary );
  192. g_RealLibrary = NULL;
  193. }
  194. return true;
  195. noload:
  196. if ( BitsKey )
  197. RegCloseKey( BitsKey );
  198. if ( g_RealLibrary )
  199. FreeLibrary( g_RealLibrary );
  200. return false;
  201. }
  202. VOID WINAPI
  203. ServiceMain(
  204. DWORD argc,
  205. LPTSTR *argv
  206. )
  207. /*++
  208. Routine Description:
  209. Callback by the service controller when starting this service.
  210. Arguments:
  211. argc - number of arguments, usually 1
  212. argv - argv[0] is the name of the service.
  213. argv[>0] are arguments passed to the service.
  214. Return Value:
  215. None
  216. --*/
  217. {
  218. volatile static LONG ThreadRunning = 0;
  219. if ( InterlockedCompareExchange( &ThreadRunning, 1, 0 ) == 1 )
  220. {
  221. // A thread is already running ServiceMain, just exit.
  222. // The service controller has a bug where it can create multiple
  223. // threads to call ServiceMain in high stress conditions.
  224. return;
  225. }
  226. if (!JumpToRealDLL( argc, argv) )
  227. {
  228. BITSServiceMain(
  229. argc,
  230. argv,
  231. RegisterServiceCtrlHandlerEx );
  232. }
  233. ThreadRunning = 0;
  234. }
  235. DWORD g_LastServiceControl;
  236. ULONG WINAPI
  237. BITSServiceHandler(
  238. DWORD dwCode,
  239. DWORD dwEventType,
  240. PVOID EventData,
  241. PVOID pData
  242. )
  243. /*++
  244. Routine Description:
  245. Lowest level callback from the service controller to
  246. cause this service to change our status. (stop, start, pause, etc).
  247. Arguments:
  248. opCode - One of the service "Controls" value.
  249. SERVICE_CONTROL_{STOP, PAUSE, CONTINUE, INTERROGATE, SHUTDOWN}.
  250. Return Value:
  251. None
  252. --*/
  253. {
  254. switch(dwCode)
  255. {
  256. case SERVICE_CONTROL_STOP:
  257. {
  258. LogService( "STOP request" );
  259. //
  260. // only relevant in running state; damaging if we are stopping
  261. // and g_hServiceStopEvent is deleted.
  262. //
  263. if (gServiceStatus.dwCurrentState == SERVICE_RUNNING)
  264. {
  265. g_LastServiceControl = dwCode;
  266. UpdateState( SERVICE_STOP_PENDING );
  267. SetEvent( g_hServiceStopEvent );
  268. }
  269. break;
  270. }
  271. case SERVICE_CONTROL_INTERROGATE:
  272. // Service controller wants us to call SetServiceStatus.
  273. LogService( "INTERROGATE request" );
  274. UpdateState(gServiceStatus.dwCurrentState);
  275. break ;
  276. case SERVICE_CONTROL_SHUTDOWN:
  277. // The machine is shutting down. We'll be killed once we return.
  278. LogService( "SHUTDOWN request" );
  279. g_LastServiceControl = dwCode;
  280. UpdateState( SERVICE_STOP_PENDING );
  281. SetEvent( g_hServiceStopEvent );
  282. while (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING)
  283. {
  284. LogService( "service pending; sleeping..." );
  285. Sleep(100);
  286. }
  287. break;
  288. #if !defined( BITS_V12_ON_NT4 )
  289. case SERVICE_CONTROL_DEVICEEVENT:
  290. return DeviceEventCallback( dwEventType, EventData );
  291. #endif
  292. case SERVICE_CONTROL_SESSIONCHANGE:
  293. {
  294. WTSSESSION_NOTIFICATION* pswtsi = (WTSSESSION_NOTIFICATION*) EventData;
  295. DWORD dwSessionId = pswtsi->dwSessionId;
  296. switch (dwEventType)
  297. {
  298. case WTS_SESSION_LOGON:
  299. {
  300. LogService("logon at session %d", dwSessionId);
  301. SessionLogonCallback( dwSessionId );
  302. break;
  303. }
  304. case WTS_SESSION_LOGOFF:
  305. {
  306. LogService("logoff at session %d", dwSessionId);
  307. SessionLogoffCallback( dwSessionId );
  308. break;
  309. }
  310. default: //Is there a default?
  311. break;
  312. }
  313. break;
  314. }
  315. default:
  316. LogError( "%!ts!: Unexpected service control message %d.\n", SERVICE_NAME, dwCode);
  317. return ERROR_CALL_NOT_IMPLEMENTED;
  318. }
  319. return NO_ERROR;
  320. }
  321. bool
  322. IsServiceShuttingDown()
  323. {
  324. return (gServiceStatus.dwCurrentState == SERVICE_STOP_PENDING);
  325. }
  326. VOID
  327. UpdateState(
  328. DWORD dwNewState
  329. )
  330. /*++
  331. Routine Description:
  332. Updates this services state with the service controller.
  333. Arguments:
  334. dwNewState - The next start for this service. One of
  335. SERVICE_START_PENDING
  336. SERVICE_RUNNING
  337. Return Value:
  338. None
  339. --*/
  340. {
  341. DWORD status = ERROR_SUCCESS;
  342. LogService("state change: old %d new %d", gServiceStatus.dwCurrentState, dwNewState );
  343. switch (dwNewState)
  344. {
  345. case SERVICE_RUNNING:
  346. case SERVICE_STOPPED:
  347. gServiceStatus.dwCheckPoint = 0;
  348. gServiceStatus.dwWaitHint = 0;
  349. break;
  350. case SERVICE_START_PENDING:
  351. case SERVICE_STOP_PENDING:
  352. ++gServiceStatus.dwCheckPoint;
  353. gServiceStatus.dwWaitHint = 30000L;
  354. break;
  355. default:
  356. ASSERT(0);
  357. status = ERROR_INVALID_SERVICE_CONTROL;
  358. break;
  359. }
  360. if (status == ERROR_SUCCESS)
  361. {
  362. gServiceStatus.dwCurrentState = dwNewState;
  363. if (!SetServiceStatus(ghServiceHandle, &gServiceStatus))
  364. {
  365. status = GetLastError();
  366. }
  367. }
  368. if (status != ERROR_SUCCESS)
  369. {
  370. LogError( "%!ts!: Failed to update service state: %d\n", SERVICE_NAME, status);
  371. }
  372. // We could return a status but how would we recover? Ignore it, the
  373. // worst thing is that services will kill us and there's nothing
  374. // we can about it if this call fails.
  375. LogInfo( "Finished updating service state to %u", dwNewState );
  376. return;
  377. }
  378. extern "C"
  379. VOID
  380. BITSServiceMain(
  381. DWORD argc,
  382. LPTSTR *argv,
  383. PREGISTER_FUNC lpRegisterFunc
  384. )
  385. /*++
  386. Routine Description:
  387. Callback by the service controller when starting this service.
  388. Arguments:
  389. argc - number of arguments, usually 1
  390. argv - argv[0] is the name of the service.
  391. argv[>0] are arguments passed to the service.
  392. Return Value:
  393. None
  394. --*/
  395. {
  396. BOOL f = FALSE;
  397. HRESULT hr = S_OK;
  398. bool bGlobals = false;
  399. bool bQmgr = false;
  400. try
  401. {
  402. DWORD status = ERROR_SUCCESS;
  403. FILETIME ftStartTime;
  404. GetSystemTimeAsFileTime( &ftStartTime );
  405. Log_Init();
  406. Log_StartLogger();
  407. LogInfo("Service started at %!TIMESTAMP!", FILETIMEToUINT64( ftStartTime ) );
  408. //
  409. // Set up for service notifications.
  410. //
  411. gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  412. gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  413. gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
  414. // The SESSIONCHANGE notification is only available on WindowsXP
  415. // and the service controller will become confused if this is given on windows 2000
  416. if ( WINDOWSXP_PLATFORM == g_PlatformVersion )
  417. gServiceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
  418. gServiceStatus.dwWin32ExitCode = 0;
  419. gServiceStatus.dwServiceSpecificExitCode = 0;
  420. gServiceStatus.dwCheckPoint = 0;
  421. gServiceStatus.dwWaitHint = 30000L;
  422. ghServiceHandle = (*lpRegisterFunc)( SERVICE_NAME,
  423. BITSServiceHandler,
  424. 0
  425. );
  426. if (0 == ghServiceHandle)
  427. {
  428. status = GetLastError();
  429. ASSERT(status != ERROR_SUCCESS);
  430. LogError( "RegisterServiceCtrlHandlerEx failed %!winerr!", status);
  431. hr = HRESULT_FROM_WIN32( status );
  432. }
  433. UpdateState(SERVICE_START_PENDING);
  434. // Set up an event that will be signaled when the service is
  435. // stopped or shutdown.
  436. g_hServiceStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  437. if( NULL == g_hServiceStopEvent )
  438. {
  439. status = GetLastError();
  440. LogError( "CreateEvent failed %!winerr!", status );
  441. THROW_HRESULT( HRESULT_FROM_WIN32( status ));
  442. }
  443. #if defined( BITS_V12_ON_NT4 )
  444. if ( WINDOWS2000_PLATFORM == g_PlatformVersion ||
  445. NT4_PLATFORM == g_PlatformVersion )
  446. #else
  447. if ( WINDOWS2000_PLATFORM == g_PlatformVersion )
  448. #endif
  449. {
  450. HRESULT Hr;
  451. bool CoInitCalled = false;
  452. Hr = CoInitializeEx( NULL, COINIT_MULTITHREADED );
  453. if ( FAILED( Hr ) &&
  454. ( Hr != RPC_E_CHANGED_MODE ) )
  455. THROW_HRESULT( Hr );
  456. CoInitCalled = true;
  457. Hr =
  458. CoInitializeSecurity(
  459. NULL, // pSecDesc
  460. -1, // cAuthSvc
  461. NULL, // asAuthSvc
  462. NULL, // pReserved
  463. RPC_C_AUTHN_LEVEL_PKT, // dwAuthnLevel
  464. RPC_C_IMP_LEVEL_IDENTIFY, // dwImpLevel
  465. NULL, // pReserved2
  466. #if defined( BITS_V12_ON_NT4 )
  467. 0,
  468. #else
  469. EOAC_NO_CUSTOM_MARSHAL | // dwCapabilities
  470. EOAC_DISABLE_AAA |
  471. EOAC_STATIC_CLOAKING,
  472. #endif
  473. NULL ); // pReserved3
  474. if ( FAILED( Hr ) &&
  475. ( Hr != RPC_E_TOO_LATE ) )
  476. {
  477. LogError( "Unable to initialize security on Win2k, error %!winerr!", Hr );
  478. if ( CoInitCalled )
  479. CoUninitialize();
  480. THROW_HRESULT( Hr );
  481. }
  482. }
  483. LogInfo( "Initializing globalinfo\n" );
  484. THROW_HRESULT( GlobalInfo::Init() );
  485. bGlobals = true;
  486. LogInfo( "Initializing qmgr\n" );
  487. THROW_HRESULT( InitQmgr() );
  488. bQmgr = true;
  489. LogInfo( "Setting service to running.");
  490. //
  491. // Allow service controller to resume other duties.
  492. //
  493. UpdateState(SERVICE_RUNNING);
  494. //
  495. // wait for the stop signal.
  496. //
  497. if( WAIT_OBJECT_0 != WaitForSingleObject( g_hServiceStopEvent, INFINITE ))
  498. {
  499. status = GetLastError();
  500. LogError( "ServiceMain failed waiting for stop signal %!winerr!", status);
  501. hr = HRESULT_FROM_WIN32( status );
  502. }
  503. hr = S_OK;
  504. }
  505. catch ( ComError exception )
  506. {
  507. hr = exception.Error();
  508. }
  509. if (bQmgr)
  510. {
  511. HRESULT hr2 = UninitQmgr();
  512. if (FAILED(hr2))
  513. {
  514. LogError( "uninit Qmgr failed %!winerr!", hr2);
  515. }
  516. }
  517. if (bGlobals)
  518. {
  519. HRESULT hr2 = GlobalInfo::Uninit();
  520. if (FAILED(hr2))
  521. {
  522. LogError( "uninit GlobalInfo failed %!winerr!", hr2);
  523. }
  524. }
  525. if (g_hServiceStopEvent)
  526. {
  527. CloseHandle( g_hServiceStopEvent );
  528. g_hServiceStopEvent = NULL;
  529. }
  530. if (FAILED(hr))
  531. {
  532. gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  533. gServiceStatus.dwServiceSpecificExitCode = hr;
  534. }
  535. LogService( "ServiceMain returning, hr = %x", hr );
  536. Log_Close();
  537. UpdateState(SERVICE_STOPPED);
  538. }
  539. enum
  540. {
  541. STARTUP_UNKNOWN,
  542. STARTUP_DEMAND,
  543. STARTUP_AUTO
  544. }
  545. g_ServiceStartupState = STARTUP_UNKNOWN;
  546. HRESULT
  547. SetServiceStartup( bool bAutoStart )
  548. {
  549. LogService( "Setting startup to %s", bAutoStart ? ("Auto") : ("Demand") );
  550. HRESULT Hr = S_OK;
  551. //
  552. // Changing the service state is expensive, so avoid it if possible.
  553. //
  554. // No need to monitor external changes to the startup state, though:
  555. //
  556. // If the admin changes our state from AUTO to DEMAND or DISABLED, then
  557. // the consequent lack of progress is his fault, and admins should know that.
  558. //
  559. // If the admin changes our state from DEMAND to AUTO, then we will
  560. // start more often but the result is otherwise harmless.
  561. //
  562. if ((g_ServiceStartupState == STARTUP_DEMAND && bAutoStart == FALSE) ||
  563. (g_ServiceStartupState == STARTUP_AUTO && bAutoStart == TRUE))
  564. {
  565. LogService( "startup state is already correct" );
  566. return S_OK;
  567. }
  568. if (gServiceStatus.dwCurrentState != SERVICE_RUNNING)
  569. {
  570. LogService("can't change startup state in state %d", gServiceStatus.dwCurrentState);
  571. return S_OK;
  572. }
  573. SC_HANDLE hServiceManager = NULL;
  574. SC_HANDLE hService = NULL;
  575. try
  576. {
  577. try
  578. {
  579. hServiceManager =
  580. OpenSCManager( NULL,
  581. NULL,
  582. SC_MANAGER_ALL_ACCESS );
  583. if ( !hServiceManager ) throw (DWORD)GetLastError();
  584. hService =
  585. OpenService( hServiceManager,
  586. SERVICE_NAME,
  587. SERVICE_CHANGE_CONFIG );
  588. if ( !hService ) throw (DWORD)GetLastError();
  589. BOOL bResult =
  590. ChangeServiceConfig( hService, // service handle
  591. SERVICE_NO_CHANGE, // dwServiceType
  592. bAutoStart ? SERVICE_AUTO_START : SERVICE_DEMAND_START, // dwStartType
  593. SERVICE_NO_CHANGE, // dwErrorControl
  594. NULL, // lpBinaryPathName
  595. NULL, // lpLoadOrderGroup
  596. NULL, // lpdwTagId
  597. NULL, // lpDependencies
  598. NULL, // lpServiceStartName
  599. NULL, // lpPassword
  600. NULL); // lpDisplayName
  601. if ( !bResult ) throw (DWORD)GetLastError();
  602. if (bAutoStart)
  603. {
  604. g_ServiceStartupState = STARTUP_AUTO;
  605. }
  606. else
  607. {
  608. g_ServiceStartupState = STARTUP_DEMAND;
  609. }
  610. }
  611. catch( DWORD dwException )
  612. {
  613. throw (HRESULT)HRESULT_FROM_WIN32( dwException );
  614. }
  615. }
  616. catch (HRESULT HrException)
  617. {
  618. Hr = HrException;
  619. LogError( "An error occurred setting service startup, %!winerr!", Hr );
  620. }
  621. if ( hService )
  622. {
  623. CloseServiceHandle( hService );
  624. }
  625. if ( hServiceManager )
  626. {
  627. CloseServiceHandle( hServiceManager );
  628. }
  629. LogService( " HR: %!winerr!", Hr );
  630. return Hr;
  631. }
  632. int InitializeBitsAllocator();
  633. extern "C"
  634. BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
  635. {
  636. if (dwReason == DLL_PROCESS_ATTACH)
  637. {
  638. g_hInstance = hInstance;
  639. DisableThreadLibraryCalls(hInstance);
  640. if (!InitCompilerLibrary())
  641. {
  642. return FALSE;
  643. }
  644. if ( !DetectProductVersion() )
  645. {
  646. UninitCompilerLibrary();
  647. return FALSE;
  648. }
  649. if (0 != InitializeBitsAllocator())
  650. {
  651. UninitCompilerLibrary();
  652. return FALSE;
  653. }
  654. }
  655. else if ( dwReason == DLL_PROCESS_DETACH )
  656. {
  657. UninitCompilerLibrary();
  658. }
  659. return TRUE; // ok
  660. }
  661. #if CHECK_DLL_VERSION
  662. //
  663. // This ungainly typedef seems to have no global definition. There are several identical
  664. // definitions in the Windows NT sources, each of which has that bizarre bit-stripping
  665. // on szKey. I got mine from \nt\base\ntsetup\srvpack\update\splib\common.h.
  666. //
  667. typedef struct tagVERHEAD {
  668. WORD wTotLen;
  669. WORD wValLen;
  670. WORD wType; /* always 0 */
  671. WCHAR szKey[(sizeof("VS_VERSION_INFO")+3)&~03];
  672. VS_FIXEDFILEINFO vsf;
  673. } VERHEAD ;
  674. /*
  675. ** Purpose:
  676. ** Gets the file version values from the given file and sets the
  677. ** given ULONG64 variable.
  678. ** Arguments:
  679. ** szFullPath: a zero terminated character string containing the fully
  680. ** qualified path (including disk drive) to the file.
  681. ** Returns:
  682. ** fTrue if file and file version resource found and retrieved,
  683. ** fFalse if not.
  684. +++
  685. ** Implementation:
  686. **************************************************************************/
  687. BOOL
  688. GetFileVersion64(
  689. LPTSTR szFullPath,
  690. ULONG64 * pVer
  691. )
  692. {
  693. BOOL fRet = false;
  694. DWORD dwHandle;
  695. DWORD InfoSize;
  696. try
  697. {
  698. //
  699. // Get the file version info size
  700. //
  701. if ((InfoSize = GetFileVersionInfoSize( szFullPath, &dwHandle)) == 0)
  702. {
  703. return (fRet);
  704. }
  705. //
  706. // Allocate enough size to hold version info
  707. //
  708. auto_ptr<TCHAR> lpData ( LPTSTR(new byte[ InfoSize ]));
  709. //
  710. // Get the version info
  711. //
  712. fRet = GetFileVersionInfo( szFullPath, dwHandle, InfoSize, lpData.get());
  713. if (fRet)
  714. {
  715. UINT dwLen;
  716. VS_FIXEDFILEINFO *pvsfi;
  717. fRet = VerQueryValue(
  718. lpData.get(),
  719. L"\\",
  720. (LPVOID *)&pvsfi,
  721. &dwLen
  722. );
  723. //
  724. // Convert two DWORDs into a 64-bit integer.
  725. //
  726. if (fRet)
  727. {
  728. *pVer = ( ULONG64(pvsfi->dwFileVersionMS) << 32) | (pvsfi->dwFileVersionLS);
  729. }
  730. }
  731. return (fRet);
  732. }
  733. catch ( ComError err )
  734. {
  735. return false;
  736. }
  737. }
  738. BOOL
  739. GetModuleVersion64(
  740. HMODULE hDll,
  741. ULONG64 * pVer
  742. )
  743. {
  744. DWORD* pdwTranslation;
  745. VS_FIXEDFILEINFO* pFileInfo;
  746. UINT uiSize;
  747. HRSRC hrsrcVersion = FindResource(
  748. hDll,
  749. MAKEINTRESOURCE(VS_VERSION_INFO),
  750. RT_VERSION);
  751. if (!hrsrcVersion) return false;
  752. HGLOBAL hglobalVersion = LoadResource(hDll, hrsrcVersion);
  753. if (!hglobalVersion) return false;
  754. VERHEAD * pVerHead = (VERHEAD *) LockResource(hglobalVersion);
  755. if (!pVerHead) return false;
  756. // I stole this code from \nt\com\complus\src\shared\util\svcerr.cpp,
  757. // and the comment is theirs:
  758. //
  759. // VerQueryValue will write to the memory, for some reason.
  760. // Therefore we must make a writable copy of the version
  761. // resource info before calling that API.
  762. auto_ptr<char> pvVersionInfo ( new char[pVerHead->wTotLen + pVerHead->wTotLen/2] );
  763. memcpy(pvVersionInfo.get(), pVerHead, pVerHead->wTotLen); // SEC: REVIEWED 2002-03-28
  764. // Retrieve file version info
  765. BOOL fRet = VerQueryValue( pvVersionInfo.get(),
  766. L"\\",
  767. (void**)&pFileInfo,
  768. &uiSize);
  769. if (fRet)
  770. {
  771. *pVer = (ULONG64(pFileInfo->dwFileVersionMS) << 32) | (pFileInfo->dwFileVersionLS);
  772. }
  773. return fRet;
  774. }
  775. #endif