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.

820 lines
22 KiB

  1. //
  2. // Copyright (C) 1993-1997 Microsoft Corporation. All Rights Reserved.
  3. //
  4. // MODULE: service.c
  5. //
  6. // PURPOSE: Implements functions required by all services
  7. // windows.
  8. //
  9. // FUNCTIONS:
  10. // main(int argc, char **argv);
  11. // NTmain(int argc, char **argv);
  12. // W95main(int argc, char **argv);
  13. // service_ctrl(DWORD dwCtrlCode);
  14. // service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  15. // CmdInstallService();
  16. // CmdRemoveService();
  17. // CmdDebugService(int argc, char **argv); // DEBUG only
  18. // ControlHandler ( DWORD dwCtrlType ); // DEBUG only
  19. // GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); // DEBUG only
  20. //
  21. // COMMENTS:
  22. //
  23. // AUTHOR: Claus Giloi (based on SDK sample)
  24. //
  25. #include "precomp.h"
  26. #ifndef DEBUG
  27. #undef _tprintf
  28. #define _tprintf force_compile_error
  29. #endif // !DEBUG
  30. // internal variables
  31. SERVICE_STATUS ssStatus; // current status of the service
  32. SERVICE_STATUS_HANDLE sshStatusHandle;
  33. DWORD dwErr = 0;
  34. OSVERSIONINFO g_osvi; // The os version info structure global
  35. BOOL g_fInShutdown = FALSE;
  36. // internal function prototypes
  37. VOID WINAPI service_ctrl(DWORD dwCtrlCode);
  38. VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  39. VOID CmdInstallService();
  40. VOID CmdRemoveService();
  41. void __cdecl NTmain(int argc, char **argv);
  42. void __cdecl W95main(int argc, char **argv);
  43. // Debug only functionality
  44. #ifdef DEBUG
  45. TCHAR szErr[256];
  46. BOOL bDebug = FALSE;
  47. VOID CmdDebugService(int argc, char **argv);
  48. BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
  49. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  50. extern BOOL InitDebugMemoryOptions(void);
  51. extern VOID DumpMemoryLeaksAndBreak(void);
  52. #endif // DEBUG
  53. typedef BOOL (WINAPI *PFNCHANGESERVICECONFIG2)(SC_HANDLE, DWORD, LPVOID);
  54. //
  55. // FUNCTION: main
  56. //
  57. // PURPOSE: entrypoint for service
  58. //
  59. // PARAMETERS:
  60. // argc - number of command line arguments
  61. // argv - array of command line arguments
  62. //
  63. // RETURN VALUE:
  64. // none
  65. //
  66. // COMMENTS:
  67. // Get Platform type and
  68. // call appropriate main for platform (NT or Win95)
  69. //
  70. void __cdecl main(int argc, char **argv)
  71. {
  72. #ifdef DEBUG
  73. InitDebugMemoryOptions();
  74. #endif // DEBUG
  75. // Store OS version info
  76. g_osvi.dwOSVersionInfoSize = sizeof(g_osvi);
  77. if (FALSE == ::GetVersionEx(&g_osvi))
  78. {
  79. ERROR_OUT(("GetVersionEx() failed!"));
  80. return;
  81. }
  82. RegEntry rePol(POLICIES_KEY, HKEY_LOCAL_MACHINE);
  83. if ( rePol.GetNumber( REGVAL_POL_NO_RDS, DEFAULT_POL_NO_RDS) )
  84. {
  85. WARNING_OUT(("RDS launch prevented by policy"));
  86. return;
  87. }
  88. if ( IS_NT )
  89. {
  90. NTmain( argc, argv );
  91. }
  92. else
  93. {
  94. W95main( argc, argv );
  95. }
  96. #ifdef DEBUG
  97. DumpMemoryLeaksAndBreak();
  98. #endif // DEBUG
  99. }
  100. //
  101. // FUNCTION: NTmain
  102. //
  103. // PURPOSE: entrypoint for service
  104. //
  105. // PARAMETERS:
  106. // argc - number of command line arguments
  107. // argv - array of command line arguments
  108. //
  109. // RETURN VALUE:
  110. // none
  111. //
  112. // COMMENTS:
  113. // NTmain() either performs the command line task, or
  114. // call StartServiceCtrlDispatcher to register the
  115. // main service thread. When the this call returns,
  116. // the service has stopped, so exit.
  117. //
  118. void __cdecl NTmain(int argc, char **argv)
  119. {
  120. SERVICE_TABLE_ENTRY dispatchTable[] =
  121. {
  122. { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main },
  123. { NULL, NULL }
  124. };
  125. if ( (argc > 1) &&
  126. ((*argv[1] == '-') || (*argv[1] == '/')) )
  127. {
  128. if ( lstrcmpi( "install", argv[1]+1 ) == 0 )
  129. {
  130. CmdInstallService();
  131. }
  132. else if ( lstrcmpi( "remove", argv[1]+1 ) == 0 )
  133. {
  134. CmdRemoveService();
  135. }
  136. #ifdef DEBUG
  137. else if ( lstrcmpi( "debug", argv[1]+1 ) == 0 )
  138. {
  139. bDebug = TRUE;
  140. CmdDebugService(argc, argv);
  141. }
  142. #endif // DEBUG
  143. else
  144. {
  145. goto dispatch;
  146. }
  147. exit(0);
  148. }
  149. // if it doesn't match any of the above parameters
  150. // the service control manager may be starting the service
  151. // so we must call StartServiceCtrlDispatcher
  152. dispatch:
  153. #ifdef DEBUG
  154. // this is just to be friendly
  155. printf( "%s -install to install the service\n", SZAPPNAME );
  156. printf( "%s -remove to remove the service\n", SZAPPNAME );
  157. printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
  158. printf( "\nStartServiceCtrlDispatcher being called.\n" );
  159. printf( "This may take several seconds. Please wait.\n" );
  160. #endif // DEBUG
  161. if (!StartServiceCtrlDispatcher(dispatchTable)) {
  162. AddToMessageLog(EVENTLOG_ERROR_TYPE,
  163. 0,
  164. MSG_ERR_SERVICE,
  165. TEXT("StartServiceCtrlDispatcher failed."));
  166. }
  167. }
  168. //
  169. // FUNCTION: W95main
  170. //
  171. // PURPOSE: entrypoint for pseudo-service on Win95
  172. //
  173. // PARAMETERS:
  174. // argc - number of command line arguments
  175. // argv - array of command line arguments
  176. //
  177. // RETURN VALUE:
  178. // none
  179. //
  180. // COMMENTS:
  181. // W95main() registers as Win95 service and calls Init routine directly
  182. //
  183. typedef DWORD (WINAPI * REGISTERSERVICEPROC)(DWORD, DWORD);
  184. #ifndef RSP_SIMPLE_SERVICE
  185. #define RSP_SIMPLE_SERVICE 0x00000001
  186. #endif
  187. void __cdecl W95main(int argc, char **argv)
  188. {
  189. HMODULE hKernel;
  190. REGISTERSERVICEPROC lpfnRegisterServiceProcess;
  191. HANDLE hServiceEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, SERVICE_PAUSE_EVENT);
  192. if (hServiceEvent != NULL) // Service is already running
  193. {
  194. return;
  195. }
  196. if ( hKernel = GetModuleHandle("KERNEL32.DLL") )
  197. {
  198. if ( lpfnRegisterServiceProcess =
  199. (REGISTERSERVICEPROC)GetProcAddress ( hKernel,
  200. "RegisterServiceProcess" ))
  201. {
  202. if (!lpfnRegisterServiceProcess(NULL, RSP_SIMPLE_SERVICE))
  203. {
  204. ERROR_OUT(("RegisterServiceProcess failed"));
  205. }
  206. }
  207. else
  208. {
  209. ERROR_OUT(("GetProcAddr of RegisterServiceProcess failed"));
  210. }
  211. }
  212. else
  213. {
  214. ERROR_OUT(("GetModuleHandle of KERNEL32.DLL failed"));
  215. }
  216. MNMServiceStart(argc, argv);
  217. CloseHandle(hServiceEvent);
  218. }
  219. //
  220. // FUNCTION: service_main
  221. //
  222. // PURPOSE: To perform actual initialization of the service
  223. //
  224. // PARAMETERS:
  225. // dwArgc - number of command line arguments
  226. // lpszArgv - array of command line arguments
  227. //
  228. // RETURN VALUE:
  229. // none
  230. //
  231. // COMMENTS:
  232. // This routine performs the service initialization and then calls
  233. // the user defined MNMServiceStart() routine to perform majority
  234. // of the work.
  235. //
  236. void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
  237. {
  238. // register our service control handler:
  239. //
  240. sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
  241. if (!sshStatusHandle)
  242. goto cleanup;
  243. // SERVICE_STATUS members that don't change
  244. //
  245. ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  246. ssStatus.dwServiceSpecificExitCode = 0;
  247. TRACE_OUT(("starting service\n\r"));
  248. MNMServiceStart( dwArgc, lpszArgv );
  249. cleanup:
  250. return;
  251. }
  252. //
  253. // FUNCTION: service_ctrl
  254. //
  255. // PURPOSE: This function is called by the SCM whenever
  256. // ControlService() is called on this service.
  257. //
  258. // PARAMETERS:
  259. // dwCtrlCode - type of control requested
  260. //
  261. // RETURN VALUE:
  262. // none
  263. //
  264. // COMMENTS:
  265. //
  266. VOID WINAPI service_ctrl(DWORD dwCtrlCode)
  267. {
  268. // Handle the requested control code.
  269. //
  270. switch(dwCtrlCode)
  271. {
  272. // Stop the service.
  273. //
  274. // SERVICE_STOP_PENDING should be reported before
  275. // setting the Stop Event - hServerStopEvent - in
  276. // MNMServiceStop(). This avoids a race condition
  277. // which may result in a 1053 - The Service did not respond...
  278. // error.
  279. case SERVICE_CONTROL_STOP:
  280. ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 30000);
  281. MNMServiceStop();
  282. return;
  283. case SERVICE_CONTROL_SHUTDOWN:
  284. g_fInShutdown = TRUE;
  285. break;
  286. // Update the service status.
  287. //
  288. case SERVICE_CONTROL_INTERROGATE:
  289. break;
  290. case SERVICE_CONTROL_PAUSE:
  291. ReportStatusToSCMgr(SERVICE_PAUSE_PENDING, NO_ERROR, 30000);
  292. MNMServicePause();
  293. return;
  294. case SERVICE_CONTROL_CONTINUE:
  295. ReportStatusToSCMgr(SERVICE_CONTINUE_PENDING, NO_ERROR, 30000);
  296. MNMServiceContinue();
  297. return;
  298. default:
  299. break;
  300. }
  301. }
  302. //
  303. // FUNCTION: ReportStatusToSCMgr()
  304. //
  305. // PURPOSE: Sets the current status of the service and
  306. // reports it to the Service Control Manager
  307. //
  308. // PARAMETERS:
  309. // dwCurrentState - the state of the service
  310. // dwWin32ExitCode - error code to report
  311. // dwWaitHint - worst case estimate to next checkpoint
  312. //
  313. // RETURN VALUE:
  314. // TRUE - success
  315. // FALSE - failure
  316. //
  317. // COMMENTS:
  318. //
  319. BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
  320. DWORD dwWin32ExitCode,
  321. DWORD dwWaitHint)
  322. {
  323. static DWORD dwCheckPoint = 1;
  324. BOOL fResult = TRUE;
  325. #ifdef DEBUG
  326. if ( bDebug )
  327. return TRUE;
  328. #endif
  329. if ( IS_NT ) // when debugging we don't report to the SCM
  330. {
  331. switch ( dwCurrentState )
  332. {
  333. case SERVICE_START_PENDING:
  334. case SERVICE_STOP_PENDING:
  335. case SERVICE_CONTINUE_PENDING:
  336. case SERVICE_PAUSE_PENDING:
  337. break;
  338. case SERVICE_PAUSED:
  339. case SERVICE_STOPPED:
  340. case SERVICE_RUNNING:
  341. ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  342. SERVICE_ACCEPT_PAUSE_CONTINUE ;
  343. break;
  344. }
  345. ssStatus.dwCurrentState = dwCurrentState;
  346. ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  347. ssStatus.dwWaitHint = dwWaitHint;
  348. if ( ( dwCurrentState == SERVICE_RUNNING ) ||
  349. ( dwCurrentState == SERVICE_STOPPED ) ||
  350. ( dwCurrentState == SERVICE_PAUSED ))
  351. ssStatus.dwCheckPoint = 0;
  352. else
  353. ssStatus.dwCheckPoint = dwCheckPoint++;
  354. // Report the status of the service to the service control manager.
  355. //
  356. if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
  357. AddToMessageLog(EVENTLOG_ERROR_TYPE,
  358. 0,
  359. MSG_ERR_SERVICE,
  360. TEXT("SetServiceStatus"));
  361. }
  362. }
  363. return fResult;
  364. }
  365. ///////////////////////////////////////////////////////////////////
  366. //
  367. // The following code handles service installation and removal
  368. //
  369. //
  370. // FUNCTION: CmdInstallService()
  371. //
  372. // PURPOSE: Installs the service
  373. //
  374. // PARAMETERS:
  375. // none
  376. //
  377. // RETURN VALUE:
  378. // none
  379. //
  380. // COMMENTS:
  381. //
  382. void CmdInstallService()
  383. {
  384. SC_HANDLE schService;
  385. SC_HANDLE schSCManager;
  386. TCHAR szPath[MAX_PATH];
  387. TCHAR szSrvcDisplayName[MAX_PATH];
  388. if ( GetModuleFileName( NULL, szPath, MAX_PATH ) == 0 )
  389. {
  390. #ifdef DEBUG
  391. _tprintf(TEXT("Unable to install %s - %s\n"),
  392. TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
  393. #endif // DEBUG
  394. return;
  395. }
  396. schSCManager = OpenSCManager(
  397. NULL, // machine (NULL == local)
  398. NULL, // database (NULL == default)
  399. SC_MANAGER_ALL_ACCESS // access required
  400. );
  401. if ( schSCManager )
  402. {
  403. LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_TITLE,
  404. szSrvcDisplayName, CCHMAX(szSrvcDisplayName));
  405. schService = CreateService(
  406. schSCManager, // SCManager database
  407. TEXT(SZSERVICENAME), // name of service
  408. szSrvcDisplayName, // name to display
  409. SERVICE_ALL_ACCESS, // desired access
  410. SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
  411. // service type -- allow interaction with desktop
  412. SERVICE_DEMAND_START, // start type
  413. SERVICE_ERROR_NORMAL, // error control type
  414. szPath, // service's binary
  415. NULL, // no load ordering group
  416. NULL, // no tag identifier
  417. TEXT(SZDEPENDENCIES), // dependencies
  418. NULL, // LocalSystem account
  419. NULL); // no password
  420. if ( schService )
  421. {
  422. HINSTANCE hAdvApi;
  423. if ( IS_NT && ( hAdvApi = LoadLibrary ( "ADVAPI32.DLL" )))
  424. {
  425. #ifdef UNICODE
  426. #error "non-unicode assumption - entry point name"
  427. #endif // UNICODE
  428. if ( PFNCHANGESERVICECONFIG2 lpCSC =
  429. (PFNCHANGESERVICECONFIG2)GetProcAddress ( hAdvApi,
  430. "ChangeServiceConfig2A" ))
  431. {
  432. SERVICE_DESCRIPTION ServiceDescription;
  433. CHAR szDescription[1024]; // Calling A variant below
  434. LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_DESCRIPTION,
  435. szDescription, CCHMAX(szDescription));
  436. ServiceDescription.lpDescription = szDescription;
  437. (*lpCSC) ( schService,
  438. SERVICE_CONFIG_DESCRIPTION,
  439. (LPVOID) &ServiceDescription );
  440. }
  441. FreeLibrary ( hAdvApi );
  442. }
  443. #ifdef DEBUG
  444. _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  445. #endif // DEBUG
  446. CloseServiceHandle(schService);
  447. }
  448. else
  449. {
  450. if ( GetLastError() == ERROR_SERVICE_EXISTS )
  451. {
  452. schService = OpenService(schSCManager, TEXT(SZSERVICENAME),
  453. SERVICE_ALL_ACCESS);
  454. if (schService)
  455. {
  456. // try to stop the service
  457. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  458. {
  459. #ifdef DEBUG
  460. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  461. #endif // DEBUG
  462. Sleep( 1000 );
  463. while( QueryServiceStatus( schService, &ssStatus ) )
  464. {
  465. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  466. {
  467. #ifdef DEBUG
  468. _tprintf(TEXT("."));
  469. #endif // DEBUG
  470. Sleep( 1000 );
  471. }
  472. else
  473. break;
  474. }
  475. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  476. {
  477. #ifdef DEBUG
  478. _tprintf(TEXT("\n%s stopped.\n"),
  479. TEXT(SZSERVICEDISPLAYNAME) );
  480. #endif // DEBUG
  481. }
  482. else
  483. {
  484. #ifdef DEBUG
  485. _tprintf(TEXT("\n%s failed to stop.\n"),
  486. TEXT(SZSERVICEDISPLAYNAME) );
  487. #endif // DEBUG
  488. }
  489. }
  490. // now set manual startup
  491. if ( ChangeServiceConfig( schService, SERVICE_NO_CHANGE,
  492. SERVICE_DEMAND_START, SERVICE_NO_CHANGE,
  493. NULL, NULL, NULL, NULL, NULL, NULL, NULL))
  494. {
  495. #ifdef DEBUG
  496. _tprintf(TEXT("%s set to manual start.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  497. #endif //DEBUG
  498. }
  499. else
  500. {
  501. #ifdef DEBUG
  502. _tprintf(TEXT("ChangeServiceConfig failed - %s\n"), GetLastErrorText(szErr,256));
  503. #endif //DEBUG
  504. }
  505. CloseServiceHandle(schService);
  506. }
  507. else
  508. {
  509. #ifdef DEBUG
  510. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  511. #endif //DEBUG
  512. }
  513. }
  514. else
  515. {
  516. #ifdef DEBUG
  517. _tprintf(TEXT("CreateService failed - %s\n"),
  518. GetLastErrorText(szErr, 256));
  519. #endif // DEBUG
  520. }
  521. }
  522. CloseServiceHandle(schSCManager);
  523. }
  524. else
  525. {
  526. #ifdef DEBUG
  527. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  528. #endif //DEBUG
  529. }
  530. }
  531. //
  532. // FUNCTION: CmdRemoveService()
  533. //
  534. // PURPOSE: Stops and removes the service
  535. //
  536. // PARAMETERS:
  537. // none
  538. //
  539. // RETURN VALUE:
  540. // none
  541. //
  542. // COMMENTS:
  543. //
  544. void CmdRemoveService()
  545. {
  546. SC_HANDLE schService;
  547. SC_HANDLE schSCManager;
  548. schSCManager = OpenSCManager(
  549. NULL, // machine (NULL == local)
  550. NULL, // database (NULL == default)
  551. SC_MANAGER_ALL_ACCESS // access required
  552. );
  553. if ( schSCManager )
  554. {
  555. schService = OpenService(schSCManager, TEXT(SZSERVICENAME),
  556. SERVICE_ALL_ACCESS);
  557. if (schService)
  558. {
  559. // try to stop the service
  560. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  561. {
  562. #ifdef DEBUG
  563. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  564. #endif // DEBUG
  565. Sleep( 1000 );
  566. while( QueryServiceStatus( schService, &ssStatus ) )
  567. {
  568. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  569. {
  570. #ifdef DEBUG
  571. _tprintf(TEXT("."));
  572. #endif // DEBUG
  573. Sleep( 1000 );
  574. }
  575. else
  576. break;
  577. }
  578. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  579. {
  580. #ifdef DEBUG
  581. _tprintf(TEXT("\n%s stopped.\n"),
  582. TEXT(SZSERVICEDISPLAYNAME) );
  583. #endif // DEBUG
  584. }
  585. else
  586. {
  587. #ifdef DEBUG
  588. _tprintf(TEXT("\n%s failed to stop.\n"),
  589. TEXT(SZSERVICEDISPLAYNAME) );
  590. #endif // DEBUG
  591. }
  592. }
  593. // now remove the service
  594. if( DeleteService(schService) )
  595. {
  596. #ifdef DEBUG
  597. _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  598. #endif //DEBUG
  599. }
  600. else
  601. {
  602. #ifdef DEBUG
  603. _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
  604. #endif //DEBUG
  605. }
  606. CloseServiceHandle(schService);
  607. }
  608. else
  609. {
  610. #ifdef DEBUG
  611. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  612. #endif //DEBUG
  613. }
  614. CloseServiceHandle(schSCManager);
  615. }
  616. else
  617. {
  618. #ifdef DEBUG
  619. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  620. #endif //DEBUG
  621. }
  622. }
  623. #ifdef DEBUG
  624. ///////////////////////////////////////////////////////////////////
  625. //
  626. // The following code is for running the service as a console app
  627. //
  628. //
  629. // FUNCTION: CmdDebugService(int argc, char ** argv)
  630. //
  631. // PURPOSE: Runs the service as a console application
  632. //
  633. // PARAMETERS:
  634. // argc - number of command line arguments
  635. // argv - array of command line arguments
  636. //
  637. // RETURN VALUE:
  638. // none
  639. //
  640. // COMMENTS:
  641. //
  642. void CmdDebugService(int argc, char ** argv)
  643. {
  644. DWORD dwArgc;
  645. LPTSTR *lpszArgv;
  646. #ifdef UNICODE
  647. lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
  648. #else
  649. dwArgc = (DWORD) argc;
  650. lpszArgv = argv;
  651. #endif
  652. _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  653. SetConsoleCtrlHandler( ControlHandler, TRUE );
  654. MNMServiceStart( dwArgc, lpszArgv );
  655. }
  656. //
  657. // FUNCTION: ControlHandler ( DWORD dwCtrlType )
  658. //
  659. // PURPOSE: Handled console control events
  660. //
  661. // PARAMETERS:
  662. // dwCtrlType - type of control event
  663. //
  664. // RETURN VALUE:
  665. // True - handled
  666. // False - unhandled
  667. //
  668. // COMMENTS:
  669. //
  670. BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
  671. {
  672. switch( dwCtrlType )
  673. {
  674. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  675. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  676. _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  677. MNMServiceStop();
  678. return TRUE;
  679. break;
  680. }
  681. return FALSE;
  682. }
  683. //
  684. // FUNCTION: GetLastErrorText
  685. //
  686. // PURPOSE: copies error message text to string
  687. //
  688. // PARAMETERS:
  689. // lpszBuf - destination buffer
  690. // dwSize - size of buffer
  691. //
  692. // RETURN VALUE:
  693. // destination buffer
  694. //
  695. // COMMENTS:
  696. //
  697. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
  698. {
  699. DWORD dwRet;
  700. LPTSTR lpszTemp = NULL;
  701. dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  702. NULL,
  703. GetLastError(),
  704. LANG_NEUTRAL,
  705. (LPTSTR)&lpszTemp,
  706. 0,
  707. NULL );
  708. // supplied buffer is not long enough
  709. if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  710. lpszBuf[0] = TEXT('\0');
  711. else
  712. {
  713. lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
  714. _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  715. }
  716. if ( lpszTemp )
  717. LocalFree((HLOCAL) lpszTemp );
  718. return lpszBuf;
  719. }
  720. #endif // DEBUG