Leaked source code of windows server 2003
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.

822 lines
23 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. DWORD dwModuleFileName = GetModuleFileName( NULL, szPath, MAX_PATH - 1 );
  389. szPath[MAX_PATH-1] = 0;
  390. if (dwModuleFileName == 0 )
  391. {
  392. #ifdef DEBUG
  393. _tprintf(TEXT("Unable to install %s - %s\n"),
  394. TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
  395. #endif // DEBUG
  396. return;
  397. }
  398. schSCManager = OpenSCManager(
  399. NULL, // machine (NULL == local)
  400. NULL, // database (NULL == default)
  401. SC_MANAGER_ALL_ACCESS // access required
  402. );
  403. if ( schSCManager )
  404. {
  405. LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_TITLE,
  406. szSrvcDisplayName, CCHMAX(szSrvcDisplayName));
  407. schService = CreateService(
  408. schSCManager, // SCManager database
  409. TEXT(SZSERVICENAME), // name of service
  410. szSrvcDisplayName, // name to display
  411. SERVICE_ALL_ACCESS, // desired access
  412. SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
  413. // service type -- allow interaction with desktop
  414. SERVICE_DEMAND_START, // start type
  415. SERVICE_ERROR_NORMAL, // error control type
  416. szPath, // service's binary
  417. NULL, // no load ordering group
  418. NULL, // no tag identifier
  419. TEXT(SZDEPENDENCIES), // dependencies
  420. NULL, // LocalSystem account
  421. NULL); // no password
  422. if ( schService )
  423. {
  424. HINSTANCE hAdvApi;
  425. if ( IS_NT && ( hAdvApi = NmLoadLibrary ( "ADVAPI32.DLL" ,TRUE)))
  426. {
  427. #ifdef UNICODE
  428. #error "non-unicode assumption - entry point name"
  429. #endif // UNICODE
  430. if ( PFNCHANGESERVICECONFIG2 lpCSC =
  431. (PFNCHANGESERVICECONFIG2)GetProcAddress ( hAdvApi,
  432. "ChangeServiceConfig2A" ))
  433. {
  434. SERVICE_DESCRIPTION ServiceDescription;
  435. CHAR szDescription[1024]; // Calling A variant below
  436. LoadString(GetModuleHandle(NULL), IDS_MNMSRVC_DESCRIPTION,
  437. szDescription, CCHMAX(szDescription));
  438. ServiceDescription.lpDescription = szDescription;
  439. (*lpCSC) ( schService,
  440. SERVICE_CONFIG_DESCRIPTION,
  441. (LPVOID) &ServiceDescription );
  442. }
  443. FreeLibrary ( hAdvApi );
  444. }
  445. #ifdef DEBUG
  446. _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  447. #endif // DEBUG
  448. CloseServiceHandle(schService);
  449. }
  450. else
  451. {
  452. if ( GetLastError() == ERROR_SERVICE_EXISTS )
  453. {
  454. schService = OpenService(schSCManager, TEXT(SZSERVICENAME),
  455. SERVICE_ALL_ACCESS);
  456. if (schService)
  457. {
  458. // try to stop the service
  459. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  460. {
  461. #ifdef DEBUG
  462. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  463. #endif // DEBUG
  464. Sleep( 1000 );
  465. while( QueryServiceStatus( schService, &ssStatus ) )
  466. {
  467. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  468. {
  469. #ifdef DEBUG
  470. _tprintf(TEXT("."));
  471. #endif // DEBUG
  472. Sleep( 1000 );
  473. }
  474. else
  475. break;
  476. }
  477. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  478. {
  479. #ifdef DEBUG
  480. _tprintf(TEXT("\n%s stopped.\n"),
  481. TEXT(SZSERVICEDISPLAYNAME) );
  482. #endif // DEBUG
  483. }
  484. else
  485. {
  486. #ifdef DEBUG
  487. _tprintf(TEXT("\n%s failed to stop.\n"),
  488. TEXT(SZSERVICEDISPLAYNAME) );
  489. #endif // DEBUG
  490. }
  491. }
  492. // now set manual startup
  493. if ( ChangeServiceConfig( schService, SERVICE_NO_CHANGE,
  494. SERVICE_DEMAND_START, SERVICE_NO_CHANGE,
  495. NULL, NULL, NULL, NULL, NULL, NULL, NULL))
  496. {
  497. #ifdef DEBUG
  498. _tprintf(TEXT("%s set to manual start.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  499. #endif //DEBUG
  500. }
  501. else
  502. {
  503. #ifdef DEBUG
  504. _tprintf(TEXT("ChangeServiceConfig failed - %s\n"), GetLastErrorText(szErr,256));
  505. #endif //DEBUG
  506. }
  507. CloseServiceHandle(schService);
  508. }
  509. else
  510. {
  511. #ifdef DEBUG
  512. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  513. #endif //DEBUG
  514. }
  515. }
  516. else
  517. {
  518. #ifdef DEBUG
  519. _tprintf(TEXT("CreateService failed - %s\n"),
  520. GetLastErrorText(szErr, 256));
  521. #endif // DEBUG
  522. }
  523. }
  524. CloseServiceHandle(schSCManager);
  525. }
  526. else
  527. {
  528. #ifdef DEBUG
  529. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  530. #endif //DEBUG
  531. }
  532. }
  533. //
  534. // FUNCTION: CmdRemoveService()
  535. //
  536. // PURPOSE: Stops and removes the service
  537. //
  538. // PARAMETERS:
  539. // none
  540. //
  541. // RETURN VALUE:
  542. // none
  543. //
  544. // COMMENTS:
  545. //
  546. void CmdRemoveService()
  547. {
  548. SC_HANDLE schService;
  549. SC_HANDLE schSCManager;
  550. schSCManager = OpenSCManager(
  551. NULL, // machine (NULL == local)
  552. NULL, // database (NULL == default)
  553. SC_MANAGER_ALL_ACCESS // access required
  554. );
  555. if ( schSCManager )
  556. {
  557. schService = OpenService(schSCManager, TEXT(SZSERVICENAME),
  558. SERVICE_ALL_ACCESS);
  559. if (schService)
  560. {
  561. // try to stop the service
  562. if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  563. {
  564. #ifdef DEBUG
  565. _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  566. #endif // DEBUG
  567. Sleep( 1000 );
  568. while( QueryServiceStatus( schService, &ssStatus ) )
  569. {
  570. if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  571. {
  572. #ifdef DEBUG
  573. _tprintf(TEXT("."));
  574. #endif // DEBUG
  575. Sleep( 1000 );
  576. }
  577. else
  578. break;
  579. }
  580. if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  581. {
  582. #ifdef DEBUG
  583. _tprintf(TEXT("\n%s stopped.\n"),
  584. TEXT(SZSERVICEDISPLAYNAME) );
  585. #endif // DEBUG
  586. }
  587. else
  588. {
  589. #ifdef DEBUG
  590. _tprintf(TEXT("\n%s failed to stop.\n"),
  591. TEXT(SZSERVICEDISPLAYNAME) );
  592. #endif // DEBUG
  593. }
  594. }
  595. // now remove the service
  596. if( DeleteService(schService) )
  597. {
  598. #ifdef DEBUG
  599. _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  600. #endif //DEBUG
  601. }
  602. else
  603. {
  604. #ifdef DEBUG
  605. _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
  606. #endif //DEBUG
  607. }
  608. CloseServiceHandle(schService);
  609. }
  610. else
  611. {
  612. #ifdef DEBUG
  613. _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  614. #endif //DEBUG
  615. }
  616. CloseServiceHandle(schSCManager);
  617. }
  618. else
  619. {
  620. #ifdef DEBUG
  621. _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  622. #endif //DEBUG
  623. }
  624. }
  625. #ifdef DEBUG
  626. ///////////////////////////////////////////////////////////////////
  627. //
  628. // The following code is for running the service as a console app
  629. //
  630. //
  631. // FUNCTION: CmdDebugService(int argc, char ** argv)
  632. //
  633. // PURPOSE: Runs the service as a console application
  634. //
  635. // PARAMETERS:
  636. // argc - number of command line arguments
  637. // argv - array of command line arguments
  638. //
  639. // RETURN VALUE:
  640. // none
  641. //
  642. // COMMENTS:
  643. //
  644. void CmdDebugService(int argc, char ** argv)
  645. {
  646. DWORD dwArgc;
  647. LPTSTR *lpszArgv;
  648. #ifdef UNICODE
  649. lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
  650. #else
  651. dwArgc = (DWORD) argc;
  652. lpszArgv = argv;
  653. #endif
  654. _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  655. SetConsoleCtrlHandler( ControlHandler, TRUE );
  656. MNMServiceStart( dwArgc, lpszArgv );
  657. }
  658. //
  659. // FUNCTION: ControlHandler ( DWORD dwCtrlType )
  660. //
  661. // PURPOSE: Handled console control events
  662. //
  663. // PARAMETERS:
  664. // dwCtrlType - type of control event
  665. //
  666. // RETURN VALUE:
  667. // True - handled
  668. // False - unhandled
  669. //
  670. // COMMENTS:
  671. //
  672. BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
  673. {
  674. switch( dwCtrlType )
  675. {
  676. case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
  677. case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
  678. _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  679. MNMServiceStop();
  680. return TRUE;
  681. break;
  682. }
  683. return FALSE;
  684. }
  685. //
  686. // FUNCTION: GetLastErrorText
  687. //
  688. // PURPOSE: copies error message text to string
  689. //
  690. // PARAMETERS:
  691. // lpszBuf - destination buffer
  692. // dwSize - size of buffer
  693. //
  694. // RETURN VALUE:
  695. // destination buffer
  696. //
  697. // COMMENTS:
  698. //
  699. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
  700. {
  701. DWORD dwRet;
  702. LPTSTR lpszTemp = NULL;
  703. dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  704. NULL,
  705. GetLastError(),
  706. LANG_NEUTRAL,
  707. (LPTSTR)&lpszTemp,
  708. 0,
  709. NULL );
  710. // supplied buffer is not long enough
  711. if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  712. lpszBuf[0] = TEXT('\0');
  713. else
  714. {
  715. lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
  716. _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  717. }
  718. if ( lpszTemp )
  719. LocalFree((HLOCAL) lpszTemp );
  720. return lpszBuf;
  721. }
  722. #endif // DEBUG