您的位置:首页 > 其它

如何创建 windows NT/Windows 2000 服务(Service)

2011-07-23 12:37 495 查看
原文:http://www.codeproject.com/KB/system/windows_nt_service.aspx
没有严格按原文翻译,只是写了原文的大概意思。
作者是一名来自印度的开发员,叫 Anish C.V
(国内的相关文章,我用 google 搜索了一下,大部分是直接一段代码,没有基础的讲解,对于初接触者来说,是比较难以理解的,一些 API 的调用,根本不知道其目的是什么。这篇文章虽然不是面面俱到,但对整个开发所涉及的一些背景作了比较详细的介绍,对于新手理解究竟什么是服务,与普通应用程序有什么区别,非常有帮助)
-------------------------------------------
Windows Service 是特别为 SCM (Service Control Manager 服务控制管理器) 编写的可执行文件,需要与 SCM 进行通讯。
SCM 是一个 RPC 服务器 (Remote Procedure Call),在系统启动时被加载,在系统桌面启动之前就被执行,隐藏在后台进行一些系统服务。他管理着一个数据库,里面存储了当前已经安装的各种服务程序 (Service),可对已安装的服务进行控制及管理,如启动某一项服务,暂时,重启,停止等操作。

作为一名开发者,我们想实现一个简单的服务 (Service),可以分四部分来编写。

1. 创建一个 Win32/Console 控制台应用。
2. 创建一个名字诸如 ServiceMain() 的函数来作为服务的主程序,服务的入口函数。
3. 创建一个服务控制处理函数,用来与 SCM 进行通讯。
4. 创建 安装/删除服务的函数,来注册登记一个可执行文件到 SCM 管理的服务数据库中去。

首先,让我们来看一下控制台应用程序的主函数。
#include "Winsvc.h"  //Header file for Services.

main(){
SERVICE_TABLE_ENTRY Table[]={{"Service1", ServiceMain}, {NULL, NULL}};
StartServiceCtrlDispatcher(Table);
}

这里唯一要做的就是填充一个 SERVICE_TABLE_ENTRY 数组,第一个元素 [0][0] 包含了服务的名称,可以是任何字符串,随你喜欢。 第二个元素[0][1]是服务的主程序,当然也你也可以命名为其它,只要各处保持一致就行了。接下来就是调用 StartServiceCtrlDispatcher,将Table 数组传进去。数据中后面那两个 NULL 是表示数组结束,这里只传入一个服务的名称及入口函数,你也可以在同一个可执行文件里,实现多个服务,将其一一添加到这个数组中。

典型的 ServiceMain() 入口函数定义:
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)

接下来分析 ServiceMain() 函数。
其主要步骤是:
1. 填充用于和 SCM 通讯的 SERVICE_STATUS 结构体的成员值。
2. 注册前面所说的服务控制处理函数。
3. 调用实际的处理函数

这里我们需要两个全局变量:
SERVICE_STATUS m_ServiceStatus;
SERVICE_STATUS_HANDLE m_ServiceStatusHandle;

我们需要通过 SERVICE_STATUS 的数据类型来通知 SCM 当前我们的服务是什么状态,调用 SetServiceStatus(),传入 m_ServiceStatus 来达成目的。这个结构体的重要成员变量如下:
m_ServiceStatus.dwServiceType = SERVICE_WIN32;
m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;  // 表示正在启动初始化

m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; 在服务控制程序(如NT命令行)只能接受 启动/停止,通常在控制面板的管理工具里,还可以设置服务接受 暂停/继续。
在服务主程序 ServiceMain 的开头,我们要设置 m_ServiceStatus.dwCurrentState 为 SERVICE_START_PENDING,来标记服务正在启动中,如果有错误发生,则设置为 SERVICE_STOPPED来通知 SCM。默认情况下,SCM会检查服务的可用性,如果2分钟内没有响应,没有正常启动,则 SCM 会终止这个服务。

API 函数 RegisterServiceCtrlHandler() 用来设置和 SCM 通讯的服务控制处理函数(Service Control Handler),有两个参数,一个是服务名,如:"Service1",另一个是处理函数名,如:ServiceCtrlHandler,当 SCM 要求服务更改状态时,会发送信号给 ServiceCtrlHandler 处理函数。

到了这里,我们就可以告诉 SCM,服务已经启动,设置 dwCurrentState 为 SERVICE_RUNNING。接下来,调用实际要做的处理。

再来分析一下 服务控制处理函数(Service Control Handler)。这是用来与 SCM 通讯的函数,通常会收到 如 start, stop, pause 或 pause 的用户命令,通过一个变量发送到处理函数,它的值是:SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_STOP, SERVICE_CONTROL_INTERROGATE 等,要对每个编写处理程序。

安装及删除服务
这需要在系统注册表里添加节点,可以调用 API 来实现,主要是 CreateService() 和 DeleteService(),在使用前,要取得 SCM 服务数据库,调用的函数是 OpenSCManager(),具体查看下面的示例代码。

示例代码:
#include "stdafx.h"
#include "Windows.h"
#include "Winsvc.h"
#include "time.h"

SERVICE_STATUS m_ServiceStatus; SERVICE_STATUS_HANDLE m_ServiceStatusHandle;
BOOL bRunning=true;
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD Opcode);
BOOL InstallService();
BOOL DeleteService();
int main(int argc, char* argv[])
{
if(argc>1)
{
if(strcmp(argv[1],"-i")==0)
{
if(InstallService())
printf("\n\nService Installed Sucessfully\n");
else
printf("\n\nError Installing Service\n");
}
if(strcmp(argv[1],"-d")==0)
{
if(DeleteService())
printf("\n\nService UnInstalled Sucessfully\n");
else
printf("\n\nError UnInstalling Service\n");
}
else
{
printf("\n\nUnknown Switch Usage\n\nFor Install
use Srv1 -i\n\nFor UnInstall use Srv1 -d\n");
}
}
else
{
SERVICE_TABLE_ENTRY DispatchTable[]=
{{"Service1",ServiceMain},{NULL,NULL}};
StartServiceCtrlDispatcher(DispatchTable);
}
return 0;
}

void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
DWORD status;
DWORD specificError;
m_ServiceStatus.dwServiceType = SERVICE_WIN32;
m_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
m_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwServiceSpecificExitCode = 0;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;

m_ServiceStatusHandle = RegisterServiceCtrlHandler("Service1",
ServiceCtrlHandler);
if (m_ServiceStatusHandle == (SERVICE_STATUS_HANDLE)0)
{
return;
}
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;
if (!SetServiceStatus (m_ServiceStatusHandle, &m_ServiceStatus))
{
}

bRunning=true;
while(bRunning)
{
Sleep(3000);
//Place Your Code for processing here....

}
return;
}

void WINAPI ServiceCtrlHandler(DWORD Opcode)
{
switch(Opcode)
{
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
m_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP:
m_ServiceStatus.dwWin32ExitCode = 0;
m_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
m_ServiceStatus.dwCheckPoint = 0;
m_ServiceStatus.dwWaitHint = 0;

SetServiceStatus (m_ServiceStatusHandle,&m_ServiceStatus);
bRunning=false;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
}
return;
}

BOOL InstallService()
{
char strDir[1024];
HANDLE schSCManager,schService;
GetCurrentDirectory(1024,strDir);
strcat(strDir,"\\Srv1.exe");
schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);

if (schSCManager == NULL)
return false;
LPCTSTR lpszBinaryPathName=strDir;

schService = CreateService(schSCManager,"Service1",
"The Display Name Needed", // 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

lpszBinaryPathName, // service's binary

NULL, // no load ordering group

NULL, // no tag identifier

NULL, // no dependencies

NULL, // LocalSystem account

NULL); // no password

if (schService == NULL)
return false;

CloseServiceHandle(schService);
return true;
}

BOOL DeleteService()
{
HANDLE schSCManager;
SC_HANDLE hService;
schSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);

if (schSCManager == NULL)
return false;
hService=OpenService(schSCManager,"Service1",SERVICE_ALL_ACCESS);
if (hService == NULL)
return false;
if(DeleteService(hService)==0)
return false;
if(CloseServiceHandle(hService)==0)
return false;

return true;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: