用C编写Windows处事措施的五个步调
当前位置:以往代写 > C/C++ 教程 >用C编写Windows处事措施的五个步调
2019-06-13

用C编写Windows处事措施的五个步调

用C编写Windows处事措施的五个步调

副标题#e#

Windows 处事被设计用于需要在靠山运行的应用措施以及实现没有用户交互的任务。为了进修这种节制台应用措施的基本常识,C(不是C++)是最佳选择。本文将成立并实现一个简朴的处事措施,其成果是查询系统中可用物理内存数量,然后将功效写入一个文本文件。最后,你可以用所学常识编写本身的 Windows 处事。

当初我写第一个NT 处事时,我到 MSDN 上找例子。在哪里我找到了一篇 Nigel Thompson 写的文章:“Creating a Simple Win32 Service in C++”,这篇文章附带一个 C++ 例子。固然这篇文章很好地表明白处事的开拓进程,可是,我仍然感受缺少我需要的重要信息。我想领略通过什么框架,挪用什么函数,以及何时挪用,但 C++ 在这方面没有让我轻松几多。面向工具的要领当然利便,但由于用类对底层 Win32 函数挪用举办了封装,它倒霉于进修处事措施的根基常识。这就是为什么我以为 C 越发适合于编写低级处事措施可能实现简朴靠山任务的处事。在你对处事措施有了充实透彻的领略之后,用 C++ 编写才气游刃有余。当我分开本来的事情岗亭,不得不向另一小我私家转移我的常识的时候,操作我用 C 所写的例子就很是容易表明 NT 处事之所以然。

处事是一个运行在靠山并实现勿需用户交互的任务的节制台措施。Windows NT/2000/XP 操纵系统提供为处事措施提供专门的支持。人们可以用处事节制面板来设置安装好的处事措施,也就是 Windows 2000/XP 节制面板|打点东西中的“处事”(或在“开始”|“运行”对话框中输入 services.msc /s——译者注)。可以将处事设置成操纵系统启动时自动启动,这样你就不必每次再重启系统后还要手动启动处事。

本文将首先表明如何建设一个按期查询可用物理内存并将功效写入某个文本文件的处事。然后指导你完成生成,安装和实现处事的整个进程。

第一步:主函数和全局界说

首先,包括所需的头文件。例子要挪用 Win32 函数(windows.h)和磁盘文件写入(stdio.h):

#include <windows.h>
#include <stdio.h>

接着,界说两个常量:

#define SLEEP_TIME 5000
#define LOGFILE "C:\\MyServices\\memstatus.txt"

SLEEP_TIME 指定两次持续查询可用内存之间的毫秒隔断。在第二步中编写处事事情轮回的时候要利用该常量。

LOGFILE 界说日志文件的路径,你将会用 WriteToLog 函数将内存查询的功效输出到该文件,WriteToLog 函数界说如下:

int WriteToLog(char* str)
{
 FILE* log;
 log = fopen(LOGFILE, "a+");
 if (log == NULL)
  return -1;
 fprintf(log, "%s\n", str);
 fclose(log);
 return 0;
}

声明几个全局变量,以便在措施的多个函数之间共享它们值。另外,做一个函数的前向界说:

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();

此刻,筹备事情已经停当,你可以开始编码了。处事措施节制台措施的一个子集。因此,开始你可以界说一个 main 函数,它是措施的进口点。对付处事措施来说,main 的代码令人惊奇地简短,因为它只建设分配表并启动节制分配机。

void main()
{
 SERVICE_TABLE_ENTRY ServiceTable[2];
 ServiceTable[0].lpServiceName = "MemoryStatus";
 ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
 ServiceTable[1].lpServiceName = NULL;
 ServiceTable[1].lpServiceProc = NULL;
 // 启动处事的节制分配机线程
 StartServiceCtrlDispatcher(ServiceTable);
}

一个措施大概包括若干个处事。每一个处事都必需列于专门的分配表中(为此该措施界说了一个 ServiceTable 布局数组)。这个表中的每一项都要在 SERVICE_TABLE_ENTRY 布局之中。它有两个域:

lpServiceName: 指向暗示处事名称字符串的指针;当界说了多个处事时,那么这个域必需指定;

lpServiceProc: 指向处事主函数的指针(处事进口点);

分配表的最后一项必需是处事名和处事主函数域的 NULL 指针,文本例子措施中只宿主一个处事,所以处事名的界说是可选的。

处事节制打点器(SCM:Services Control Manager)是一个打点系统所有处事的历程。当 SCM 启动某个处事时,它期待某个历程的主线程来挪用 StartServiceCtrlDispatcher 函数。将分配表通报给 StartServiceCtrlDispatcher。这将把挪用历程的主线程转换为节制分配器。该分配器启动一个新线程,该线程运行分配表中每个处事的 ServiceMain 函数(本文例子中只有一个处事)分配器还监督措施中所有处事的执行环境。然后分配器将节制请求从 SCM 传给处事。

#p#分页标题#e#

留意:假如 StartServiceCtrlDispatcher 函数30秒没有被挪用,便会报错,为了制止这种环境,我们必需在 ServiceMain 函数中(拜见本文例子)或在非主函数的单独线程中初始化处事分配表。本文所描写的处事不需要防御这样的环境。

分配表中所有的处事执行完之后(譬喻,用户通过“处事”节制面板措施遏制它们),可能产生错误时。StartServiceCtrlDispatcher 挪用返回。然后主历程终止。


#p#副标题#e#

第二步:ServiceMain 函数

Listing 1 展示了 ServiceMain 的代码。该函数是处事的进口点。它运行在一个单独的线程傍边,这个线程是由节制分配器建设的。ServiceMain 应该尽大概早早为处事注册节制处理惩罚器。这要通过挪用 RegisterServiceCtrlHadler 函数来实现。你要将两个参数通报给此函数:处事名和指向 ControlHandlerfunction 的指针。

它指示节制分配器挪用 ControlHandler 函数处理惩罚 SCM 节制请求。注册完节制处理惩罚器之后,得到状态句柄(hStatus)。通过挪用 SetServiceStatus 函数,用 hStatus 向 SCM 陈诉处事的状态。

Listing 1 展示了如何指定处事特征和其当前状态来初始化 ServiceStatus 布局,ServiceStatus 布局的每个域都有其用途:

dwServiceType:指示处事范例,建设 Win32 处事。赋值 SERVICE_WIN32;

dwCurrentState:指定处事的当前状态。因为处事的初始化在这里没有完成,所以这里的状态为 SERVICE_START_PENDING;

dwControlsAccepted:这个域通知 SCM 处事接管哪个域。本文例子是答允 STOP 和 SHUTDOWN 请求。处理惩罚节制请求将在第三步接头;

dwWin32ExitCode 和 dwServiceSpecificExitCode:这两个域在你终止处事并陈诉退出细节时很有用。初始化处事时并不退出,因此,它们的值为 0;

dwCheckPoint 和 dwWaitHint:这两个域暗示初始化某个处事历程时要30秒以上。本文例子处事的初始化进程很短,所以这两个域的值都为 0。

挪用 SetServiceStatus 函数向 SCM 陈诉处事的状态时。要提供 hStatus 句柄和 ServiceStatus 布局。留意 ServiceStatus 一个全局变量,所以你可以跨多个函数利用它。ServiceMain 函数中,你给布局的几个域赋值,它们在处事运行的整个进程中都保持稳定,好比:dwServiceType。

在陈诉了处事状态之后,你可以挪用 InitService 函数来完成初始化。这个函数只是添加一个说明性字符串到日志文件。如下面代码所示:

// 处事初始化
int InitService()
{
 int result;
 result = WriteToLog("Monitoring started.");
 return(result);
}

在 ServiceMain 中,查抄 InitService 函数的返回值。假如初始化有错(因为有大概写日志文件失败),则将处事状态置为终止并退出 ServiceMain:

error = InitService();
if (error)
{
 // 初始化失败,终止处事
 ServiceStatus.dwCurrentState = SERVICE_STOPPED;
 ServiceStatus.dwWin32ExitCode = -1;
 SetServiceStatus(hStatus, &ServiceStatus);
 // 退出 ServiceMain
 return;
}

假如初始化乐成,则向 SCM 陈诉状态:

// 向 SCM 陈诉运行状态
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (hStatus, &ServiceStatus);

接着,启动事情轮回。每五秒钟查询一个可用物理内存并将功效写入日志文件。

如 Listing 1 所示,轮回一直随处事的状态为 SERVICE_RUNNING 或日志文件写入堕落为止。状态大概在 ControlHandler 函数响应 SCM 节制请求时修改。

#p#副标题#e#

第三步:处理惩罚节制请求

在第二步中,你用 ServiceMain 函数注册了节制处理惩罚器函数。节制处理惩罚器与处理惩罚各类 Windows 动静的窗口回调函数很是雷同。它查抄 SCM 发送了什么请求并采纳相应动作。

每次你挪用 SetServiceStatus 函数的时候,必需指定处事吸收 STOP 和 SHUTDOWN 请求。Listing 2 示范了如安在 ControlHandler 函数中处理惩罚它们。

STOP 请求是 SCM 终止处事的时候发送的。譬喻,假如用户在“处事”节制面板中手动终止处事。SHUTDOWN 请求是封锁呆板时,由 SCM 发送给所有运行中处事的请求。两种环境的处理惩罚方法沟通:

写日志文件,监督遏制;

向 SCM 陈诉 SERVICE_STOPPED 状态;

由于 ServiceStatus 布局对付整个措施而言为全局量,ServiceStatus 中的事情轮回在当前状态改变或处事终止后遏制。其它的节制请求如:PAUSE 和 CONTINUE 在本文的例子没有处理惩罚。

节制处理惩罚器函数必需陈诉处事状态,即便 SCM 每次发送节制请求的时候状态保持沟通。因此,不管响应什么请求,都要挪用 SetServiceStatus。

用C编写Windows办事法子的五个法式
图一 显示 MemoryStatus 处事的处事节制面板

第四步:安装和设置处事

#p#分页标题#e#

措施编好了,将之编译成 exe 文件。本文例子建设的文件叫 MemoryStatus.exe,将它拷贝到 C:\MyServices 文件夹。为了在呆板上安装这个处事,需要用 SC.EXE 可执行文件,它是 Win32 Platform SDK 中附带的一个东西。(译者注:Visaul Studio .NET 2003 IDE 情况中也有这个东西,详细存放位置在:C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\Bin\winnt)。利用这个实用东西可以安装和移除处事。其它节制操纵将通过处事节制面板来完成。以下是用呼吁行安装 MemoryStatus 处事的要领:

sc create MemoryStatus binpath= c:\MyServices\MemoryStatus.exe

发出此建设呼吁。指定处事名和二进制文件的路径(留意 binpath= 和路径之间的谁人空格)。安装乐成后,便可以用处事节制面板来节制这个处事(拜见图一)。用节制面板的东西栏启动和终止这个处事。

用C编写Windows办事法子的五个法式
图二 MemoryStatus 处事的属性窗口

MemoryStatus 的启动范例是手动,也就是说按照需要来启动这个处事。右键单击该处事,然后选择上下文菜单中的“属性”菜单项,此时显示该处事的属性窗口。在这里可以修改启动范例以及其它配置。你还可以从“通例”标签中启动/遏制处事。以下是从系统中移除处事的要领:

sc delete MemoryStatus

指定 “delete” 选项和处事名。此处事将被标志为删除,下次西通重启后,该处事将被完全移除。

第五步:测试处事

从处事节制面板启动 MemoryStatus 处事。假如初始化不堕落,暗示启动乐成。过一会儿将处事遏制。查抄一下 C:\MyServices 文件夹中 memstatus.txt 文件的处事输出。在我的呆板上输出是这样的:

Monitoring started.
273469440
273379328
273133568
273084416
Monitoring stopped.

为了测试 MemoryStatus 处事在堕落环境下的行为,可以将 memstatus.txt 文件配置成只读。这样一来,处事应该无法启动。

去掉只读属性,启动处事,在将文件设成只读。处事将遏制执行,因为此时日志文件写入失败。假如你更新处事节制面板的内容,会发明处事状态是已经遏制。

开拓更大更好的处事措施

领略 Win32 处事的根基观念,使你能更好地用 C++ 来设计包装类。包装类埋没了对底层 Win32 函数的挪用并提供了一种舒适的通用接口。修改 MemoryStatus 措施代码,建设满意本身需要的处事!为了实现比本文例子所示范的更巨大的任务,你可以建设多线程的处事,将功课分别成几个事情者线程并从 ServiceMain 函数中监督它们的执行。

    关键字:

在线提交作业