// // 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