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 // SERVICE_STATUS members that don't change in example
144 ssStatus
.dwServiceType
= SERVICE_WIN32_OWN_PROCESS
;
145 ssStatus
.dwServiceSpecificExitCode
= 0;
148 // report the status to the service control manager.
150 if (!ReportStatusToSCMgr(
151 SERVICE_START_PENDING
, // service state
152 NO_ERROR
, // exit code
157 ServiceStart( dwArgc
, lpszArgv
);
161 // try to report the stopped status to the service control manager.
164 (VOID
)ReportStatusToSCMgr(
175 // FUNCTION: service_ctrl
177 // PURPOSE: This function is called by the SCM whenever
178 // ControlService() is called on this service.
181 // dwCtrlCode - type of control requested
188 VOID WINAPI
service_ctrl(DWORD dwCtrlCode
)
190 // Handle the requested control code.
196 // SERVICE_STOP_PENDING should be reported before
197 // setting the Stop Event - hServerStopEvent - in
198 // ServiceStop(). This avoids a race condition
199 // which may result in a 1053 - The Service did not respond...
201 case SERVICE_CONTROL_STOP
:
202 ReportStatusToSCMgr(SERVICE_STOP_PENDING
, NO_ERROR
, 0);
206 // Update the service status.
208 case SERVICE_CONTROL_INTERROGATE
:
211 // invalid control code
218 ReportStatusToSCMgr(ssStatus
.dwCurrentState
, NO_ERROR
, 0);
224 // FUNCTION: ReportStatusToSCMgr()
226 // PURPOSE: Sets the current status of the service and
227 // reports it to the Service Control Manager
230 // dwCurrentState - the state of the service
231 // dwWin32ExitCode - error code to report
232 // dwWaitHint - worst case estimate to next checkpoint
240 BOOL
ReportStatusToSCMgr(DWORD dwCurrentState
,
241 DWORD dwWin32ExitCode
,
244 static DWORD dwCheckPoint
= 1;
248 if ( !bDebug
) // when debugging we don't report to the SCM
250 if (dwCurrentState
== SERVICE_START_PENDING
)
251 ssStatus
.dwControlsAccepted
= 0;
253 ssStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
;
255 ssStatus
.dwCurrentState
= dwCurrentState
;
256 ssStatus
.dwWin32ExitCode
= dwWin32ExitCode
;
257 ssStatus
.dwWaitHint
= dwWaitHint
;
259 if ( ( dwCurrentState
== SERVICE_RUNNING
) ||
260 ( dwCurrentState
== SERVICE_STOPPED
) )
261 ssStatus
.dwCheckPoint
= 0;
263 ssStatus
.dwCheckPoint
= dwCheckPoint
++;
266 // Report the status of the service to the service control manager.
267 fResult
= SetServiceStatus(sshStatusHandle
, &ssStatus
);
269 AddToMessageLog(TEXT("SetServiceStatus"));
277 // FUNCTION: AddToMessageLog(LPTSTR lpszMsg)
279 // PURPOSE: Allows any thread to log an error message
282 // lpszMsg - text for message
289 VOID
AddToMessageLog(LPTSTR lpszMsg
)
291 TCHAR szMsg
[(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100 ];
293 LPTSTR lpszStrings
[2];
297 dwErr
= GetLastError();
299 // Use event logging to log the error.
301 hEventSource
= RegisterEventSource(NULL
, TEXT(SZSERVICENAME
));
304 _stprintf_s(szMsg
,(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME
), dwErr
);
306 _sntprintf(szMsg
,(sizeof(SZSERVICENAME
) / sizeof(TCHAR
)) + 100, TEXT("%s error: %d"), TEXT(SZSERVICENAME
), dwErr
);
308 lpszStrings
[0] = szMsg
;
309 lpszStrings
[1] = lpszMsg
;
311 if (hEventSource
!= NULL
)
313 ReportEvent(hEventSource
, // handle of event source
314 EVENTLOG_ERROR_TYPE
, // event type
317 NULL
, // current user's SID
318 2, // strings in lpszStrings
319 0, // no bytes of raw data
320 lpszStrings
, // array of error strings
321 NULL
); // no raw data
323 (VOID
) DeregisterEventSource(hEventSource
);
331 ///////////////////////////////////////////////////////////////////
333 // The following code handles service installation and removal
338 // FUNCTION: CmdInstallService()
340 // PURPOSE: Installs the service
350 void CmdInstallService()
352 SC_HANDLE schService
;
353 SC_HANDLE schSCManager
;
357 if ( GetModuleFileName( NULL
, szPath
, 512 ) == 0 )
359 _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME
), GetLastErrorText(szErr
, 256));
363 schSCManager
= OpenSCManager(
364 NULL
, // machine (NULL == local)
365 NULL
, // database (NULL == default)
366 SC_MANAGER_CONNECT
| SC_MANAGER_CREATE_SERVICE
// access required
370 schService
= CreateService(
371 schSCManager
, // SCManager database
372 TEXT(SZSERVICENAME
), // name of service
373 TEXT(SZSERVICEDISPLAYNAME
), // name to display
374 SERVICE_QUERY_STATUS
, // desired access
375 SERVICE_WIN32_OWN_PROCESS
, // service type
376 SERVICE_AUTO_START
, // start type
377 SERVICE_ERROR_NORMAL
, // error control type
378 szPath
, // service's binary
379 NULL
, // no load ordering group
380 NULL
, // no tag identifier
381 TEXT(SZDEPENDENCIES
), // dependencies
382 NULL
, // LocalSystem account
383 NULL
); // no password
387 _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
388 CloseServiceHandle(schService
);
392 _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr
, 256));
395 CloseServiceHandle(schSCManager
);
398 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
404 // FUNCTION: CmdRemoveService()
406 // PURPOSE: Stops and removes the service
416 void CmdRemoveService()
418 SC_HANDLE schService
;
419 SC_HANDLE schSCManager
;
421 schSCManager
= OpenSCManager(
422 NULL
, // machine (NULL == local)
423 NULL
, // database (NULL == default)
424 SC_MANAGER_CONNECT
// access required
428 schService
= OpenService(schSCManager
, TEXT(SZSERVICENAME
), DELETE
| SERVICE_STOP
| SERVICE_QUERY_STATUS
);
432 // try to stop the service
433 if ( ControlService( schService
, SERVICE_CONTROL_STOP
, &ssStatus
) )
435 _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME
));
438 while ( QueryServiceStatus( schService
, &ssStatus
) )
440 if ( ssStatus
.dwCurrentState
== SERVICE_STOP_PENDING
)
449 if ( ssStatus
.dwCurrentState
== SERVICE_STOPPED
)
450 _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
452 _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
456 // now remove the service
457 if ( DeleteService(schService
) )
458 _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME
) );
460 _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr
,256));
463 CloseServiceHandle(schService
);
466 _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr
,256));
468 CloseServiceHandle(schSCManager
);
471 _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr
,256));
477 ///////////////////////////////////////////////////////////////////
479 // The following code is for running the service as a console app
484 // FUNCTION: CmdDebugService(int argc, char ** argv)
486 // PURPOSE: Runs the service as a console application
489 // argc - number of command line arguments
490 // argv - array of command line arguments
497 void CmdDebugService(int argc
, char ** argv
)
503 lpszArgv
= CommandLineToArgvW(GetCommandLineW(), &(dwArgc
) );
504 if (NULL
== lpszArgv
)
506 // CommandLineToArvW failed!!
507 _tprintf(TEXT("CmdDebugService CommandLineToArgvW returned NULL\n"));
511 dwArgc
= (DWORD
) argc
;
515 _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
517 SetConsoleCtrlHandler( ControlHandler
, TRUE
);
519 ServiceStart( dwArgc
, lpszArgv
);
522 // Must free memory allocated for arguments
524 GlobalFree(lpszArgv
);
531 // FUNCTION: ControlHandler ( DWORD dwCtrlType )
533 // PURPOSE: Handled console control events
536 // dwCtrlType - type of control event
544 BOOL WINAPI
ControlHandler ( DWORD dwCtrlType
)
546 switch ( dwCtrlType
)
548 case CTRL_BREAK_EVENT
: // use Ctrl+C or Ctrl+Break to simulate
549 case CTRL_C_EVENT
: // SERVICE_CONTROL_STOP in debug mode
550 _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME
));
560 // FUNCTION: GetLastErrorText
562 // PURPOSE: copies error message text to string
565 // lpszBuf - destination buffer
566 // dwSize - size of buffer
569 // destination buffer
573 LPTSTR
GetLastErrorText( LPTSTR lpszBuf
, DWORD dwSize
)
576 LPTSTR lpszTemp
= NULL
;
578 dwRet
= FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
|FORMAT_MESSAGE_ARGUMENT_ARRAY
,
586 // supplied buffer is not long enough
587 if ( !dwRet
|| ( (long)dwSize
< (long)dwRet
+14 ) )
588 lpszBuf
[0] = TEXT('\0');
591 if (NULL
!= lpszTemp
)
593 lpszTemp
[lstrlen(lpszTemp
)-2] = TEXT('\0'); //remove cr and newline character
595 _stprintf_s( lpszBuf
, dwSize
, TEXT("%s (0x%x)"), lpszTemp
, GetLastError() );
597 _sntprintf( lpszBuf
, dwSize
, TEXT("%s (0x%x)"), lpszTemp
, GetLastError() );
602 if ( NULL
!= lpszTemp
)
603 LocalFree((HLOCAL
) lpszTemp
);