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+1, 510 ) == 0 )
375
_tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
379
strcat(szPath, "\"");
381
schSCManager = OpenSCManager(
382
NULL, // machine (NULL == local)
383
NULL, // database (NULL == default)
384
SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE // access required
388
schService = CreateService(
389
schSCManager, // SCManager database
390
TEXT(SZSERVICENAME), // name of service
391
TEXT(SZSERVICEDISPLAYNAME), // name to display
392
SERVICE_QUERY_STATUS, // desired access
393
SERVICE_WIN32_OWN_PROCESS, // service type
394
SERVICE_DEMAND_START, // start type -- alternative: SERVICE_AUTO_START
395
SERVICE_ERROR_NORMAL, // error control type
396
szPath, // service's binary
397
NULL, // no load ordering group
398
NULL, // no tag identifier
399
TEXT(SZDEPENDENCIES), // dependencies
400
NULL, // LocalSystem account
401
NULL); // no password
405
_tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
406
CloseServiceHandle(schService);
410
_tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
414
CloseServiceHandle(schSCManager);
418
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
425
// FUNCTION: CmdStartService()
427
// PURPOSE: Start the service
437
int CmdStartService()
441
SC_HANDLE schSCManager;
442
SC_HANDLE schService;
445
// Open a handle to the SC Manager database.
446
schSCManager = OpenSCManager(
447
NULL, // local machine
448
NULL, // ServicesActive database
449
SC_MANAGER_ALL_ACCESS); // full access rights
451
if (NULL == schSCManager) {
452
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
456
schService = OpenService(
457
schSCManager, // SCM database
458
SZSERVICENAME, // service name
461
if (schService == NULL) {
462
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
467
schService, // handle to service
468
0, // number of arguments
469
NULL) ) // no arguments
471
_tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText(szErr,256));
476
_tprintf(TEXT("Service Started\n"));
479
CloseServiceHandle(schService);
480
CloseServiceHandle(schSCManager);
485
// FUNCTION: CmdRemoveService()
487
// PURPOSE: Stops and removes the service
497
int CmdRemoveService()
499
SC_HANDLE schService;
500
SC_HANDLE schSCManager;
504
schSCManager = OpenSCManager(
505
NULL, // machine (NULL == local)
506
NULL, // database (NULL == default)
507
SC_MANAGER_CONNECT // access required
511
schService = OpenService(schSCManager, TEXT(SZSERVICENAME), DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
515
// try to stop the service
516
if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) )
518
_tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
521
while ( QueryServiceStatus( schService, &ssStatus ) )
523
if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING )
532
if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
533
_tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
536
_tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
542
// now remove the service
543
if ( DeleteService(schService) )
544
_tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
547
_tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
552
CloseServiceHandle(schService);
556
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
560
CloseServiceHandle(schSCManager);
564
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
573
///////////////////////////////////////////////////////////////////
575
// The following code is for running the service as a console app
580
// FUNCTION: CmdDebugService(int argc, char ** argv)
582
// PURPOSE: Runs the service as a console application
585
// argc - number of command line arguments
586
// argv - array of command line arguments
593
void CmdDebugService(int argc, char ** argv)
599
lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
600
if (NULL == lpszArgv)
602
// CommandLineToArvW failed!!
603
_tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
607
dwArgc = (DWORD) argc;
611
_tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
613
SetConsoleCtrlHandler( ControlHandler, TRUE );
615
ServiceStart( dwArgc, lpszArgv );
618
// Must free memory allocated for arguments
620
GlobalFree(lpszArgv);
627
// FUNCTION: ControlHandler ( DWORD dwCtrlType )
629
// PURPOSE: Handled console control events
632
// dwCtrlType - type of control event
640
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
642
switch ( dwCtrlType )
644
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
645
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
646
_tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
656
// FUNCTION: GetLastErrorText
658
// PURPOSE: copies error message text to string
661
// lpszBuf - destination buffer
662
// dwSize - size of buffer
665
// destination buffer
669
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
672
LPTSTR lpszTemp = NULL;
674
dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
682
// supplied buffer is not long enough
683
if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
684
lpszBuf[0] = TEXT('\0');
687
lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
688
_stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, (int)GetLastError() );
692
LocalFree((HLOCAL) lpszTemp );