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) 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);
19 CmdDebugService(int argc, char **argv);
20 ControlHandler ( DWORD dwCtrlType );
21 GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
23 ---------------------------------------------------------------------------*/
25 #ifndef STANDALONE_NFSD
34 SERVICE_STATUS ssStatus
; // current status of the service
35 SERVICE_STATUS_HANDLE sshStatusHandle
;
40 // internal function prototypes
41 VOID WINAPI
service_ctrl(DWORD dwCtrlCode
);
42 VOID WINAPI
service_main(DWORD dwArgc
, LPTSTR
*lpszArgv
);
43 VOID
CmdInstallService();
44 VOID
CmdRemoveService();
45 VOID
CmdDebugService(int argc
, char **argv
);
46 BOOL WINAPI
ControlHandler ( DWORD dwCtrlType
);
47 LPTSTR
GetLastErrorText( LPTSTR lpszBuf
, DWORD dwSize
);
52 // PURPOSE: entrypoint for service
55 // argc - number of command line arguments
56 // argv - array of command line arguments
62 // main() either performs the command line task, or
63 // call StartServiceCtrlDispatcher to register the
64 // main service thread. When the this call returns,
65 // the service has stopped, so exit.
67 void __cdecl
main(int argc
, char **argv
)
69 SERVICE_TABLE_ENTRY dispatchTable
[] =
71 { TEXT(SZSERVICENAME
), (LPSERVICE_MAIN_FUNCTION
)service_main
},
76 ((*argv
[1] == '-') || (*argv
[1] == '/')) )
78 if ( _stricmp( "install", argv
[1]+1 ) == 0 )
82 else if ( _stricmp( "remove", argv
[1]+1 ) == 0 )
86 else if ( _stricmp( "debug", argv
[1]+1 ) == 0 )
89 CmdDebugService(argc
, argv
);
98 // if it doesn't match any of the above parameters
99 // the service control manager may be starting the service
100 // so we must call StartServiceCtrlDispatcher
102 // this is just to be friendly
103 printf( "%s -install to install the service\n", SZAPPNAME
);
104 printf( "%s -remove to remove the service\n", SZAPPNAME
);
105 printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME
);
106 printf( "\nStartServiceCtrlDispatcher being called.\n" );
107 printf( "This may take several seconds. Please wait.\n" );
109 if (!StartServiceCtrlDispatcher(dispatchTable
))
110 AddToMessageLog(TEXT("StartServiceCtrlDispatcher failed."));
116 // FUNCTION: service_main
118 // PURPOSE: To perform actual initialization of the service
121 // dwArgc - number of command line arguments
122 // lpszArgv - array of command line arguments
128 // This routine performs the service initialization and then calls
129 // the user defined ServiceStart() routine to perform majority
132 void WINAPI
service_main(DWORD dwArgc
, LPTSTR
*lpszArgv
)
135 // register our service control handler:
137 sshStatusHandle
= RegisterServiceCtrlHandler( TEXT(SZSERVICENAME
), service_ctrl
);
139 if (!sshStatusHandle
)
142 DbgPrint("Starting service\n");
144 // SERVICE_STATUS members that don't change in example
146 ssStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
147 ssStatus
.dwServiceSpecificExitCode
= 0;
150 // report the status to the service control manager.
152 if (!ReportStatusToSCMgr(
153 SERVICE_START_PENDING
, // service state
154 NO_ERROR
, // exit code
158 DbgPrint("Starting service 2\n");
160 SetConsoleCtrlHandler( ControlHandler
, TRUE
);
162 ServiceStart( dwArgc
, lpszArgv
);
168 // try to report the stopped status to the service control manager.
171 (VOID
)ReportStatusToSCMgr(
182 // FUNCTION: service_ctrl
184 // PURPOSE: This function is called by the SCM whenever
185 // ControlService() is called on this service.
188 // dwCtrlCode - type of control requested
195 VOID WINAPI
service_ctrl(DWORD dwCtrlCode
)
197 // Handle the requested control code.
200 DbgPrint("service_ctrl called\n");
206 // SERVICE_STOP_PENDING should be reported before
207 // setting the Stop Event - hServerStopEvent - in
208 // ServiceStop(). This avoids a race condition
209 // which may result in a 1053 - The Service did not respond...
212 case SERVICE_CONTROL_SHUTDOWN
:
214 case SERVICE_CONTROL_STOP
:
215 DbgPrint("for stop\n");
216 ReportStatusToSCMgr(SERVICE_STOP_PENDING
, NO_ERROR
, 0);
221 // Update the service status.
223 case SERVICE_CONTROL_INTERROGATE
:
226 // invalid control code
233 ReportStatusToSCMgr(ssStatus
.dwCurrentState
, NO_ERROR
, 0);
239 // FUNCTION: ReportStatusToSCMgr()
241 // PURPOSE: Sets the current status of the service and
242 // reports it to the Service Control Manager
245 // dwCurrentState - the state of the service
246 // dwWin32ExitCode - error code to report
247 // dwWaitHint - worst case estimate to next checkpoint
255 BOOL
ReportStatusToSCMgr(DWORD dwCurrentState
,
256 DWORD dwWin32ExitCode
,
259 static DWORD dwCheckPoint
= 1;
263 if ( !bDebug
) // when debugging we don't report to the SCM
265 if (dwCurrentState
== SERVICE_START_PENDING
)
266 ssStatus
.dwControlsAccepted
= 0;
269 ssStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
271 ssStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN
;
274 ssStatus
.dwCurrentState
= dwCurrentState
;
275 ssStatus
.dwWin32ExitCode
= dwWin32ExitCode
;
276 ssStatus
.dwWaitHint
= dwWaitHint
;
278 if ( ( dwCurrentState
== SERVICE_RUNNING
) ||
279 ( dwCurrentState
== SERVICE_STOPPED
) )
280 ssStatus
.dwCheckPoint
= 0;
282 ssStatus
.dwCheckPoint
= dwCheckPoint
++;
285 // Report the status of the service to the service control manager.
286 fResult
= SetServiceStatus(sshStatusHandle
, &ssStatus
);
288 AddToMessageLog(TEXT("SetServiceStatus"));
296 // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
298 // PURPOSE: Allows any thread to log an error message
301 // lpszMsg - text for message
308 VOID
AddToMessageLog(LPTSTR lpszMsg
)
310 TCHAR szMsg
[(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100 ];
312 LPTSTR lpszStrings
[2];
316 dwErr
= GetLastError();
318 // Use event logging to log the error.
320 hEventSource
= RegisterEventSource(NULL
, TEXT(SZSERVICENAME
));
323 _stprintf_s(szMsg
,(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME
), dwErr
);
325 _sntprintf(szMsg
,(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME
), dwErr
);
327 lpszStrings
[0] = szMsg
;
328 lpszStrings
[1] = lpszMsg
;
330 if (hEventSource
!= NULL
)
332 ReportEvent(hEventSource
, // handle of event source
333 EVENTLOG_ERROR_TYPE
, // event type
336 NULL
, // current user's SID
337 2, // strings in lpszStrings
338 0, // no bytes of raw data
339 lpszStrings
, // array of error strings
340 NULL
); // no raw data
342 (VOID
) DeregisterEventSource(hEventSource
);
350 ///////////////////////////////////////////////////////////////////
352 // The following code handles service installation and removal
357 // FUNCTION: CmdInstallService()
359 // PURPOSE: Installs the service
369 void CmdInstallService()
371 SC_HANDLE schService
;
372 SC_HANDLE schSCManager
;
376 if ( GetModuleFileName( NULL
, szPath
, 512 ) == 0 )
378 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME
), GetLastErrorText(szErr
, 256));
382 schSCManager
= OpenSCManager(
383 NULL
, // machine (NULL == local)
384 NULL
, // database (NULL == default)
385 SC_MANAGER_CONNECT
| SC_MANAGER_CREATE_SERVICE
// access required
389 schService
= CreateService(
390 schSCManager
, // SCManager database
391 TEXT(SZSERVICENAME
), // name of service
392 TEXT(SZSERVICEDISPLAYNAME
), // name to display
393 SERVICE_QUERY_STATUS
, // desired access
394 SERVICE_WIN32_OWN_PROCESS
, // service type
395 SERVICE_AUTO_START
, // start type
396 SERVICE_ERROR_NORMAL
, // error control type
397 szPath
, // service's binary
398 NULL
, // no load ordering group
399 NULL
, // no tag identifier
400 TEXT(SZDEPENDENCIES
), // dependencies
401 NULL
, // LocalSystem account
402 NULL
); // no password
406 _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
407 CloseServiceHandle(schService
);
411 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr
, 256));
414 CloseServiceHandle(schSCManager
);
417 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
423 // FUNCTION: CmdRemoveService()
425 // PURPOSE: Stops and removes the service
435 void CmdRemoveService()
437 SC_HANDLE schService
;
438 SC_HANDLE schSCManager
;
440 schSCManager
= OpenSCManager(
441 NULL
, // machine (NULL == local)
442 NULL
, // database (NULL == default)
443 SC_MANAGER_CONNECT
// access required
447 schService
= OpenService(schSCManager
, TEXT(SZSERVICENAME
), DELETE
| SERVICE_STOP
| SERVICE_QUERY_STATUS
);
451 // try to stop the service
452 if ( ControlService( schService
, SERVICE_CONTROL_STOP
, &ssStatus
) )
454 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME
));
457 while ( QueryServiceStatus( schService
, &ssStatus
) )
459 if ( ssStatus
.dwCurrentState
== SERVICE_STOP_PENDING
)
468 if ( ssStatus
.dwCurrentState
== SERVICE_STOPPED
)
469 _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
471 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
475 // now remove the service
476 if ( DeleteService(schService
) )
477 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
479 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr
,256));
482 CloseServiceHandle(schService
);
485 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr
,256));
487 CloseServiceHandle(schSCManager
);
490 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
496 ///////////////////////////////////////////////////////////////////
498 // The following code is for running the service as a console app
503 // FUNCTION: CmdDebugService(int argc, char ** argv)
505 // PURPOSE: Runs the service as a console application
508 // argc - number of command line arguments
509 // argv - array of command line arguments
516 void CmdDebugService(int argc
, char ** argv
)
522 lpszArgv
= CommandLineToArgvW(GetCommandLineW(), &(dwArgc
) );
523 if (NULL
== lpszArgv
)
525 // CommandLineToArvW failed!!
526 _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
530 dwArgc
= (DWORD
) argc
;
534 _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
536 SetConsoleCtrlHandler( ControlHandler
, TRUE
);
538 ServiceStart( dwArgc
, lpszArgv
);
541 // Must free memory allocated for arguments
543 GlobalFree(lpszArgv
);
550 // FUNCTION: ControlHandler ( DWORD dwCtrlType )
552 // PURPOSE: Handled console control events
555 // dwCtrlType - type of control event
563 BOOL WINAPI
ControlHandler ( DWORD dwCtrlType
)
565 switch ( dwCtrlType
)
567 case CTRL_BREAK_EVENT
: // use Ctrl+C or Ctrl+Break to simulate
568 case CTRL_C_EVENT
: // SERVICE_CONTROL_STOP in debug mode
569 _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
579 // FUNCTION: GetLastErrorText
581 // PURPOSE: copies error message text to string
584 // lpszBuf - destination buffer
585 // dwSize - size of buffer
588 // destination buffer
592 LPTSTR
GetLastErrorText( LPTSTR lpszBuf
, DWORD dwSize
)
595 LPTSTR lpszTemp
= NULL
;
597 dwRet
= FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
605 // supplied buffer is not long enough
606 if ( !dwRet
|| ( (long)dwSize
< (long)dwRet
+14 ) )
607 lpszBuf
[0] = TEXT('\0');
610 if (NULL
!= lpszTemp
)
612 lpszTemp
[lstrlen(lpszTemp
)-2] = TEXT('\0'); //remove cr and newline character
614 _stprintf_s( lpszBuf
, dwSize
, TEXT("%s (0x%x)"), lpszTemp
, GetLastError() );
616 _sntprintf( lpszBuf
, dwSize
, TEXT("%s (0x%x)"), lpszTemp
, GetLastError() );
621 if ( NULL
!= lpszTemp
)
622 LocalFree((HLOCAL
) lpszTemp
);