Windows服务,也称NT服务,提供将服务器转换为可以用命令或者在启动时初始化的服务所需的管理能力,初始化发生 在任何用户登录之 前,服务可以暂停、恢复、终止、监控。
注册表维护与服务的有关信息。
(1)三个步骤:
1)创建一个新的main进入点用户将服务注册给SCM,提供逻辑的服务器进入点和名称;
2)将老的main()进入点函数转换为ServiceMain(),在这里注册服务控制处理程序并将其状态通知SCM。
ServiceMain()这个名称是逻辑服务名的占位符,在单个进程中可以有一个或多个逻辑服务;
3)编写服务控制处理程序函数以便对来自SCM的命令做出响应
下面的图显示了步骤流程:
1)在程序入口点(如main)中,向SCM注册服务的主函数和名称
通过StartServiceCtrlDispatcher函数。
BOOL StartServiceCtrlDispatcher(
SERVICE_TABLE_ENTRY *lpServiceStartTable
)
惟一的参数 lpServiceStartTable是SERVICE_TABLE_ENTRY项的数组的地址,每个项都是逻辑服务名称和进入点。数组的末尾通过一对NULL进入点来表示
如果注册成功返回TRUE。
SERVICE_TABLE_ENTRY的原型为:
typedef struct _SERVICE_TABLE_ENTRY{
LPTSTR lpServiceName;
LPSERVICE_MAIN_FUNCTION lpServiceProc;
}SERVICE_TABLE_ENTRY, *LPSERVICE_TABLE_ENTRY;
其中 lpServiceName为服务名称, lpServiceProc为指向ServiceMain的函数指针。
只要将函数的指针赋值给lpServiceProc,再调用StartServiceCtrlDispatcher,这样,这个函数( ServiceMain )就成为了服务主函数。
main:主服务进入点
static LPTSTR serviceName = _T("MyService");
//Main routine that start the service control dispatcher
VOID _tmain(int argc, LPTSTR argv[])
{
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{serviceName, ServiceMain},
{NULL, NULL}
};
if(!StartServiceCtrlDispatcher(dispatcherTable))
{
printf("StartServiceCtrlDispatcher error\n");
}
//ServiceMain() will not run until started by SCM
//Return here only when all services have terminated
return;
}
2)在 ServiceMain()中注册服务控制处理程序并将其状态通知SCM
服务主函数一般称为ServiceMain函数,其函数名没有什么特殊要求, 但是其参数借口和调用类型,必须和要求一致。
ServiceMain函数的原型:
VOID WINAPI ServiceMain(
DWORD dwArgc,
LPTSTR* lpszArgv
);
服务主函数的参数与main函数的参数使用方法相似,但是服务主函数的参数不是通过在命令行启动时设定的,
而是通过SCM的相关API进行传递的(StartService函数,后面介绍)
在 ServiceMain()中 注册服务控制处理程序:
每个逻辑服务必须使用RegisterServiceHandlerEx立即注册一个处理程序,这个函数调用必须位于ServiceMain的开始处,而且不能再次调用。
SCM在接收到服务的控制请求之后,会调用这个处理程序。
RegisterServiceHandlerEx函数原型:
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerEx(
LPCTSTR lpServiceName,
LPHANDLER_FUNCTION_EX lpHandlerPorc,
LPVOID lpContext
)
lpServiceName为服务名称,和使用StartServiceCtrlDispatcher向SCM注册的服务名称相同。
lpHandlerProc是扩展处理程序函数的地址。
lpContext是用户定义的要传递给控制处理程序的数据。一般可省略
返回值是一个 SERVICE_STATUS_HANDLE对象,如果为0表示有误。
在 ServiceMain()中设置服务状态 :
BOOL SetServiceStatus(
SERVICE_STATUS_HANDLE hServiceStatus,
LPSERVICE_STATUS lpServiceStatus
)
hServiceStatus是RegisterServiceHandleEx返回的 SERVICE_STATUS_HANDLE 。
lpServicesStatus指向一个SERVICE_STATUS结构,这个结构描述服务属性、状态和能力。
SERVICE_STATUS结构的定义:
typedef struct _SERVICE_STATUS {
DWORD dwServiceType;
DWORD dwCurrentState;
DWORD dwControlsAccepted;
DWORD dwWin32ExitCode;
DWORD dwServiceSpecificExitCode;
DWORD dwCheckPoint;
DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;
具体参数含义见MSDN。
ServiceMain:
{
ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ServiceStatus.dwCurrentState = SERVICE_START_PENDING ;
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwServiceSpecificExitCode = 0;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
hStatus = RegisterServiceCtrlHandler("serviceName",(LPHANDLER_FUNCTION)HandlerEx);
if (hStatus == (SERVICE_STATUS_HANDLE)0)
{
// Registering Control Handler failed
return;
}
ServiceStatus.dwCurrentState = SERVICE_RUNNING ;
SetServiceStatus (hStatus, &ServiceStatus);
}
3)编写服务控制处理程序函数以便对来自SCM的命令做出响应服务控制处理程序是一个在RegisterServiceCtrlHandleEx中指定的回调函数。
DWORD WINAPI HandlerEx(
DWORD dwControl,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext
)
dwControl参数表示由SCM发送的应该被处理的实际控制信号.
dwEventType通常是0,而非零值用于设备管理。一般可省略
lpEventData提供这些事件中的一些所需要的额外数据。一般可省略
lpContext是在注册处理程序时,传递给RegisterServiceCtrlHandler的用户自定义数据。一般可省略
HandlerEx:
{
switch(request)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (hStatus, &ServiceStatus);
break;
case SERVICE_CONTROL_PAUSE:
ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_PAUSED ;
SetServiceStatus (hStatus, &ServiceStatus);
break;
case SERVICE_CONTROL_CONTINUE:ServiceStatus.dwWin32ExitCode = 0;
ServiceStatus.dwCurrentState = SERVICE_RUNNING ;
SetServiceStatus (hStatus, &ServiceStatus);
break;
default:
break;
}
// Report current status
SetServiceStatus (hStatus, &ServiceStatus);
return;
}
2)停止服务和查询服务:
3)删除服务:
4)文本记录日志内容
注意:如果使用Windows 7 或Windows8 系统,sc create 服务名称 binPath= "exe应用程序路径" 可能会创建失败,提示错误是5,无法访问。 这是权限的问题,你可以把创建的命令放在批处理文件里面,然后右击选择管理员身份运行就Ok了。