home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winbase / winnt / service / service.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  16KB  |  590 lines

  1. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  2. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  3. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  4. // PARTICULAR PURPOSE.
  5. //
  6. // Copyright (C) 1993-1997  Microsoft Corporation.  All Rights Reserved.
  7. //
  8. //  MODULE:   service.c
  9. //
  10. //  PURPOSE:  Implements functions required by all services
  11. //            windows.
  12. //
  13. //  FUNCTIONS:
  14. //    main(int argc, char **argv);
  15. //    service_ctrl(DWORD dwCtrlCode);
  16. //    service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  17. //    CmdInstallService();
  18. //    CmdRemoveService();
  19. //    CmdDebugService(int argc, char **argv);
  20. //    ControlHandler ( DWORD dwCtrlType );
  21. //    GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  22. //
  23. //  COMMENTS:
  24. //
  25. //  AUTHOR: Craig Link - Microsoft Developer Support
  26. //
  27.  
  28.  
  29. #include <windows.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <process.h>
  33. #include <tchar.h>
  34.  
  35. #include "service.h"
  36.  
  37.  
  38.  
  39. // internal variables
  40. SERVICE_STATUS          ssStatus;       // current status of the service
  41. SERVICE_STATUS_HANDLE   sshStatusHandle;
  42. DWORD                   dwErr = 0;
  43. BOOL                    bDebug = FALSE;
  44. TCHAR                   szErr[256];
  45.  
  46. // internal function prototypes
  47. VOID WINAPI service_ctrl(DWORD dwCtrlCode);
  48. VOID WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv);
  49. VOID CmdInstallService();
  50. VOID CmdRemoveService();
  51. VOID CmdDebugService(int argc, char **argv);
  52. BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
  53. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
  54.  
  55. //
  56. //  FUNCTION: main
  57. //
  58. //  PURPOSE: entrypoint for service
  59. //
  60. //  PARAMETERS:
  61. //    argc - number of command line arguments
  62. //    argv - array of command line arguments
  63. //
  64. //  RETURN VALUE:
  65. //    none
  66. //
  67. //  COMMENTS:
  68. //    main() either performs the command line task, or
  69. //    call StartServiceCtrlDispatcher to register the
  70. //    main service thread.  When the this call returns,
  71. //    the service has stopped, so exit.
  72. //
  73. void _CRTAPI1 main(int argc, char **argv)
  74. {
  75.     SERVICE_TABLE_ENTRY dispatchTable[] =
  76.     {
  77.         { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION)service_main },
  78.         { NULL, NULL }
  79.     };
  80.  
  81.     if ( (argc > 1) &&
  82.          ((*argv[1] == '-') || (*argv[1] == '/')) )
  83.     {
  84.         if ( _stricmp( "install", argv[1]+1 ) == 0 )
  85.         {
  86.             CmdInstallService();
  87.         }
  88.         else if ( _stricmp( "remove", argv[1]+1 ) == 0 )
  89.         {
  90.             CmdRemoveService();
  91.         }
  92.         else if ( _stricmp( "debug", argv[1]+1 ) == 0 )
  93.         {
  94.             bDebug = TRUE;
  95.             CmdDebugService(argc, argv);
  96.         }
  97.         else
  98.         {
  99.             goto dispatch;
  100.         }
  101.         exit(0);
  102.     }
  103.  
  104.     // if it doesn't match any of the above parameters
  105.     // the service control manager may be starting the service
  106.     // so we must call StartServiceCtrlDispatcher
  107.     dispatch:
  108.         // this is just to be friendly
  109.         printf( "%s -install          to install the service\n", SZAPPNAME );
  110.         printf( "%s -remove           to remove the service\n", SZAPPNAME );
  111.         printf( "%s -debug <params>   to run as a console app for debugging\n", SZAPPNAME );
  112.         printf( "\nStartServiceCtrlDispatcher being called.\n" );
  113.         printf( "This may take several seconds.  Please wait.\n" );
  114.  
  115.         if (!StartServiceCtrlDispatcher(dispatchTable))
  116.             AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
  117. }
  118.  
  119.  
  120.  
  121. //
  122. //  FUNCTION: service_main
  123. //
  124. //  PURPOSE: To perform actual initialization of the service
  125. //
  126. //  PARAMETERS:
  127. //    dwArgc   - number of command line arguments
  128. //    lpszArgv - array of command line arguments
  129. //
  130. //  RETURN VALUE:
  131. //    none
  132. //
  133. //  COMMENTS:
  134. //    This routine performs the service initialization and then calls
  135. //    the user defined ServiceStart() routine to perform majority
  136. //    of the work.
  137. //
  138. void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
  139. {
  140.  
  141.     // register our service control handler:
  142.     //
  143.     sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), service_ctrl);
  144.  
  145.     if (!sshStatusHandle)
  146.         goto cleanup;
  147.  
  148.     // SERVICE_STATUS members that don't change in example
  149.     //
  150.     ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  151.     ssStatus.dwServiceSpecificExitCode = 0;
  152.  
  153.  
  154.     // report the status to the service control manager.
  155.     //
  156.     if (!ReportStatusToSCMgr(
  157.         SERVICE_START_PENDING, // service state
  158.         NO_ERROR,              // exit code
  159.         3000))                 // wait hint
  160.         goto cleanup;
  161.  
  162.  
  163.     ServiceStart( dwArgc, lpszArgv );
  164.  
  165. cleanup:
  166.  
  167.     // try to report the stopped status to the service control manager.
  168.     //
  169.     if (sshStatusHandle)
  170.         (VOID)ReportStatusToSCMgr(
  171.                             SERVICE_STOPPED,
  172.                             dwErr,
  173.                             0);
  174.  
  175.     return;
  176. }
  177.  
  178.  
  179.  
  180. //
  181. //  FUNCTION: service_ctrl
  182. //
  183. //  PURPOSE: This function is called by the SCM whenever
  184. //           ControlService() is called on this service.
  185. //
  186. //  PARAMETERS:
  187. //    dwCtrlCode - type of control requested
  188. //
  189. //  RETURN VALUE:
  190. //    none
  191. //
  192. //  COMMENTS:
  193. //
  194. VOID WINAPI service_ctrl(DWORD dwCtrlCode)
  195. {
  196.     // Handle the requested control code.
  197.     //
  198.     switch(dwCtrlCode)
  199.     {
  200.         // Stop the service.
  201.         //
  202.         // SERVICE_STOP_PENDING should be reported before
  203.         // setting the Stop Event - hServerStopEvent - in
  204.         // ServiceStop().  This avoids a race condition
  205.         // which may result in a 1053 - The Service did not respond...
  206.         // error.
  207.         case SERVICE_CONTROL_STOP:
  208.             ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 0);
  209.             ServiceStop();
  210.             return;
  211.  
  212.         // Update the service status.
  213.         //
  214.         case SERVICE_CONTROL_INTERROGATE:
  215.             break;
  216.  
  217.         // invalid control code
  218.         //
  219.         default:
  220.             break;
  221.  
  222.     }
  223.  
  224.     ReportStatusToSCMgr(ssStatus.dwCurrentState, NO_ERROR, 0);
  225. }
  226.  
  227.  
  228.  
  229. //
  230. //  FUNCTION: ReportStatusToSCMgr()
  231. //
  232. //  PURPOSE: Sets the current status of the service and
  233. //           reports it to the Service Control Manager
  234. //
  235. //  PARAMETERS:
  236. //    dwCurrentState - the state of the service
  237. //    dwWin32ExitCode - error code to report
  238. //    dwWaitHint - worst case estimate to next checkpoint
  239. //
  240. //  RETURN VALUE:
  241. //    TRUE  - success
  242. //    FALSE - failure
  243. //
  244. //  COMMENTS:
  245. //
  246. BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
  247.                          DWORD dwWin32ExitCode,
  248.                          DWORD dwWaitHint)
  249. {
  250.     static DWORD dwCheckPoint = 1;
  251.     BOOL fResult = TRUE;
  252.  
  253.  
  254.     if ( !bDebug ) // when debugging we don't report to the SCM
  255.     {
  256.         if (dwCurrentState == SERVICE_START_PENDING)
  257.             ssStatus.dwControlsAccepted = 0;
  258.         else
  259.             ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  260.  
  261.         ssStatus.dwCurrentState = dwCurrentState;
  262.         ssStatus.dwWin32ExitCode = dwWin32ExitCode;
  263.         ssStatus.dwWaitHint = dwWaitHint;
  264.  
  265.         if ( ( dwCurrentState == SERVICE_RUNNING ) ||
  266.              ( dwCurrentState == SERVICE_STOPPED ) )
  267.             ssStatus.dwCheckPoint = 0;
  268.         else
  269.             ssStatus.dwCheckPoint = dwCheckPoint++;
  270.  
  271.  
  272.         // Report the status of the service to the service control manager.
  273.         //
  274.         if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
  275.             AddToMessageLog(TEXT("SetServiceStatus"));
  276.         }
  277.     }
  278.     return fResult;
  279. }
  280.  
  281.  
  282.  
  283. //
  284. //  FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
  285. //
  286. //  PURPOSE: Allows any thread to log an error message
  287. //
  288. //  PARAMETERS:
  289. //    lpszMsg - text for message
  290. //
  291. //  RETURN VALUE:
  292. //    none
  293. //
  294. //  COMMENTS:
  295. //
  296. VOID AddToMessageLog(LPTSTR lpszMsg)
  297. {
  298.     TCHAR   szMsg[256];
  299.     HANDLE  hEventSource;
  300.     LPTSTR  lpszStrings[2];
  301.  
  302.  
  303.     if ( !bDebug )
  304.     {
  305.         dwErr = GetLastError();
  306.  
  307.         // Use event logging to log the error.
  308.         //
  309.         hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
  310.  
  311.         _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
  312.         lpszStrings[0] = szMsg;
  313.         lpszStrings[1] = lpszMsg;
  314.  
  315.         if (hEventSource != NULL) {
  316.             ReportEvent(hEventSource, // handle of event source
  317.                 EVENTLOG_ERROR_TYPE,  // event type
  318.                 0,                    // event category
  319.                 0,                    // event ID
  320.                 NULL,                 // current user's SID
  321.                 2,                    // strings in lpszStrings
  322.                 0,                    // no bytes of raw data
  323.                 lpszStrings,          // array of error strings
  324.                 NULL);                // no raw data
  325.  
  326.             (VOID) DeregisterEventSource(hEventSource);
  327.         }
  328.     }
  329. }
  330.  
  331.  
  332.  
  333.  
  334. ///////////////////////////////////////////////////////////////////
  335. //
  336. //  The following code handles service installation and removal
  337. //
  338.  
  339.  
  340. //
  341. //  FUNCTION: CmdInstallService()
  342. //
  343. //  PURPOSE: Installs the service
  344. //
  345. //  PARAMETERS:
  346. //    none
  347. //
  348. //  RETURN VALUE:
  349. //    none
  350. //
  351. //  COMMENTS:
  352. //
  353. void CmdInstallService()
  354. {
  355.     SC_HANDLE   schService;
  356.     SC_HANDLE   schSCManager;
  357.  
  358.     TCHAR szPath[512];
  359.  
  360.     if ( GetModuleFileName( NULL, szPath, 512 ) == 0 )
  361.     {
  362.         _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
  363.         return;
  364.     }
  365.  
  366.     schSCManager = OpenSCManager(
  367.                         NULL,                   // machine (NULL == local)
  368.                         NULL,                   // database (NULL == default)
  369.                         SC_MANAGER_ALL_ACCESS   // access required
  370.                         );
  371.     if ( schSCManager )
  372.     {
  373.         schService = CreateService(
  374.             schSCManager,               // SCManager database
  375.             TEXT(SZSERVICENAME),        // name of service
  376.             TEXT(SZSERVICEDISPLAYNAME), // name to display
  377.             SERVICE_ALL_ACCESS,         // desired access
  378.             SERVICE_WIN32_OWN_PROCESS,  // service type
  379.             SERVICE_DEMAND_START,       // start type
  380.             SERVICE_ERROR_NORMAL,       // error control type
  381.             szPath,                     // service's binary
  382.             NULL,                       // no load ordering group
  383.             NULL,                       // no tag identifier
  384.             TEXT(SZDEPENDENCIES),       // dependencies
  385.             NULL,                       // LocalSystem account
  386.             NULL);                      // no password
  387.  
  388.         if ( schService )
  389.         {
  390.             _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  391.             CloseServiceHandle(schService);
  392.         }
  393.         else
  394.         {
  395.             _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
  396.         }
  397.  
  398.         CloseServiceHandle(schSCManager);
  399.     }
  400.     else
  401.         _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  402. }
  403.  
  404.  
  405.  
  406. //
  407. //  FUNCTION: CmdRemoveService()
  408. //
  409. //  PURPOSE: Stops and removes the service
  410. //
  411. //  PARAMETERS:
  412. //    none
  413. //
  414. //  RETURN VALUE:
  415. //    none
  416. //
  417. //  COMMENTS:
  418. //
  419. void CmdRemoveService()
  420. {
  421.     SC_HANDLE   schService;
  422.     SC_HANDLE   schSCManager;
  423.  
  424.     schSCManager = OpenSCManager(
  425.                         NULL,                   // machine (NULL == local)
  426.                         NULL,                   // database (NULL == default)
  427.                         SC_MANAGER_ALL_ACCESS   // access required
  428.                         );
  429.     if ( schSCManager )
  430.     {
  431.         schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
  432.  
  433.         if (schService)
  434.         {
  435.             // try to stop the service
  436.             if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
  437.             {
  438.                 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
  439.                 Sleep( 1000 );
  440.  
  441.                 while( QueryServiceStatus( schService, &ssStatus ) )
  442.                 {
  443.                     if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
  444.                     {
  445.                         _tprintf(TEXT("."));
  446.                         Sleep( 1000 );
  447.                     }
  448.                     else
  449.                         break;
  450.                 }
  451.  
  452.                 if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
  453.                     _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  454.                 else
  455.                     _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  456.  
  457.             }
  458.  
  459.             // now remove the service
  460.             if( DeleteService(schService) )
  461.                 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
  462.             else
  463.                 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
  464.  
  465.  
  466.             CloseServiceHandle(schService);
  467.         }
  468.         else
  469.             _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
  470.  
  471.         CloseServiceHandle(schSCManager);
  472.     }
  473.     else
  474.         _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
  475. }
  476.  
  477.  
  478.  
  479.  
  480. ///////////////////////////////////////////////////////////////////
  481. //
  482. //  The following code is for running the service as a console app
  483. //
  484.  
  485.  
  486. //
  487. //  FUNCTION: CmdDebugService(int argc, char ** argv)
  488. //
  489. //  PURPOSE: Runs the service as a console application
  490. //
  491. //  PARAMETERS:
  492. //    argc - number of command line arguments
  493. //    argv - array of command line arguments
  494. //
  495. //  RETURN VALUE:
  496. //    none
  497. //
  498. //  COMMENTS:
  499. //
  500. void CmdDebugService(int argc, char ** argv)
  501. {
  502.     DWORD dwArgc;
  503.     LPTSTR *lpszArgv;
  504.  
  505. #ifdef UNICODE
  506.     lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
  507. #else
  508.     dwArgc   = (DWORD) argc;
  509.     lpszArgv = argv;
  510. #endif
  511.  
  512.     _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  513.  
  514.     SetConsoleCtrlHandler( ControlHandler, TRUE );
  515.  
  516.     ServiceStart( dwArgc, lpszArgv );
  517. }
  518.  
  519.  
  520. //
  521. //  FUNCTION: ControlHandler ( DWORD dwCtrlType )
  522. //
  523. //  PURPOSE: Handled console control events
  524. //
  525. //  PARAMETERS:
  526. //    dwCtrlType - type of control event
  527. //
  528. //  RETURN VALUE:
  529. //    True - handled
  530. //    False - unhandled
  531. //
  532. //  COMMENTS:
  533. //
  534. BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
  535. {
  536.     switch( dwCtrlType )
  537.     {
  538.         case CTRL_BREAK_EVENT:  // use Ctrl+C or Ctrl+Break to simulate
  539.         case CTRL_C_EVENT:      // SERVICE_CONTROL_STOP in debug mode
  540.             _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
  541.             ServiceStop();
  542.             return TRUE;
  543.             break;
  544.  
  545.     }
  546.     return FALSE;
  547. }
  548.  
  549. //
  550. //  FUNCTION: GetLastErrorText
  551. //
  552. //  PURPOSE: copies error message text to string
  553. //
  554. //  PARAMETERS:
  555. //    lpszBuf - destination buffer
  556. //    dwSize - size of buffer
  557. //
  558. //  RETURN VALUE:
  559. //    destination buffer
  560. //
  561. //  COMMENTS:
  562. //
  563. LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
  564. {
  565.     DWORD dwRet;
  566.     LPTSTR lpszTemp = NULL;
  567.  
  568.     dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
  569.                            NULL,
  570.                            GetLastError(),
  571.                            LANG_NEUTRAL,
  572.                            (LPTSTR)&lpszTemp,
  573.                            0,
  574.                            NULL );
  575.  
  576.     // supplied buffer is not long enough
  577.     if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
  578.         lpszBuf[0] = TEXT('\0');
  579.     else
  580.     {
  581.         lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0');  //remove cr and newline character
  582.         _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
  583.     }
  584.  
  585.     if ( lpszTemp )
  586.         LocalFree((HLOCAL) lpszTemp );
  587.  
  588.     return lpszBuf;
  589. }
  590.