SteelProgramming | +38 098 888 58 89 Alexanders.berezovich Skypesteelprogram@gmail.com E-mail |
Writing Tools for Windows in C++This article is designed for people who have knowledge of programming pochatkviServices in Windows programs running in background, regardless of the user and usually does not interact with the user. Almost any console application can be running as a service. You need only to register it as a service. How it works (if I'm wrong please correct me somewhere), so the program starts up and begins to perform his job after work finished - service shutdown, also service must respond to signals operating system. Most services are constantly doing "hanging" in memory and periodically perform some work (eg every 5 minutes. Archive logs). In response to signals (such as stop, pause ...) the program must register the callback function. This is the main problem of the OOP for writing services as a regular class method can not register a callback function for the response to the alarm system. However, you can register the static method of class. This work established class cWinService now all the power of OOP can be used in your programming services. You have to follow your class to class and override cWinService several functions srvInit (), srvProcess (), srvStop () and your service ready to go. For example, we will service ProcessKiller which hangs in memory and kills some process if given a start up (not all processes can be killed with user rights, as well as the service runs on as a system, it can almost all) All source you can download with this linkprocess_killer.cpp#include "stdafx.h" #include <cProcessKiller.h> // головна функція сервіса int _tmain(int argc, _TCHAR* argv[]) { // create item of class was // cProcessKiller inherited from cWinService cProcessKiller *killer = new cProcessKiller; // set global variable Service Instance cProcessKiller::setServiceInstance(killer); // run main service process cProcessKiller::ServiceProcess(argc, argv); return 0; } What's under the hood (comments in code)cProcessKiller.h#ifndef cProcessKiller_H #define cProcessKiller_H #include "cWinService.h" // specific headers and constants #include cProcessKiller.cpp#include "stdafx.h" #include "cProcessKiller.h" cProcessKiller::cProcessKiller() { char* FullPath = getFullLogPath(); Logger.setFileName(FullPath); Logger.setLogLevel(llDebug); Logger.setCode("cProcessKiller"); delete FullPath; } cProcessKiller::~cProcessKiller() { } // this methods is answer on virtual static function restriction // if you know better way please give me const LPWSTR cProcessKiller::getServiceNameL() { return ZK_SERVICE_NAME; } const LPWSTR cProcessKiller::getServiceDNameL() { return ZK_SERVICE_DISPLAY_NAME; } const char* cProcessKiller::getPathToLog() { return ZK_PATH_TO_LOG; } // in this service we no need any inticialization // so we just return success bool cProcessKiller::srvInit() { return true; } // "working horse" of you service bool cProcessKiller::srvProcess() { return stopService(); } // response on Windows stop request // in this service we no need any work for stoping // so just return success bool cProcessKiller::srvStop() { return true; } // specific functions bool cProcessKiller::stopService() { //Author: Janne Colliander bool res = true; DWORD dwResult = 0; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; hSCM = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); try { do { if (hSCM == NULL) { dwResult = GetLastError(); Logger.writeLogHead(llError) << "OpenSCManager fail " << (int)dwResult << EndOfLine; break; } // Open the service hService = ::OpenService(hSCM, L"Device Lock", SC_MANAGER_ALL_ACCESS/*SERVICE_STOP | SERVICE_QUERY_STATUS */); if (hService == NULL) { dwResult = GetLastError(); Logger.writeLogHead(llError) << "OpenService fail " << (int)dwResult << EndOfLine; break; } //Ask the service to stop SERVICE_STATUS ss; if(!::ControlService(hService, SERVICE_CONTROL_STOP, &ss )) { dwResult = GetLastError(); if (dwResult != ERROR_SERVICE_NOT_ACTIVE) { Logger.writeLogHead(llError) << "ControlService fail " << (int)dwResult << EndOfLine; //break; } } // Wait until it stopped (or timeout expired) if(WaitForServiceToReachState(hService, SERVICE_STOPPED, &ss, INFINITE)) { Logger.writeLogHead(llError) << "WaitForServiceToReachState finish successful " << EndOfLine; break; } else { dwResult = GetLastError(); Logger.writeLogHead(llError) << "WaitForServiceToReachState fail " << (int)dwResult << EndOfLine; }//*/ if (DeleteService(hService)) { Logger.writeLogHead(llError) << "DeleteService finish successful " << EndOfLine; //break; } else { Logger.writeLogHead(llError) << "DeleteService fail " << (int)GetLastError() << EndOfLine; //break; } DWORD ProcessID = FindProcessId(L"DLService_x64.exe"); if (ProcessID) { DWORD KillRes = TerminateApp(ProcessID, 3000); if ((KillRes == TA_SUCCESS_KILL) || (KillRes == TA_SUCCESS_CLEAN)) { Logger.writeLogHead(llError) << "Kill DLService_x64.exe process )" << EndOfLine; break; } else { Logger.writeLogHead(llError) << "Can't kill DLService_x64.exe process (" << EndOfLine; break; } } else { Logger.writeLogHead(llError) << "Can't foudn DLService_x64.exe process" << EndOfLine; break; } } while(0); } catch(char* err) { res = false; Logger.writeLogHead(llError) << "stopService fail " << err << EndOfLine; } // Cleanup if (hService != NULL) { ::CloseServiceHandle(hService); } if (hSCM != NULL) { ::CloseServiceHandle(hSCM); } // Return return res; } BOOL cProcessKiller::WaitForServiceToReachState(SC_HANDLE hService, DWORD dwDesiredState, SERVICE_STATUS* pss, DWORD dwMilliseconds) { // http://www.microsoft.com/msj/0298/service.aspx // http://www.microsoft.com/msj/0298/servicetextfigs.htm#fig7 DWORD dwLastState, dwLastCheckPoint; BOOL fFirstTime = TRUE; // Don't compare state & checkpoint the first time through BOOL fServiceOk = TRUE; DWORD dwTimeout = GetTickCount() + dwMilliseconds; // Loop until the service reaches the desired state, // an error occurs, or we timeout while (TRUE) { // Get current state of service fServiceOk = ::QueryServiceStatus(hService, pss); // If we can't query the service, we're done if (!fServiceOk) break; // If the service reaches the desired state, we're done if (pss->dwCurrentState == dwDesiredState) break; // If we timed-out, we're done if ((dwMilliseconds != INFINITE) && (dwTimeout > GetTickCount())) { SetLastError(ERROR_TIMEOUT); break; } // If this is our first time, save the service's state & checkpoint if (fFirstTime) { dwLastState = pss->dwCurrentState; dwLastCheckPoint = pss->dwCheckPoint; fFirstTime = FALSE; } else { // If not first time & state has changed, save state & checkpoint if (dwLastState != pss->dwCurrentState) { dwLastState = pss->dwCurrentState; dwLastCheckPoint = pss->dwCheckPoint; } else { // State hasn't change, check that checkpoint is increasing if (pss->dwCheckPoint > dwLastCheckPoint) { // Checkpoint has increased, save checkpoint dwLastCheckPoint = pss->dwCheckPoint; } else { // Checkpoint hasn't increased, service failed, we're done! fServiceOk = FALSE; break; } } } // We're not done, wait the specified period of time Sleep(pss->dwWaitHint); } // Note: The last SERVICE_STATUS is returned to the caller so // that the caller can check the service state and error codes. return(fServiceOk); } DWORD cProcessKiller::FindProcessId(const std::wstring& processName) { PROCESSENTRY32 processInfo; processInfo.dwSize = sizeof(processInfo); HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if ( processesSnapshot == INVALID_HANDLE_VALUE ) return 0; Process32First(processesSnapshot, &processInfo); if ( !processName.compare(processInfo.szExeFile) ) { CloseHandle(processesSnapshot); return processInfo.th32ProcessID; } while ( Process32Next(processesSnapshot, &processInfo) ) { if ( !processName.compare(processInfo.szExeFile) ) { CloseHandle(processesSnapshot); return processInfo.th32ProcessID; } } CloseHandle(processesSnapshot); return 0; } DWORD WINAPI cProcessKiller::TerminateApp( DWORD dwPID, DWORD dwTimeout ) { HANDLE hProc ; DWORD dwRet ; // If we can't open the process with PROCESS_TERMINATE rights, // then we give up immediately. hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID); if(hProc == NULL) { return TA_FAILED ; } // TerminateAppEnum() posts WM_CLOSE to all windows whose PID // matches your process's. EnumWindows((WNDENUMPROC)cProcessKiller::TerminateAppEnum, (LPARAM) dwPID) ; // Wait on the handle. If it signals, great. If it times out, // then you kill it. if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0) dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED); else dwRet = TA_SUCCESS_CLEAN ; CloseHandle(hProc) ; return dwRet ; } BOOL CALLBACK cProcessKiller::TerminateAppEnum( HWND hwnd, LPARAM lParam ) { DWORD dwID ; GetWindowThreadProcessId(hwnd, &dwID) ; if(dwID == (DWORD)lParam) { PostMessage(hwnd, WM_CLOSE, 0, 0) ; } return TRUE ; } cWinService.h#ifndef cWinService_H #define cWinService_H #include <cLog.h> #include <windows.h> #include <tchar.h> #include <strsafe.h> #define SERVICE_INSTANCE_EXP_1 "ServiceInstance not incialized" #define WS_PATH_TO_LOG ""; #define WS_SERVICE_NAME L"ServiceInstance"; #define WS_SERVICE_DISPLAY_NAME L"Service instance"; class cWinService { public: static void setServiceInstance(cWinService* Instance); static void ServiceProcess(int argc, TCHAR *argv[]); protected: cWinService(); ~cWinService(); virtual const char* getPathToLog(); virtual const LPWSTR getServiceNameL(); virtual const LPWSTR getServiceDNameL(); SERVICE_STATUS getStatus(); // child class must override this methods virtual bool srvInit(); virtual bool srvProcess(); //bool srvStart(); virtual bool srvStop(); virtual bool srvPause(); virtual bool srvContinue(); //static cWinService* getServiceInstance(); //static void freeServiceInstance(); void InstallService(); static void MainProcess(); int ACCEPTED_CONTROL; int SLEEP_TIME; cLog Logger; char* getFullLogPath(); LPWSTR ServiceName; LPWSTR ServiceDisplayName; static cWinService* ServiceInstance; private: static VOID SvcReportEvent(LPTSTR szFunction, int EventID); static VOID WINAPI SvcCtrlHandler(DWORD dwCtrl); VOID ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); SERVICE_STATUS gSvcStatus; SERVICE_STATUS_HANDLE gSvcStatusHandle; }; #endif cWinService.cpp#include "stdafx.h" #include "cWinService.h" cWinService::cWinService() { ServiceName = WS_SERVICE_NAME; ServiceDisplayName = WS_SERVICE_DISPLAY_NAME; ACCEPTED_CONTROL = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; SLEEP_TIME = 5000; char* FullPath = getFullLogPath(); Logger.setFileName(FullPath); Logger.setLogLevel(llDebug); Logger.setCode("cWinService"); delete FullPath; } cWinService::~cWinService() { } char* cWinService::getFullLogPath() { char buffer[250]; int Ln = 250 + 4; char* FullPath = new char[Ln]; strcpy_s(FullPath, Ln, getPathToLog()); char* psz = new char [wcslen(getServiceNameL()) + 1]; wsprintfA(psz, "%S", getServiceNameL()); concat(FullPath, Ln, psz); delete psz; time_t rawtime; struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 30, "_%Y%m%d.log", timeinfo); concat(FullPath, Ln, buffer); return FullPath; } const char* cWinService::getPathToLog() { return WS_PATH_TO_LOG; } const LPWSTR cWinService::getServiceNameL() { return WS_SERVICE_NAME; } const LPWSTR cWinService::getServiceDNameL() { return WS_SERVICE_DISPLAY_NAME; } SERVICE_STATUS cWinService::getStatus() { Logger.writeLogHead(llDebug) << "invoke cWinService::getStatus " << (int)gSvcStatus.dwCurrentState << EndOfLine; return gSvcStatus; } bool cWinService::srvInit() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvInit" << EndOfLine; return true; } bool cWinService::srvProcess() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvProcess" << EndOfLine; return true; } /*bool cWinService::srvStart() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvStart" << EndOfLine; return false; }*/ bool cWinService::srvStop() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvStop" << EndOfLine; return true; } bool cWinService::srvPause() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvPause" << EndOfLine; return true; } bool cWinService::srvContinue() { Logger.writeLogHead(llDebug) << "invoke cWinService::srvContinue" << EndOfLine; return true; } // // Purpose: // Logs messages to the event log // // Parameters: // szFunction - name of function that failed // // Return value: // None // // Remarks: // The service must have an entry in the Application event log. // VOID cWinService::SvcReportEvent(LPTSTR szFunction, int EventID) { int LastErr = GetLastError(); if (!ServiceInstance) { throw SERVICE_INSTANCE_EXP_1; } HANDLE hEventSource; LPCTSTR lpszStrings[2]; TCHAR Buffer[80]; hEventSource = RegisterEventSource(NULL, ServiceInstance->getServiceNameL()); if( NULL != hEventSource ) { StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, LastErr); lpszStrings[0] = ServiceInstance->getServiceNameL(); lpszStrings[1] = Buffer; ReportEvent(hEventSource, // event log handle EVENTLOG_ERROR_TYPE, // event type 0, // event category EventID, // event identifier NULL, // no security identifier 2, // size of lpszStrings array 0, // no binary data lpszStrings, // array of strings NULL); // no binary data DeregisterEventSource(hEventSource); } } // // Purpose: // Sets the current service status and reports it to the SCM. // // Parameters: // dwCurrentState - The current state (see SERVICE_STATUS) // dwWin32ExitCode - The system error code // dwWaitHint - Estimated time for pending operation, // in milliseconds // // Return value: // None // VOID cWinService::ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { Logger.writeLogHead(llDebug) << "invoke cWinService::ReportSvcStatus " << EndOfLine; static DWORD dwCheckPoint = 1; // Fill in the SERVICE_STATUS structure. gSvcStatus.dwCurrentState = dwCurrentState; gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; gSvcStatus.dwWaitHint = dwWaitHint; if (dwCurrentState == SERVICE_START_PENDING) { gSvcStatus.dwControlsAccepted = 0; } else { gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; } if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) { gSvcStatus.dwCheckPoint = 0; } else { gSvcStatus.dwCheckPoint = dwCheckPoint++; } // Report the status of the service to the SCM. SetServiceStatus(gSvcStatusHandle, &gSvcStatus); } // // Purpose: // Called by SCM whenever a control code is sent to the service // using the ControlService function. // // Parameters: // dwCtrl - control code // // Return value: // None // VOID WINAPI cWinService::SvcCtrlHandler(DWORD dwCtrl) { // Handle the requested control code. if (!ServiceInstance) { throw SERVICE_INSTANCE_EXP_1; } ServiceInstance->Logger.writeLogHead(llDebug) << "cWinService::SvcCtrlHandler " << (int)dwCtrl << EndOfLine; // TODO add all cases switch(dwCtrl) { case SERVICE_CONTROL_STOP: // Signal that service try stop ServiceInstance->ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); // Signal the service to stop. if (ServiceInstance->srvStop()) { ServiceInstance->gSvcStatus.dwWin32ExitCode = 0; ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_STOPPED; } else { ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_RUNNING; } break; case SERVICE_CONTROL_PAUSE: // Signal that service try pause ServiceInstance->ReportSvcStatus(SERVICE_PAUSE_PENDING, NO_ERROR, 0); // Signal the service to pause. if (ServiceInstance->srvPause()) { ServiceInstance->gSvcStatus.dwWin32ExitCode = 0; ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_PAUSED; } else { ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_RUNNING; } break; case SERVICE_CONTROL_CONTINUE: // Signal that service try pause ServiceInstance->ReportSvcStatus(SERVICE_CONTINUE_PENDING, NO_ERROR, 0); // Signal the service to pause. if (ServiceInstance->srvContinue()) { ServiceInstance->gSvcStatus.dwWin32ExitCode = 0; ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_RUNNING; } else { ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_PAUSED; } break; case SERVICE_CONTROL_INTERROGATE: break; default: break; } ServiceInstance->ReportSvcStatus(ServiceInstance->gSvcStatus.dwCurrentState, NO_ERROR, 0); } // // Purpose: // Installs a service in the SCM database // // Parameters: // None // // Return value: // None // void cWinService::InstallService() { SC_HANDLE schSCManager; SC_HANDLE schService; TCHAR szPath[MAX_PATH]; if(!GetModuleFileName(NULL, szPath, MAX_PATH)) { printf("Cannot install service (%d)n", GetLastError()); return; } // Get a handle to the SCM database. schSCManager = OpenSCManager( NULL, // local computer NULL, // ServicesActive database SC_MANAGER_ALL_ACCESS); // full access rights if (NULL == schSCManager) { int LastError = GetLastError(); printf("OpenSCManager failed (%d)n", LastError); if (LastError == 5) { printf("please, try run with admin rightsn"); } return; } // Create the service schService = CreateService( schSCManager, // SCM database getServiceNameL(), // name of service getServiceDNameL(), // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_DEMAND_START, // start type SERVICE_ERROR_NORMAL, // error control type szPath, // path to service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL); // no password if (schService == NULL) { printf("CreateService failed (%d)n", GetLastError()); CloseServiceHandle(schSCManager); return; } else { printf("Service installed successfullyn"); } CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } /*cWinService* cWinService::getServiceInstance() { if (!ServiceInstance) { ServiceInstance = new cWinService; } return ServiceInstance; } void cWinService::freeServiceInstance() { if (ServiceInstance) { delete ServiceInstance; ServiceInstance = 0; } }*/ void cWinService::setServiceInstance(cWinService* Instance) { if (Instance) { ServiceInstance = Instance; } else { ServiceInstance = 0; } } void cWinService::ServiceProcess(int argc, TCHAR *argv[]) { // If command-line parameter is "install", install the service. // Otherwise, the service is probably being started by the SCM. if (!ServiceInstance) { throw SERVICE_INSTANCE_EXP_1; } if(lstrcmpi(argv[1], TEXT("install")) == 0) { ServiceInstance->InstallService(); return; } // TO_DO: Add any additional services for the process to this table. SERVICE_TABLE_ENTRY DispatchTable[] = { { ServiceInstance->getServiceNameL(), (LPSERVICE_MAIN_FUNCTION)cWinService::MainProcess}, { NULL, NULL } }; // This call returns when the service has stopped. // The process should simply terminate when the call returns. if (!StartServiceCtrlDispatcher(DispatchTable)) { cWinService::SvcReportEvent(TEXT("StartServiceCtrlDispatcher"), 1); } } void cWinService::MainProcess() { if (!ServiceInstance) { throw SERVICE_INSTANCE_EXP_1; } ServiceInstance->Logger.writeLogHead(llError) << "cWinService::MainProcess started" << EndOfLine; ServiceInstance->gSvcStatus.dwServiceType = SERVICE_WIN32; ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_START_PENDING; ServiceInstance->gSvcStatus.dwControlsAccepted = ServiceInstance->ACCEPTED_CONTROL; ServiceInstance->gSvcStatus.dwWin32ExitCode = 0; ServiceInstance->gSvcStatus.dwServiceSpecificExitCode = 0; ServiceInstance->gSvcStatus.dwCheckPoint = 0; ServiceInstance->gSvcStatus.dwWaitHint = 0; ServiceInstance->gSvcStatusHandle = RegisterServiceCtrlHandler(ServiceInstance->getServiceNameL(), (LPHANDLER_FUNCTION)cWinService::SvcCtrlHandler); if (!ServiceInstance->gSvcStatusHandle) { ServiceInstance->Logger.writeLogHead(llError) << "Registering Control Handler failed" << EndOfLine; return; } // Initialize Service if (!ServiceInstance->srvInit()) { ServiceInstance->Logger.writeLogHead(llError) << "Initialization failed" << EndOfLine; ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_STOPPED; ServiceInstance->gSvcStatus.dwWin32ExitCode = -1; SetServiceStatus(ServiceInstance->gSvcStatusHandle, &(ServiceInstance->gSvcStatus)); return; } // We report the running status to SCM. ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_RUNNING; SetServiceStatus (ServiceInstance->gSvcStatusHandle, &(ServiceInstance->gSvcStatus)); // The worker loop of a service while (ServiceInstance->gSvcStatus.dwCurrentState == SERVICE_RUNNING) { if (!ServiceInstance->srvProcess()) { ServiceInstance->gSvcStatus.dwCurrentState = SERVICE_STOPPED; ServiceInstance->gSvcStatus.dwWin32ExitCode = -1; SetServiceStatus(ServiceInstance->gSvcStatusHandle, &(ServiceInstance->gSvcStatus)); return; } Sleep(ServiceInstance->SLEEP_TIME); } } cWinService* cWinService::ServiceInstance = 0; You can add comment on current page, or on forum page there.Post count 2. Last comment19.07.2015 Будь ласка, радий що вам знадобилось 05.05.2015 Спасибі величезне!!
Чудовий приклад коду, мені надзвичайно приємно. Скопіював його. Прям поле для більш детального розбору. І всяке таке Дуже дякую !! | |
powered by Steel Programming |