1 /*---------------------------------------------------------------------------
2 THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3 ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
4 TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
7 Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
11 PURPOSE: Implements functions required by all Windows NT services
14 main(int argc, char **argv);
15 service_ctrl(DWORD dwCtrlCode);
16 service_main(DWORD dwArgc, LPTSTR *lpszArgv);
20 CmdDebugService(int argc, char **argv);
21 ControlHandler ( DWORD dwCtrlType );
22 GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
24 ---------------------------------------------------------------------------*/
35 SERVICE_STATUS ssStatus
; // current status of the service
36 SERVICE_STATUS_HANDLE sshStatusHandle
;
41 // internal function prototypes
42 VOID WINAPI
service_ctrl(DWORD dwCtrlCode
);
43 VOID WINAPI
service_main(DWORD dwArgc
, LPTSTR
*lpszArgv
);
44 int CmdInstallService();
45 int CmdRemoveService();
46 int CmdStartService();
47 VOID
CmdDebugService(int argc
, char **argv
);
48 BOOL WINAPI
ControlHandler ( DWORD dwCtrlType
);
49 LPTSTR
GetLastErrorText( LPTSTR lpszBuf
, DWORD dwSize
);
54 // PURPOSE: entrypoint for service
57 // argc - number of command line arguments
58 // argv - array of command line arguments
64 // main() either performs the command line task, or
65 // call StartServiceCtrlDispatcher to register the
66 // main service thread. When the this call returns,
67 // the service has stopped, so exit.
69 int __cdecl
main(int argc
, char **argv
)
71 SERVICE_TABLE_ENTRY dispatchTable
[] =
73 { TEXT(SZSERVICENAME
), (LPSERVICE_MAIN_FUNCTION
)service_main
},
78 ((*argv
[1] == '-') || (*argv
[1] == '/')) )
80 if ( _stricmp( "install", argv
[1]+1 ) == 0 )
82 return CmdInstallService();
84 else if ( _stricmp( "remove", argv
[1]+1 ) == 0 )
86 return CmdRemoveService();
88 else if ( _stricmp( "start", argv
[1]+1 ) == 0)
90 return CmdStartService();
92 else if ( _stricmp( "debug", argv
[1]+1 ) == 0 )
95 CmdDebugService(argc
, argv
);
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
108 // this is just to be friendly
109 printf( "%s -install to install the service\n", SZAPPNAME
);
110 printf( "%s -start to start the service\n", SZAPPNAME
);
111 printf( "%s -remove to remove the service\n", SZAPPNAME
);
112 printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME
);
113 printf( "\nStartServiceCtrlDispatcher being called.\n" );
114 printf( "This may take several seconds. Please wait.\n" );
116 if (!StartServiceCtrlDispatcher(dispatchTable
))
117 AddToMessageLog(MSG_FLAGS_ERROR
, TEXT("StartServiceCtrlDispatcher failed."));
125 // FUNCTION: service_main
127 // PURPOSE: To perform actual initialization of the service
130 // dwArgc - number of command line arguments
131 // lpszArgv - array of command line arguments
137 // This routine performs the service initialization and then calls
138 // the user defined ServiceStart() routine to perform majority
141 void WINAPI
service_main(DWORD dwArgc
, LPTSTR
*lpszArgv
)
144 // register our service control handler:
146 sshStatusHandle
= RegisterServiceCtrlHandler( TEXT(SZSERVICENAME
), service_ctrl
);
148 if (!sshStatusHandle
)
151 // SERVICE_STATUS members that don't change in example
153 ssStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
154 ssStatus
.dwServiceSpecificExitCode
= 0;
157 // report the status to the service control manager.
159 if (!ReportStatusToSCMgr(
160 SERVICE_START_PENDING
, // service state
161 NO_ERROR
, // exit code
166 ServiceStart( dwArgc
, lpszArgv
);
170 // try to report the stopped status to the service control manager.
173 (VOID
)ReportStatusToSCMgr(
184 // FUNCTION: service_ctrl
186 // PURPOSE: This function is called by the SCM whenever
187 // ControlService() is called on this service.
190 // dwCtrlCode - type of control requested
197 VOID WINAPI
service_ctrl(DWORD dwCtrlCode
)
199 // Handle the requested control code.
205 // SERVICE_STOP_PENDING should be reported before
206 // setting the Stop Event - hServerStopEvent - in
207 // ServiceStop(). This avoids a race condition
208 // which may result in a 1053 - The Service did not respond...
210 case SERVICE_CONTROL_STOP
:
211 ReportStatusToSCMgr(SERVICE_STOP_PENDING
, NO_ERROR
, 0);
215 // Update the service status.
217 case SERVICE_CONTROL_INTERROGATE
:
220 // invalid control code
227 ReportStatusToSCMgr(ssStatus
.dwCurrentState
, NO_ERROR
, 0);
233 // FUNCTION: ReportStatusToSCMgr()
235 // PURPOSE: Sets the current status of the service and
236 // reports it to the Service Control Manager
239 // dwCurrentState - the state of the service
240 // dwWin32ExitCode - error code to report
241 // dwWaitHint - worst case estimate to next checkpoint
249 BOOL
ReportStatusToSCMgr(DWORD dwCurrentState
,
250 DWORD dwWin32ExitCode
,
253 static DWORD dwCheckPoint
= 1;
257 if ( !bDebug
) // when debugging we don't report to the SCM
259 if (dwCurrentState
== SERVICE_START_PENDING
)
260 ssStatus
.dwControlsAccepted
= 0;
262 ssStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
264 ssStatus
.dwCurrentState
= dwCurrentState
;
265 ssStatus
.dwWin32ExitCode
= dwWin32ExitCode
;
266 ssStatus
.dwWaitHint
= dwWaitHint
;
268 if ( ( dwCurrentState
== SERVICE_RUNNING
) ||
269 ( dwCurrentState
== SERVICE_STOPPED
) )
270 ssStatus
.dwCheckPoint
= 0;
272 ssStatus
.dwCheckPoint
= dwCheckPoint
++;
275 // Report the status of the service to the service control manager.
277 if (!(fResult
= SetServiceStatus( sshStatusHandle
, &ssStatus
)))
279 AddToMessageLog(MSG_FLAGS_ERROR
, TEXT("SetServiceStatus"));
288 // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
290 // PURPOSE: Allows any thread to log an error message
293 // lpszMsg - text for message
300 void AddToMessageLog(DWORD flags
, LPTSTR lpszMsg
)
302 TCHAR szMsg
[(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100 ];
304 LPCSTR lpszStrings
[2];
308 if (flags
& MSG_FLAGS_SYS_CODE
)
309 dwErr
= GetLastError();
313 // Use event logging to log the error.
315 hEventSource
= RegisterEventSource(NULL
, TEXT(SZSERVICENAME
));
317 _stprintf(szMsg
, TEXT("%s error: %d"), TEXT(SZSERVICENAME
), (int)dwErr
);
318 lpszStrings
[0] = szMsg
;
319 lpszStrings
[1] = lpszMsg
;
321 if (hEventSource
!= NULL
)
323 ReportEvent(hEventSource
, // handle of event source
325 (flags
& MSG_FLAGS_ERROR
)
326 ? EVENTLOG_ERROR_TYPE
: EVENTLOG_INFORMATION_TYPE
,
329 NULL
, // current user's SID
330 2, // strings in lpszStrings
331 0, // no bytes of raw data
332 lpszStrings
, // array of error strings
333 NULL
); // no raw data
335 (VOID
) DeregisterEventSource(hEventSource
);
340 void ResetError (void)
345 ///////////////////////////////////////////////////////////////////
347 // The following code handles service installation and removal
352 // FUNCTION: CmdInstallService()
354 // PURPOSE: Installs the service
364 int CmdInstallService()
366 SC_HANDLE schService
;
367 SC_HANDLE schSCManager
;
373 if ( GetModuleFileName( NULL
, szPath
, 512 ) == 0 )
375 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME
), GetLastErrorText(szErr
, 256));
379 schSCManager
= OpenSCManager(
380 NULL
, // machine (NULL == local)
381 NULL
, // database (NULL == default)
382 SC_MANAGER_CONNECT
| SC_MANAGER_CREATE_SERVICE
// access required
386 schService
= CreateService(
387 schSCManager
, // SCManager database
388 TEXT(SZSERVICENAME
), // name of service
389 TEXT(SZSERVICEDISPLAYNAME
), // name to display
390 SERVICE_QUERY_STATUS
, // desired access
391 SERVICE_WIN32_OWN_PROCESS
, // service type
392 SERVICE_DEMAND_START
, // start type -- alternative: SERVICE_AUTO_START
393 SERVICE_ERROR_NORMAL
, // error control type
394 szPath
, // service's binary
395 NULL
, // no load ordering group
396 NULL
, // no tag identifier
397 TEXT(SZDEPENDENCIES
), // dependencies
398 NULL
, // LocalSystem account
399 NULL
); // no password
403 _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
404 CloseServiceHandle(schService
);
408 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr
, 256));
412 CloseServiceHandle(schSCManager
);
416 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
423 // FUNCTION: CmdStartService()
425 // PURPOSE: Start the service
435 int CmdStartService()
439 SC_HANDLE schSCManager
;
440 SC_HANDLE schService
;
443 // Open a handle to the SC Manager database.
444 schSCManager
= OpenSCManager(
445 NULL
, // local machine
446 NULL
, // ServicesActive database
447 SC_MANAGER_ALL_ACCESS
); // full access rights
449 if (NULL
== schSCManager
) {
450 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
454 schService
= OpenService(
455 schSCManager
, // SCM database
456 SZSERVICENAME
, // service name
459 if (schService
== NULL
) {
460 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr
,256));
465 schService
, // handle to service
466 0, // number of arguments
467 NULL
) ) // no arguments
469 _tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr
,256));
474 _tprintf(TEXT("Service Started\n"));
477 CloseServiceHandle(schService
);
478 CloseServiceHandle(schSCManager
);
483 // FUNCTION: CmdRemoveService()
485 // PURPOSE: Stops and removes the service
495 int CmdRemoveService()
497 SC_HANDLE schService
;
498 SC_HANDLE schSCManager
;
502 schSCManager
= OpenSCManager(
503 NULL
, // machine (NULL == local)
504 NULL
, // database (NULL == default)
505 SC_MANAGER_CONNECT
// access required
509 schService
= OpenService(schSCManager
, TEXT(SZSERVICENAME
), DELETE
| SERVICE_STOP
| SERVICE_QUERY_STATUS
);
513 // try to stop the service
514 if ( ControlService( schService
, SERVICE_CONTROL_STOP
, &ssStatus
) )
516 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME
));
519 while ( QueryServiceStatus( schService
, &ssStatus
) )
521 if ( ssStatus
.dwCurrentState
== SERVICE_STOP_PENDING
)
530 if ( ssStatus
.dwCurrentState
== SERVICE_STOPPED
)
531 _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
534 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
540 // now remove the service
541 if ( DeleteService(schService
) )
542 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
545 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr
,256));
550 CloseServiceHandle(schService
);
554 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr
,256));
558 CloseServiceHandle(schSCManager
);
562 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
571 ///////////////////////////////////////////////////////////////////
573 // The following code is for running the service as a console app
578 // FUNCTION: CmdDebugService(int argc, char ** argv)
580 // PURPOSE: Runs the service as a console application
583 // argc - number of command line arguments
584 // argv - array of command line arguments
591 void CmdDebugService(int argc
, char ** argv
)
597 lpszArgv
= CommandLineToArgvW(GetCommandLineW(), &(dwArgc
) );
598 if (NULL
== lpszArgv
)
600 // CommandLineToArvW failed!!
601 _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
605 dwArgc
= (DWORD
) argc
;
609 _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
611 SetConsoleCtrlHandler( ControlHandler
, TRUE
);
613 ServiceStart( dwArgc
, lpszArgv
);
616 // Must free memory allocated for arguments
618 GlobalFree(lpszArgv
);
625 // FUNCTION: ControlHandler ( DWORD dwCtrlType )
627 // PURPOSE: Handled console control events
630 // dwCtrlType - type of control event
638 BOOL WINAPI
ControlHandler ( DWORD dwCtrlType
)
640 switch ( dwCtrlType
)
642 case CTRL_BREAK_EVENT
: // use Ctrl+C or Ctrl+Break to simulate
643 case CTRL_C_EVENT
: // SERVICE_CONTROL_STOP in debug mode
644 _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
654 // FUNCTION: GetLastErrorText
656 // PURPOSE: copies error message text to string
659 // lpszBuf - destination buffer
660 // dwSize - size of buffer
663 // destination buffer
667 LPTSTR
GetLastErrorText( LPTSTR lpszBuf
, DWORD dwSize
)
670 LPTSTR lpszTemp
= NULL
;
672 dwRet
= FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
680 // supplied buffer is not long enough
681 if ( !dwRet
|| ( (long)dwSize
< (long)dwRet
+14 ) )
682 lpszBuf
[0] = TEXT('\0');
685 lpszTemp
[lstrlen(lpszTemp
)-2] = TEXT('\0'); //remove cr and newline character
686 _stprintf( lpszBuf
, TEXT("%s (0x%x)"), lpszTemp
, (int)GetLastError() );
690 LocalFree((HLOCAL
) lpszTemp
);