本文是Devour Heavens撰写整理的关于windows服务的知识,所有资料均来自于msdn官方文档,欢迎转载。但是为了尊重原作者的劳动,请注明出处!谢谢!
windows服务
1.基本概念
服务是一种在系统中常驻的程序,服务可以在系统启动时自启动(先于用户登录)。当一个应用程序需要常驻在系统,或者随时为其他应用程序提供服务时,可以使用服务应用程序。一般编写网络服务端程序时需要使用服务。windows系统中具有一个服务控制器(SCM)用于控制服务。
1.1 服务控制器(SCM)
服务控制器对系统中所有服务进行管理,SCM管理着系统中已经安装的服务应用程序和设备驱动程序数据库,数据库中保存的信息包括系统安装了哪些服务,每个服务包括包括如何启动每个服务、各个服务的安全属性以及控制接口等。
服务程序、服务配置程序和服务控制程序的设计都需要使用SCM提供的函数。
1.2 服务程序(Service Programs)
一个服务程序包含的可执行代码可以为一个或多个服务使用。服务程序以SERVICE_WIN32_OWN_PROCESS类型创建表示为一个服务拥有,以SERVICE_WIN32_SHARE_PROCESS创建表示为多个服务所共享。SERVICE_FILE_SYSTEM_PROCESS 类型表示文件系统驱动服务,SERVICE_KERNEL_DRIVER 类型表示驱动服务。
一个服务程序至少包括入口函数、服务主函数、控制处理函数。
1.2.1 入口函数
入口函数是可执行程序的起点,服务程序的入口函数与一般可执行程序的入口函数没有区别。
1.2.2 服务主函数ServiceMain
服务主函数一般称作ServiceMain函数。但是服务主函数的名称与线程函数ThreadPro一样,其函数名并没有特殊要求,只是其参数接口和调用类型必须与要求一致。ServiceMain函数的原型如下:
VOID WINAPI ServiceMain(
DWORD dwArgc,
LPTSTR *lpszArgv);
服务函数的参数与main函数的参数使用方法类似,但是服务主函数的参数不是通过在命令行自启动时设定的,而是通过SCM的相关API进行传递的(StartService)。
向SCM注册服务的主函数StartServiceCtrlDispatcher,服务程序通过调用它设置服务主函数,同时通知SCM。
StartServiceCtrlDispatcher函数的原型如下:
BOOL WINAPI StartServiceCtrlDispatcher(
const SERVICE_TABLE_ENTRY *lpServiceTable
);
结构体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,这个函数就成为了服务主函数。
1.2.3 控制处理函数
控制处理函数用于处理SCM向服务传递的服务控制请求。控制处理函数Handler原型如下:
VOID WINAPI Handler(
DWORD fdwControl
);
与ServiceMain函数一致,其函数名没有特殊要求。
注册控制管理函数API函数RegisterServiceCtrlHandler用于向SCM设置一个服务的控制处理函数。
SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler(
LPCTSTR lpServiceName,
LPHANDLER_FUNCTION lpHandlerProc
);
其中lpServiceName为服务名称,lpHandlerProc为Handler函数指针。
1.2.4 服务程序解析
#include <windows.h>
SERVICE_STATUS SplSrvServiceStatus;
SERVICE_STATUS_HANDLE SplSrvServiceStatusHandle;
VOID SvcDebugOut(LPSTR String, DWORD Status);
VOID WINAPI SplSrvServiceCtrlHandler (DWORD opcode);
VOID WINAPI SplSrvServiceStart (DWORD argc, LPTSTR *argv);
DWORD SplSrvServiceInitialization (DWORD argc, LPTSTR *argv,
DWORD *specificError);
VOID WINAPI SplSrvServiceStart (DWORD argc, LPTSTR *argv)
{
DWORD status;
DWORD specificError;
// 填充SERVICE_STATUS 结构
SplSrvServiceStatus.dwServiceType = SERVICE_WIN32;
SplSrvServiceStatus.dwCurrentState
= SERVICE_START_PENDING;
SplSrvServiceStatus.dwControlsAccepted
= SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
SplSrvServiceStatus.dwWin32ExitCode = 0;
SplSrvServiceStatus.dwServiceSpecificExitCode = 0;
SplSrvServiceStatus.dwCheckPoint = 0;
SplSrvServiceStatus.dwWaitHint = 0;
// 注册服务控制请求处理例程
SplSrvServiceStatusHandle = RegisterServiceCtrlHandler(
"Sample_Srv",
SplSrvServiceCtrlHandler);
if (SplSrvServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
SvcDebugOut(" [SPLSRV_SERVICE] RegisterServiceCtrlHandler "
"failed %d\n", GetLastError());
return;
}
status = SplSrvServiceInitialization(argc,argv, &specificError);
if (status != NO_ERROR)
{
SplSrvServiceStatus.dwCurrentState = SERVICE_STOPPED;
SplSrvServiceStatus.dwCheckPoint = 0;
SplSrvServiceStatus.dwWaitHint = 0;
SplSrvServiceStatus.dwWin32ExitCode = status;
SplSrvServiceStatus.dwServiceSpecificExitCode = specificError;
SetServiceStatus (SplSrvServiceStatusHandle, &SplSrvServiceStatus);
return;
}
SplSrvServiceStatus.dwCurrentState = SERVICE_RUNNING;
SplSrvServiceStatus.dwCheckPoint = 0;
SplSrvServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus (SplSrvServiceStatusHandle, &SplSrvServiceStatus))
{
status = GetLastError();
SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ld\n",status);
}
SvcDebugOut(" [SPLSRV_SERVICE] Returning the Main Thread \n",0);
return;
}
DWORD SplSrvServiceInitialization(DWORD argc,
LPTSTR *argv,
DWORD *specificError)
{
return(0);
}
VOID WINAPI SplSrvServiceCtrlHandler (DWORD Opcode)
{
DWORD status;
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
SplSrvServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
SplSrvServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
SplSrvServiceStatus.dwWin32ExitCode = 0;
SplSrvServiceStatus.dwCurrentState = SERVICE_STOPPED;
SplSrvServiceStatus.dwCheckPoint = 0;
SplSrvServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus (SplSrvServiceStatusHandle,
&SplSrvServiceStatus))
{
status = GetLastError();
SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ld\n",
status);
}
SvcDebugOut(" [SPLSRV_SERVICE] Leaving SplSrvService \n",0);
return;
case SERVICE_CONTROL_INTERROGATE:
MessageBeep(MB_OK);
break;
default:
SvcDebugOut(" [SPLSRV_SERVICE] Unrecognized opcode %ld\n",
Opcode);
}
if (!SetServiceStatus (SplSrvServiceStatusHandle, &SplSrvServiceStatus))
{
status = GetLastError();
SvcDebugOut(" [SPLSRV_SERVICE] SetServiceStatus error %ld\n",
status);
}
return;
}
void main( )
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ "Sample_Srv", (LPSERVICE_MAIN_FUNCTION) SplSrvServiceStart },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher( DispatchTable))
{
SvcDebugOut(" [SPLSRV_SERVICE] StartServiceCtrlDispatcher (%d)\n",
GetLastError());
}
}
VOID SvcDebugOut(LPSTR String, DWORD Status)
{
CHAR Buffer[1024];
if (strlen(String) < 1000)
{
wsprintf(Buffer, String, Status);
OutputDebugString(Buffer);
}
}