最近接触的工程是B/S、C/S结构的,跟之前做单机版的应用程序有很大的不同。后端很多功能都做成服务的形式。以前从来没有接触服务这个方面的东西,故趁此机会来学习一下如何制作Windows后台服务程序。
看了公司的服务程序是如何做的,因为采用了第三库,库中提供了一个类来封装NTService功能,而我想知道的更彻底些,就在网上查看了一些资料,搭配着看了那个类的底层细节。总体感觉NTService程序并不难编写,关键要知道实现思路和与之相关的一些API。
简单来说,就是Windows系统有一个Service的数据库,把自己编写的服务加进去(安装),再利用Windows本身提供的控制机制来控制服务的启动暂停停止等,如果卸载就将服务从Service表中删除。具体的内容参考MSDN和网络资料,下面有几个相关链接:
http://blog.csdn.net/todototry/archive/2007/04/16/1566442.aspx
http://blog.csdn.net/todototry/archive/2007/04/16/1566443.aspx
http://blog.csdn.net/todototry/archive/2007/04/16/1566451.aspx
http://blog.csdn.net/todototry/archive/2007/04/16/1566455.aspx
http://www.vckbase.com/document/viewdoc/?id=1474
http://www.vckbase.com/document/viewdoc/?id=1447
http://blog.csdn.net/feijj2002_/archive/2007/08/05/1727228.aspx
为了加上印象,毕竟纸上得来终觉浅,就模仿一些代码写了个简单的NTService。
留个爪子印:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <assert.h>
#define SZSERVICENAME "TNTService"
#define SZSERVICEDISPLAYNAME "TNTService_Desc"
void WINAPI TServiceMain(DWORD argc, LPTSTR * argv);
void InstallService();
void RemoveService();
void StartService();
void StopService();
void LogEvent(LPCTSTR pFormat, ...);
void Start();
void Stop();
//real service function
DWORD WINAPI RealService(LPVOID lpvThread);
SERVICE_STATUS servicestatus;
SERVICE_STATUS_HANDLE servicestatushandle;
int main(int argc, char * argv[])
{
if(argc == 2)
{
if(::strcmp(argv[1]+1, "i")==0)//i = install
{
InstallService();
}
else if (::strcmp(argv[1]+1, "r")==0)//r = remove
{
RemoveService();
}
else if (::strcmp(argv[1]+1, "s")==0)//s = start
{
StartService();
}
else if (::strcmp(argv[1]+1, "k")==0)//k = kill
{
StopService();
}
return 0;
}
SERVICE_TABLE_ENTRY service_table_entry[] =
{
{ SZSERVICENAME, TServiceMain },
{ NULL, NULL }
};
::StartServiceCtrlDispatcher(service_table_entry);
return 0;
}
void InstallService()
{
SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
assert(handle != NULL);
char szFilename[256] = {'/0'};
::GetModuleFileName(NULL, szFilename, 255);
SC_HANDLE hService = ::CreateService(handle,
SZSERVICENAME,
SZSERVICEDISPLAYNAME,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_IGNORE,
szFilename,
NULL,
NULL,
NULL,
NULL,
NULL);
assert(hService != NULL);
::CloseServiceHandle(hService);
::CloseServiceHandle(handle);
}
void RemoveService()
{
SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
assert(handle != NULL);
SC_HANDLE hService = OpenService(handle, SZSERVICENAME, SERVICE_ALL_ACCESS);
assert(hService != NULL);
DeleteService(hService);
}
void StartService()
{
SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
assert(handle != NULL);
SC_HANDLE hService = OpenService(handle, SZSERVICENAME, SERVICE_ALL_ACCESS);
assert(hService != NULL);
StartService(hService, 0, NULL);
}
void StopService()
{
SC_HANDLE handle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
assert(handle != NULL);
SC_HANDLE hService = OpenService(handle, SZSERVICENAME, SERVICE_ALL_ACCESS);
assert(hService != NULL);
ControlService (hService, SERVICE_CONTROL_STOP, &servicestatus);
}
void WINAPI ServiceCtrlHandler(DWORD dwControl)
{
switch (dwControl)
{
//下面虽然添加了暂停、继续等请求的处理代码,但没有实际作用
//这是为什么呢?到了下面的TServiceMain函数里面就明白了...
case SERVICE_CONTROL_PAUSE:
servicestatus.dwCurrentState = SERVICE_PAUSE_PENDING;
// TODO: add code to set dwCheckPoint & dwWaitHint
// This value need to try a lot to confirm
// ...
::SetServiceStatus(servicestatushandle, &servicestatus);
// TODO: add code to pause the service
// not called in this service
// ...
servicestatus.dwCurrentState = SERVICE_PAUSED;
// TODO: add code to set dwCheckPoint & dwWaitHint to 0
break;
case SERVICE_CONTROL_CONTINUE:
servicestatus.dwCurrentState = SERVICE_CONTINUE_PENDING;
// TODO: add code to set dwCheckPoint & dwWaitHint
::SetServiceStatus(servicestatushandle, &servicestatus);
// TODO: add code to unpause the service
// not called in this service
// ...
servicestatus.dwCurrentState = SERVICE_RUNNING;
// TODO: add code to set dwCheckPoint & dwWaitHint to 0
break;
case SERVICE_CONTROL_STOP:
servicestatus.dwCurrentState = SERVICE_STOP_PENDING;
// TODO: add code to set dwCheckPoint & dwWaitHint
::SetServiceStatus(servicestatushandle, &servicestatus);
// TODO: add code to stop the service
Stop();
servicestatus.dwCurrentState = SERVICE_STOPPED;
// TODO: add code to set dwCheckPoint & dwWaitHint to 0
break;
case SERVICE_CONTROL_SHUTDOWN:
// TODO: add code for system shutdown
// as quick as possible
break;
case SERVICE_CONTROL_INTERROGATE:
// TODO: add code to set the service status
// ...
servicestatus.dwCurrentState = SERVICE_RUNNING;
break;
}
::SetServiceStatus(servicestatushandle, &servicestatus);
}
void WINAPI TServiceMain(DWORD argc, LPTSTR * argv)
{
servicestatus.dwServiceType = SERVICE_WIN32;
servicestatus.dwCurrentState = SERVICE_START_PENDING;
servicestatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;//必须为STOP才能接受外部命令
servicestatus.dwWin32ExitCode = 0;
servicestatus.dwServiceSpecificExitCode = 0;
servicestatus.dwCheckPoint = 0;
servicestatus.dwWaitHint = 0;
servicestatushandle =
::RegisterServiceCtrlHandler(SZSERVICENAME, ServiceCtrlHandler);
if (servicestatushandle == (SERVICE_STATUS_HANDLE)0)
{
return;
}
bool bInitialized = false;
Start();
bInitialized = true;
servicestatus.dwCheckPoint = 0;
servicestatus.dwWaitHint = 0;
servicestatus.dwCurrentState = SERVICE_RUNNING;
::SetServiceStatus(servicestatushandle, &servicestatus);
//创建服务线程 服务完成的功能在这里调用
HANDLE hThread = CreateThread(NULL, 0, RealService, NULL, 0, NULL);
assert(hThread != NULL);
CloseHandle(hThread);
return;
}
void Start()
{
LogEvent("Service Starting...");
}
void LogEvent(LPCTSTR pFormat, ...)
{
TCHAR chMsg[256];
HANDLE hEventSource;
LPTSTR lpszStrings[1];
va_list pArg;
va_start(pArg, pFormat);
_vstprintf(chMsg, pFormat, pArg);
va_end(pArg);
lpszStrings[0] = chMsg;
if (1)
{
// Get a handle to use with ReportEvent().
hEventSource = RegisterEventSource(NULL, SZSERVICENAME);
if (hEventSource != NULL)
{
// Write to event log.
ReportEvent(hEventSource, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (LPCTSTR*) &lpszStrings[0], NULL);
DeregisterEventSource(hEventSource);
}
}
else
{
// As we are not running as a service, just write the error to the console.
_putts(chMsg);
}
}
void Stop()
{
LogEvent("Service Stoped.");
}
DWORD WINAPI RealService(LPVOID lpvThread)
{
//实现实际功能的地方
return 1;
}