402 lines
9.5 KiB
C++
402 lines
9.5 KiB
C++
//
|
|
// Copyright (C) 1993-1997 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// MODULE: service.c
|
|
//
|
|
// PURPOSE: Implements functions required by all services
|
|
// windows.
|
|
//
|
|
// FUNCTIONS:
|
|
// main(int argc, char **argv);
|
|
// NTmain(int argc, char **argv);
|
|
// W95main(int argc, char **argv);
|
|
// service_ctrl(DWORD dwCtrlCode);
|
|
// ControlHandler ( DWORD dwCtrlType ); // DEBUG only
|
|
// GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); // DEBUG only
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// AUTHOR: Claus Giloi (based on SDK sample)
|
|
//
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifndef DEBUG
|
|
#undef _tprintf
|
|
#define _tprintf force_compile_error
|
|
#endif // !DEBUG
|
|
|
|
// internal variables
|
|
SERVICE_STATUS g_ssStatus; // current status of the service
|
|
SERVICE_STATUS_HANDLE g_sshStatusHandle;
|
|
DWORD dwErr = 0;
|
|
OSVERSIONINFO g_osvi; // The os version info structure global
|
|
BOOL g_fInShutdown = FALSE;
|
|
DWORD g_dwMainThreadID = 0;
|
|
|
|
// internal function prototypes
|
|
VOID WINAPI service_ctrl(DWORD dwCtrlCode);
|
|
void __cdecl NTmain(int argc, char **argv);
|
|
void __cdecl W95main(int argc, char **argv);
|
|
|
|
|
|
// Debug only functionality
|
|
#ifdef DEBUG
|
|
TCHAR szErr[256];
|
|
BOOL bDebug = FALSE;
|
|
BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
|
|
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
|
|
extern BOOL InitDebugMemoryOptions(void);
|
|
extern VOID DumpMemoryLeaksAndBreak(void);
|
|
#endif // DEBUG
|
|
|
|
typedef BOOL (WINAPI *PFNCHANGESERVICECONFIG2)(SC_HANDLE, DWORD, LPVOID);
|
|
|
|
//
|
|
// FUNCTION: main
|
|
//
|
|
// PURPOSE: entrypoint for service
|
|
//
|
|
// PARAMETERS:
|
|
// argc - number of command line arguments
|
|
// argv - array of command line arguments
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
// Get Platform type and
|
|
// call appropriate main for platform (NT or Win95)
|
|
//
|
|
void __cdecl main(int argc, char **argv)
|
|
{
|
|
#ifdef DEBUG
|
|
InitDebugMemoryOptions();
|
|
#endif // DEBUG
|
|
|
|
// Store OS version info
|
|
g_osvi.dwOSVersionInfoSize = sizeof(g_osvi);
|
|
if (FALSE == ::GetVersionEx(&g_osvi))
|
|
{
|
|
ERROR_OUT(("GetVersionEx() failed!"));
|
|
return;
|
|
}
|
|
|
|
if ( IS_NT )
|
|
{
|
|
NTmain( argc, argv );
|
|
}
|
|
else
|
|
{
|
|
W95main( argc, argv );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DumpMemoryLeaksAndBreak();
|
|
#endif // DEBUG
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: NTmain
|
|
//
|
|
// PURPOSE: entrypoint for service
|
|
//
|
|
// PARAMETERS:
|
|
// argc - number of command line arguments
|
|
// argv - array of command line arguments
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
// NTmain() either performs the command line task, or
|
|
// call StartServiceCtrlDispatcher to register the
|
|
// main service thread. When the this call returns,
|
|
// the service has stopped, so exit.
|
|
//
|
|
void __cdecl NTmain(int argc, char **argv)
|
|
{
|
|
DWORD dwArgc;
|
|
LPTSTR *lpszArgv;
|
|
|
|
#ifdef UNICODE
|
|
lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
|
|
#else
|
|
dwArgc = (DWORD) argc;
|
|
lpszArgv = argv;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
SetConsoleCtrlHandler( ControlHandler, TRUE );
|
|
#endif // DEBUG
|
|
|
|
MNMServiceStart( dwArgc, lpszArgv );
|
|
}
|
|
|
|
//
|
|
// FUNCTION: W95main
|
|
//
|
|
// PURPOSE: entrypoint for pseudo-service on Win95
|
|
//
|
|
// PARAMETERS:
|
|
// argc - number of command line arguments
|
|
// argv - array of command line arguments
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
// W95main() registers as Win95 service and calls Init routine directly
|
|
//
|
|
typedef DWORD (WINAPI * REGISTERSERVICEPROC)(DWORD, DWORD);
|
|
#ifndef RSP_SIMPLE_SERVICE
|
|
#define RSP_SIMPLE_SERVICE 0x00000001
|
|
#endif
|
|
|
|
void __cdecl W95main(int argc, char **argv)
|
|
{
|
|
HMODULE hKernel;
|
|
REGISTERSERVICEPROC lpfnRegisterServiceProcess;
|
|
|
|
if ( hKernel = GetModuleHandle("KERNEL32.DLL") )
|
|
{
|
|
if ( lpfnRegisterServiceProcess =
|
|
(REGISTERSERVICEPROC)GetProcAddress ( hKernel,
|
|
"RegisterServiceProcess" ))
|
|
{
|
|
if (!lpfnRegisterServiceProcess(NULL, RSP_SIMPLE_SERVICE))
|
|
{
|
|
ERROR_OUT(("RegisterServiceProcess failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("GetProcAddr of RegisterServiceProcess failed"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERROR_OUT(("GetModuleHandle of KERNEL32.DLL failed"));
|
|
}
|
|
|
|
MNMServiceStart(argc, argv);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: service_ctrl
|
|
//
|
|
// PURPOSE: This function is called by the SCM whenever
|
|
// ControlService() is called on this service.
|
|
//
|
|
// PARAMETERS:
|
|
// dwCtrlCode - type of control requested
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
VOID WINAPI service_ctrl(DWORD dwCtrlCode)
|
|
{
|
|
// Handle the requested control code.
|
|
//
|
|
switch(dwCtrlCode)
|
|
{
|
|
// Stop the service.
|
|
//
|
|
// SERVICE_STOP_PENDING should be reported before
|
|
// setting the Stop Event - hServerStopEvent - in
|
|
// MNMServiceStop(). This avoids a race condition
|
|
// which may result in a 1053 - The Service did not respond...
|
|
// error.
|
|
case SERVICE_CONTROL_STOP:
|
|
ReportStatusToSCMgr(SERVICE_STOP_PENDING, NO_ERROR, 30000);
|
|
::PostThreadMessage(g_dwMainThreadID, WM_QUIT, 0, 0);
|
|
return;
|
|
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
g_fInShutdown = TRUE;
|
|
break;
|
|
|
|
// Update the service status.
|
|
//
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
break;
|
|
|
|
case SERVICE_CONTROL_PAUSE:
|
|
ReportStatusToSCMgr(SERVICE_PAUSE_PENDING, NO_ERROR, 30000);
|
|
return;
|
|
|
|
case SERVICE_CONTROL_CONTINUE:
|
|
ReportStatusToSCMgr(SERVICE_CONTINUE_PENDING, NO_ERROR, 30000);
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: ReportStatusToSCMgr()
|
|
//
|
|
// PURPOSE: Sets the current status of the service and
|
|
// reports it to the Service Control Manager
|
|
//
|
|
// PARAMETERS:
|
|
// dwCurrentState - the state of the service
|
|
// dwWin32ExitCode - error code to report
|
|
// dwWaitHint - worst case estimate to next checkpoint
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE - success
|
|
// FALSE - failure
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
BOOL ReportStatusToSCMgr(DWORD dwCurrentState,
|
|
DWORD dwWin32ExitCode,
|
|
DWORD dwWaitHint)
|
|
{
|
|
static DWORD dwCheckPoint = 1;
|
|
BOOL fResult = TRUE;
|
|
|
|
#ifdef DEBUG
|
|
if ( bDebug )
|
|
return TRUE;
|
|
#endif
|
|
|
|
if ( IS_NT ) // when debugging we don't report to the SCM
|
|
{
|
|
switch ( dwCurrentState )
|
|
{
|
|
case SERVICE_START_PENDING:
|
|
case SERVICE_STOP_PENDING:
|
|
case SERVICE_CONTINUE_PENDING:
|
|
case SERVICE_PAUSE_PENDING:
|
|
break;
|
|
|
|
case SERVICE_PAUSED:
|
|
case SERVICE_STOPPED:
|
|
case SERVICE_RUNNING:
|
|
g_ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
|
|
SERVICE_ACCEPT_PAUSE_CONTINUE ;
|
|
|
|
break;
|
|
}
|
|
|
|
g_ssStatus.dwCurrentState = dwCurrentState;
|
|
g_ssStatus.dwWin32ExitCode = dwWin32ExitCode;
|
|
g_ssStatus.dwWaitHint = dwWaitHint;
|
|
|
|
if ( ( dwCurrentState == SERVICE_RUNNING ) ||
|
|
( dwCurrentState == SERVICE_STOPPED ) ||
|
|
( dwCurrentState == SERVICE_PAUSED ))
|
|
g_ssStatus.dwCheckPoint = 0;
|
|
else
|
|
g_ssStatus.dwCheckPoint = dwCheckPoint++;
|
|
|
|
// Report the status of the service to the service control manager.
|
|
//
|
|
if (!(fResult = SetServiceStatus( g_sshStatusHandle, &g_ssStatus))) {
|
|
}
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// The following code handles service installation and removal
|
|
//
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
//
|
|
// The following code is for running the service as a console app
|
|
//
|
|
|
|
|
|
//
|
|
// FUNCTION: ControlHandler ( DWORD dwCtrlType )
|
|
//
|
|
// PURPOSE: Handled console control events
|
|
//
|
|
// PARAMETERS:
|
|
// dwCtrlType - type of control event
|
|
//
|
|
// RETURN VALUE:
|
|
// True - handled
|
|
// False - unhandled
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
BOOL WINAPI ControlHandler ( DWORD dwCtrlType )
|
|
{
|
|
switch( dwCtrlType )
|
|
{
|
|
case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
|
|
case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
|
|
PostThreadMessage(g_dwMainThreadID, WM_QUIT, 0, 0);
|
|
return TRUE;
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// FUNCTION: GetLastErrorText
|
|
//
|
|
// PURPOSE: copies error message text to string
|
|
//
|
|
// PARAMETERS:
|
|
// lpszBuf - destination buffer
|
|
// dwSize - size of buffer
|
|
//
|
|
// RETURN VALUE:
|
|
// destination buffer
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize )
|
|
{
|
|
DWORD dwRet;
|
|
LPTSTR lpszTemp = NULL;
|
|
|
|
dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
NULL,
|
|
GetLastError(),
|
|
LANG_NEUTRAL,
|
|
(LPTSTR)&lpszTemp,
|
|
0,
|
|
NULL );
|
|
|
|
// supplied buffer is not long enough
|
|
if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
|
|
lpszBuf[0] = TEXT('\0');
|
|
else
|
|
{
|
|
lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
|
|
_stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
|
|
}
|
|
|
|
if ( lpszTemp )
|
|
LocalFree((HLOCAL) lpszTemp );
|
|
|
|
return lpszBuf;
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|