对于后台运行的程序,比如基于C/S架构的服务器、各种监控系统的程序、或者是程序额外的功能需要后台运行来实现,在Windows平台中,服务经常会用到,这种对于需要24×7运行的程序是必备的,至少本人经常需要运用windows服务来搭建后台程序。于是查阅网上的各种资料,发现有不少的参考实例,但是使用起来有各种问题,不少是这些作者或者使用者对Windows NT Service API接口运用的不熟练或者不清楚,导致自己花费很多时间在理解这些错误的实例当中,不过折腾到最后,还是从错误中得到不少教训的。所以为了方便以后能够直接使用,不再做重复的工作,便在工作之余将Windows NT Service API接口进行了二次封装并简单的测试通过,发上来也让大家使用并指正其中的错误。
当然,有更权威的参考实例。http://msdn.microsoft.com/en-us/library/windows/desktop/bb540476(v=vs.85).aspx
废话不多说, 先直接贴上代码,希望不要被贱笑了...
NTService.h
#ifndef _NTSERVICE_H_ #define _NTSERVICE_H_ ///服务启动类型定义 enum SERVICE_STARTUP_TYPE { //禁用不可启动 SST_DISABLED=0, //随系统启动自动启动 SST_AUTO_START=1, //命令/手动启动 SST_DEMAND_START=2 }; ///服务执行的任务对象 class ServiceTask { public: ServiceTask(){} virtual ~ServiceTask(){} public: ///服务初始化通知 ///@return 成功返回0, 否则-1 ///@remark 完成后需及时返回, 不能长时间阻塞 virtual int OnInit(int argc, char **argv){return 0;} ///服务启动通知 ///@remark 服务启动阻塞执行此过程, 直至服务停止/关闭 virtual void OnStart(){} ///服务停止通知 ///@remark OnStart结束返回, 服务变为停止状态 virtual void OnStop(){} ///服务暂停通知 ///@remark OnStart暂停执行, 服务变为暂停状态 virtual void OnPause(){} ///被暂停的服务恢复通知 ///@remark OnStart恢复执行, 服务变为恢复状态 virtual void OnContinue(){} ///服务被强制关闭通知 ///@remark 通常是OS关闭时发生 virtual void OnShutdown(){} }; class NTServiceImpl; ///服务接口控制对象 class NTService { public: ///@param name服务名称 ///@param canstop是否可停止 ///@param canshutdown是否可关闭 ///@param canpausecontinue是否可暂停继续 NTService(const char *name, bool canstop=true, bool canshutdown=true, bool canpausecontinue=false); ~NTService(); public: ///检查服务是否安装 bool IsInstalled(); ///安装服务 ///@param display_name服务显示的名称 ///@param type服务启动类型 ///@return 成功返回0, 否则-1 int Install(const char *display_name, SERVICE_STARTUP_TYPE type); ///卸载服务 ///@return 成功返回0, 否则-1 int UnInstall(); ///设置服务启动类型 ///@return 成功返回0, 否则-1 int SetStartupType(SERVICE_STARTUP_TYPE type); ///更新服务描述信息 ///@return 成功返回0, 否则-1 int UpdateDesc(const char *desc); ///启动服务 ///@return 成功返回0, 否则-1 ///@param argc启动参数个数 ///@param argv启动参数列表 int Start(int argc=0, const char **argv=0); ///停止服务 ///@return 成功返回0, 否则-1 int Stop(); ///获取服务当前状态 ///@return 成功返回状态值, 否则-1 int GetCurrentStatus(); public: ///系统启动服务调用 ///@remark 不可主动调用 int Run(ServiceTask &service); private: NTServiceImpl *m_impl; }; #endif//_NTSERVICE_H_
NTServiceImpl.h
#ifndef _NTSERVICE_IMPL_H_ #define _NTSERVICE_IMPL_H_ #include "NTService.h" #include <Windows.h> #include <string> class NTServiceImpl { public: NTServiceImpl(const char *name, bool canstop, bool canshutdown, bool canpausecontinue); ~NTServiceImpl(); public: bool IsInstalled(); int Install(const char *display_name, SERVICE_STARTUP_TYPE type); int UnInstall(); int SetStartupType(SERVICE_STARTUP_TYPE type); int UpdateDesc(const char *desc); int Start(int argc, const char **argv); int Stop(); int GetCurrentStatus(); int Run(ServiceTask &service); private: std::string m_name; bool m_canstop; bool m_canshutdown; bool m_canpausecontinue; }; class NTServiceManager { private: NTServiceManager(); ~NTServiceManager(); public: static bool IsInstalled(const char *name); static int Install(const char *name, const char *display_name, SERVICE_STARTUP_TYPE type); static int UnInstall(const char *name); static int SetStartupType(const char *name, SERVICE_STARTUP_TYPE type); static int UpdateDesc(const char *name, const char *desc); static int Start(const char *name, int argc, const char **argv); static int Stop(const char *name); static int GetCurrentStatus(const char *name); private: static int GetRealStartupType(SERVICE_STARTUP_TYPE type); }; class NTServiceHandler { private: NTServiceHandler(const std::string &name, bool canstop, bool canshutdown, bool canpausecontinue, ServiceTask &service); ~NTServiceHandler(); public: static void CreateInstance(const std::string &name, bool canstop, bool canshutdown, bool canpausecontinue, ServiceTask &service) { DestroyInstance(); m_this =new NTServiceHandler(name, canstop, canshutdown, canpausecontinue, service); } static NTServiceHandler *GetInstance() { return m_this; } static void DestroyInstance() { if (m_this) { delete m_this; m_this = 0; } } public: int Run(); private: static void ServiceMainHandler(DWORD argc, LPSTR *argv); static void ServiceCtrlHandler(DWORD ctrl); private: void NTS_Start(DWORD argc, LPSTR *argv); void NTS_Stop(); void NTS_Pause(); void NTS_Continue(); void NTS_Shutdown(); void SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode=NO_ERROR, DWORD dwWaitHint=0); int OnInit(int argc, char **argv); void OnStart(); void OnStop(); void OnPause(); void OnContinue(); void OnShutdown(); private: std::string m_name; ServiceTask &m_service; SERVICE_STATUS m_status; SERVICE_STATUS_HANDLE m_status_handle; static NTServiceHandler *m_this; }; NTServiceHandler *NTServiceHandler::m_this = 0; #endif//_NTSERVICE_IMPL_H_
NTServiceImpl.cpp
#include "NTServiceImpl.h" #include <assert.h> NTService::NTService(const char *name, bool canstop, bool canshutdown, bool canpausecontinue) { m_impl = new NTServiceImpl(name, canstop, canshutdown, canpausecontinue); } NTService::~NTService() { if (m_impl) { delete m_impl; m_impl = 0; } } bool NTService::IsInstalled() { return m_impl->IsInstalled(); } int NTService::Install(const char *display_name, SERVICE_STARTUP_TYPE type) { return m_impl->Install(display_name, type); } int NTService::UnInstall() { return m_impl->UnInstall(); } int NTService::SetStartupType(SERVICE_STARTUP_TYPE type) { return m_impl->SetStartupType(type); } int NTService::UpdateDesc(const char *desc) { return m_impl->UpdateDesc(desc); } int NTService::Start(int argc, const char **argv) { return m_impl->Start(argc, argv); } int NTService::Stop() { return m_impl->Stop(); } int NTService::GetCurrentStatus() { return m_impl->GetCurrentStatus(); } int NTService:: Run(ServiceTask &service) { return m_impl->Run(service); } NTServiceImpl::NTServiceImpl(const char *name, bool canstop, bool canshutdown, bool canpausecontinue) : m_name(name) { m_canstop = canstop; m_canshutdown = canshutdown; m_canpausecontinue = canpausecontinue; } NTServiceImpl::~NTServiceImpl() { } bool NTServiceImpl::IsInstalled() { return NTServiceManager::IsInstalled(m_name.c_str()); } int NTServiceImpl::Install(const char *display_name, SERVICE_STARTUP_TYPE type) { return NTServiceManager::Install(m_name.c_str(), display_name, type); } int NTServiceImpl::UnInstall() { return NTServiceManager::UnInstall(m_name.c_str()); } int NTServiceImpl::Start(int argc, const char **argv) { return NTServiceManager::Start(m_name.c_str(), argc, argv); } int NTServiceImpl::Stop() { return NTServiceManager::Stop(m_name.c_str()); } int NTServiceImpl::SetStartupType(SERVICE_STARTUP_TYPE type) { return NTServiceManager::SetStartupType(m_name.c_str(), type); } int NTServiceImpl::UpdateDesc(const char *desc) { return NTServiceManager::UpdateDesc(m_name.c_str(), desc); } int NTServiceImpl::GetCurrentStatus() { return NTServiceManager::GetCurrentStatus(m_name.c_str()); } int NTServiceImpl::Run(ServiceTask &service) { NTServiceHandler::CreateInstance(m_name, m_canstop, m_canshutdown, m_canpausecontinue, service); NTServiceHandler *handler = NTServiceHandler::GetInstance(); return handler ? handler->Run() : -1; } bool NTServiceManager::IsInstalled(const char *name) { assert(name); bool result = false; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_QUERY_STATUS); __Cleanup: if (service) { result = true; ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::Install(const char *name, const char *display_name, SERVICE_STARTUP_TYPE type) { assert(name && display_name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE); if (!scmanager) { goto __Cleanup; } char file_path[MAX_PATH] = {0}; ::GetModuleFileName(NULL, file_path, MAX_PATH); int startup_type = GetRealStartupType(type); service = CreateService( scmanager, // SCM database name, // name of service display_name, // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type startup_type, // start type SERVICE_ERROR_NORMAL, // error control type file_path, // path to service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // no dependencies NULL, // LocalSystem account NULL); // no password __Cleanup: if (service) { result = 0; ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::UnInstall(const char *name) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; SERVICE_STATUS status = {0}; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_STOP|SERVICE_QUERY_STATUS|DELETE); if (!service) { goto __Cleanup; } if (ControlService(service, SERVICE_CONTROL_STOP, &status)) { ::Sleep(1000); while (QueryServiceStatus(service, &status)) { if (status.dwCurrentState != SERVICE_STOP_PENDING) { break; } else { Sleep(1000); } } } if (DeleteService(service)) { result = 0; } __Cleanup: if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::SetStartupType(const char *name, SERVICE_STARTUP_TYPE type) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_CHANGE_CONFIG); if (!service) { goto __Cleanup; } int startup_type = GetRealStartupType(type); BOOL csc_result = ChangeServiceConfig( service, // handle of service SERVICE_NO_CHANGE, // service type: no change startup_type, // service start type SERVICE_NO_CHANGE, // error control: no change NULL, // binary path: no change NULL, // load order group: no change NULL, // tag ID: no change NULL, // dependencies: no change NULL, // account name: no change NULL, // password: no change NULL); // display name: no change if (csc_result) { result = 0; } __Cleanup: if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::UpdateDesc(const char *name, const char *desc) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; SERVICE_DESCRIPTION sd = {0}; sd.lpDescription = (char *)desc; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_CHANGE_CONFIG); if (!service) { goto __Cleanup; } if (ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd)) { result = 0; } __Cleanup: if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::Start(const char *name, int argc, const char **argv) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; SERVICE_STATUS status = {0}; DWORD dwStartTickCount = 0; DWORD dwOldCheckPoint = 0; DWORD dwWaitTime = 0; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_ALL_ACCESS); if (!service) { goto __Cleanup; } if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if(status.dwCurrentState!=SERVICE_STOPPED && status.dwCurrentState!=SERVICE_STOP_PENDING) { goto __Cleanup; } dwStartTickCount = GetTickCount(); dwOldCheckPoint = status.dwCheckPoint; while (status.dwCurrentState == SERVICE_STOP_PENDING) { dwWaitTime = status.dwWaitHint / 10; if(dwWaitTime < 1000) { dwWaitTime = 1000; } else if (dwWaitTime > 10000) { dwWaitTime = 10000; } ::Sleep(dwWaitTime); if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if (status.dwCheckPoint > dwOldCheckPoint) { dwStartTickCount = GetTickCount(); dwOldCheckPoint = status.dwCheckPoint; } else { if ((GetTickCount()-dwStartTickCount) > status.dwWaitHint) { goto __Cleanup; } } } if (!StartService(service, argc, argv)) { goto __Cleanup; } if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } dwStartTickCount = GetTickCount(); dwOldCheckPoint = status.dwCheckPoint; while (status.dwCurrentState == SERVICE_START_PENDING) { dwWaitTime = status.dwWaitHint / 10; if(dwWaitTime < 1000) { dwWaitTime = 1000; } else if (dwWaitTime > 10000) { dwWaitTime = 10000; } ::Sleep(dwWaitTime); if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if (status.dwCheckPoint > dwOldCheckPoint) { dwStartTickCount = GetTickCount(); dwOldCheckPoint = status.dwCheckPoint; } else { if ((GetTickCount()-dwStartTickCount) > status.dwWaitHint) { break; } } } __Cleanup: if (status.dwCurrentState==SERVICE_RUNNING) { result = 0; } if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::Stop(const char *name) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; SERVICE_STATUS status = {0}; DWORD dwStartTime = GetTickCount(); DWORD dwTimeout = 30000; DWORD dwWaitTime = 0; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_STOP|SERVICE_QUERY_STATUS); if (!service) { goto __Cleanup; } if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if (status.dwCurrentState == SERVICE_STOPPED) { goto __Cleanup; } while (status.dwCurrentState == SERVICE_STOP_PENDING) { dwWaitTime = status.dwWaitHint / 10; if(dwWaitTime < 1000) { dwWaitTime = 1000; } else if (dwWaitTime > 10000) { dwWaitTime = 10000; } ::Sleep(dwWaitTime); if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if (status.dwCurrentState == SERVICE_STOPPED) { goto __Cleanup; } if ((GetTickCount() - dwStartTime) > dwTimeout) { goto __Cleanup; } } if (!ControlService(service, SERVICE_CONTROL_STOP, &status)) { goto __Cleanup; } while (status.dwCurrentState != SERVICE_STOPPED) { ::Sleep(status.dwWaitHint); if (!QueryServiceStatus(service, &status)) { goto __Cleanup; } if (status.dwCurrentState == SERVICE_STOPPED) { goto __Cleanup; } if ( GetTickCount() - dwStartTime > dwTimeout ) { goto __Cleanup; } } __Cleanup: if (status.dwCurrentState == SERVICE_STOPPED) { result = 0; } if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::GetCurrentStatus(const char *name) { assert(name); int result = -1; SC_HANDLE scmanager = 0; SC_HANDLE service = 0; SERVICE_STATUS status = {0}; scmanager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!scmanager) { goto __Cleanup; } service = ::OpenService(scmanager, name, SERVICE_QUERY_STATUS); if (!service) { goto __Cleanup; } if (QueryServiceStatus(service, &status)) { result = status.dwCurrentState; } __Cleanup: if (service) { ::CloseServiceHandle(service); } if (scmanager) { ::CloseServiceHandle(scmanager); } return result; } int NTServiceManager::GetRealStartupType(SERVICE_STARTUP_TYPE type) { switch (type) { case SST_AUTO_START: { return SERVICE_AUTO_START; } case SST_DEMAND_START: { return SERVICE_DEMAND_START; } case SST_DISABLED: default: { return SERVICE_DISABLED; } } } NTServiceHandler::NTServiceHandler(const std::string &name, bool canstop, bool canshutdown, bool canpausecontinue, ServiceTask &service) : m_name(name), m_service(service) { memset(&m_status, 0, sizeof(SERVICE_STATUS)); m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; m_status.dwCurrentState = SERVICE_START_PENDING; m_status.dwWin32ExitCode = NO_ERROR; m_status.dwServiceSpecificExitCode = 0; m_status.dwCheckPoint = 0; m_status.dwWaitHint = 0; if (canstop) { m_status.dwControlsAccepted |= SERVICE_ACCEPT_STOP; } if (canshutdown) { m_status.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; } if (canpausecontinue) { m_status.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; } m_status_handle = 0; } NTServiceHandler::~NTServiceHandler() { } int NTServiceHandler::Run() { SERVICE_TABLE_ENTRY st[] = { {(LPSTR)m_name.c_str(), (LPSERVICE_MAIN_FUNCTION)ServiceMainHandler}, {NULL, NULL} }; return ::StartServiceCtrlDispatcher(st) ? 0 : -1; } void NTServiceHandler::ServiceMainHandler(DWORD argc, LPSTR *argv) { NTServiceHandler *m_this = GetInstance(); if (m_this) { m_this->m_status_handle = RegisterServiceCtrlHandler(m_this->m_name.c_str(), (LPHANDLER_FUNCTION)ServiceCtrlHandler); if (!m_this->m_status_handle) { return; } m_this->NTS_Start(argc, argv); } } void NTServiceHandler::ServiceCtrlHandler(DWORD ctrl) { NTServiceHandler *m_this = GetInstance(); if (m_this) { switch (ctrl) { case SERVICE_CONTROL_STOP: { m_this->NTS_Stop(); } break; case SERVICE_CONTROL_PAUSE: { m_this->NTS_Pause(); } break; case SERVICE_CONTROL_CONTINUE: { m_this->NTS_Continue(); } break; case SERVICE_CONTROL_SHUTDOWN: { m_this->NTS_Shutdown(); } break; case SERVICE_CONTROL_INTERROGATE: default: break; } } } void NTServiceHandler::NTS_Start(DWORD argc, LPSTR *argv) { try { SetServiceStatus(SERVICE_START_PENDING); if (OnInit(argc, argv) != 0) { exit(0); } SetServiceStatus(SERVICE_RUNNING); OnStart(); SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { SetServiceStatus(SERVICE_STOPPED, dwError); } catch (...) { SetServiceStatus(SERVICE_STOPPED); } } void NTServiceHandler::NTS_Stop() { DWORD dwOriginalState = m_status.dwCurrentState; try { SetServiceStatus(SERVICE_STOP_PENDING); OnStop(); SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { dwError = 0; SetServiceStatus(dwOriginalState); } catch (...) { SetServiceStatus(dwOriginalState); } } void NTServiceHandler::NTS_Pause() { try { SetServiceStatus(SERVICE_PAUSE_PENDING); OnPause(); SetServiceStatus(SERVICE_PAUSED); } catch (DWORD dwError) { dwError = 0; SetServiceStatus(SERVICE_RUNNING); } catch (...) { SetServiceStatus(SERVICE_RUNNING); } } void NTServiceHandler::NTS_Continue() { try { SetServiceStatus(SERVICE_CONTINUE_PENDING); OnContinue(); SetServiceStatus(SERVICE_RUNNING); } catch (DWORD dwError) { dwError = 0; SetServiceStatus(SERVICE_PAUSED); } catch (...) { SetServiceStatus(SERVICE_PAUSED); } } void NTServiceHandler::NTS_Shutdown() { try { OnShutdown(); SetServiceStatus(SERVICE_STOPPED); } catch (DWORD dwError) { dwError = 0; } catch (...) { } } void NTServiceHandler::SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { assert(m_status_handle); static DWORD dwCheckPoint = 0; m_status.dwCurrentState = dwCurrentState; m_status.dwWin32ExitCode = dwWin32ExitCode; m_status.dwWaitHint = dwWaitHint; if (dwCurrentState==SERVICE_RUNNING || dwCurrentState==SERVICE_STOPPED) { m_status.dwCheckPoint = 0; } else { m_status.dwCheckPoint = ++dwCheckPoint; } ::SetServiceStatus(m_status_handle, &m_status); } int NTServiceHandler::OnInit(int argc, char **argv) { return m_service.OnInit(argc, argv); } void NTServiceHandler::OnStart() { m_service.OnStart(); } void NTServiceHandler::OnStop() { m_service.OnStop(); } void NTServiceHandler::OnPause() { m_service.OnPause(); } void NTServiceHandler::OnContinue() { m_service.OnContinue(); } void NTServiceHandler::OnShutdown() { m_service.OnShutdown(); }