分享
 
 
 

用托管C++编写Windows服务

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

多年以来,只要提到编写Windows服务,就会想到用Visual C++编写,同时,这也是其中一件C++程序员可以做,而VB程序员不可以做的事情。以前,我们只称其为"服务"或"NT服务",现在,它们被命名为"Windows服务",而且用VB.NET或C#也可以很轻易地编写。

但是,假如你想用托管C++来编写呢?究竟,大多数有经验的Visual C++程序员都会写过一两个服务,也会知道怎样完成一个类似的工程。假设你有一个必须要一直运行以提供服务的程序,且连接着一些远程电脑,假如不想编写一本使用手册,告诉客户要记得在每次重启电脑之后重新运行此程序,你就应该使它成为一个服务;又假设你有一个用于删除过期数据库记录的便利维护工具,假如不想让治理员每周都亲手运行它一次,你就应该使它成为一个服务。看起来挺吸引人的,那就让我们开始吧。

创建服务工程

以下要做的事情非常简单:打开Visual Studio.NET,创建一个新的工程,在Visual C++工程下,选择Windows服务(.NET)。接下来,为这个服务取一个方便在电脑的服务列表中查找到的名字,在此为CGNotifier。向导会创建一个继续自System::ServicePRocess::ServiceBase的类并打开设计视图,在此,你可放入一个计时器、一个数据库连接,或其他不可见的组件。

让我们转到代码视图中看一下生成的代码,在此有一个构造函数与一个Dispose方法,这两个你都可以忽略,还有一对重载的方法:OnStart()和OnStop)。在OnStart()中,可编写服务所需的代码。服务中一个重要的范畴是使用"事件引发对象",例如System::IO::FileSystemWatcher的一个实例,一般可在OnStart()中创建这些对象,在本例中,你可为类加入事件方法,并处理在服务运行期间,由这些对象引发的事件。另有一种服务,它们对发生的事情不作反应,只在天天或每周的特定时间,执行一些特定的任务,这些服务平时通常处于休眠状态,但因为它们的工作状态是持续的,所以不应该停止它们,或者可以把它们放入一个循环中,在特定的时间检查它们是否已被停止。

OnStart()方法是服务的开始之处,并且会在执行完后返回,在此方法完成之前,服务一般不会显示为"已启动"。这就意味着,不能在OnStart()中放入一个经常使用的循环,或从别处直接调用的任何方法。最直接的方法是设置好一个单独的方法,并在一个新线程中调用它,如下所示:

private:

bool stopping;

int loopsleep; //毫秒

Threading::Thread* servicethread;

protected:

//设置好服务应做的工作

void OnStart(String* args[])

{

Threading::ThreadStart* threadStart =new Threading::ThreadStart(this,mainLoop);

servicethread = new Threading::Thread(threadStart);

servicethread->Start();

}

void mainLoop()

{

loopsleep = 1000; //毫秒

stopping = false;

while (!stopping)

{

Threading::Thread::Sleep(loopsleep);

}

}

这个循环将会一直运行,直到服务停止,因为OnStop()设置了停止标志:

void OnStop()

{

stopping = true;

}

假如你增加loopsleep值,则会在停止时,增加服务的响应时间。

安装服务

尽管这个服务什么也不做,但你仍可对它进行安装、启动和停止。为简化安装过程,可在工程中加入一个安装程序,这可在设计视图中完成(假如你喜欢,可在设计视图中打开属性窗口,并修改ServiceName属性;而向导会在工程名后加上WinService,这最好在添加安装程序之前完成,否则,就需要在多处修改服务名。),鼠标右键单击设计视图,选择添加安装程序。这将创建一个服务安装程序和一个服务过程安装程序,并显示在设计视图中,以供你设置它们的属性。

假如已经阅读了有关Windows服务的 .NET文档,你可能会想为什么要添加一个安装程序呢?难道不可以自动添加吗?实际上,假如是使用VB或C#,是可以自动添加的,而C++却不行。

服务过程安装程序只有一个比较让人感爱好的属性:服务所运行的账户。单击serviceProcessInstaller1选择它,打开其属性窗口。默认情况下,账户属性为User,这意味着在安装服务时,将会提示输入一个ID和密码,而且服务将会运行于user权限下--这在服务运行于system账户时非常有用。通常有三个选项:LocalSystem是服务被安装于未运行Windows 2003的电脑上时的唯一选择;假如服务是面向Windows 2003的,那么LocalService的权限更少,因为是更好的选择;而NetworkService答应服务验证另一台电脑,所以只在需要使用它(例如,一个服务加载了一个web页),相反,在使用公共web服务时,就不需要作为NetworkService运行,因为它不需验证远程电脑。

而服务安装程序中需要注重的属性是StartType:手动、自动、禁用。在此例中为手动。

现在,可以生成服务,并预备安装了。打开Visual Studio命令提示符,定位到工程的Debug文件夹,输入以下命令:

InstallUtil CGNotifier.exe

以下是屏幕的输出:

Microsoft (R) .NET Framework Installation utility

Version 1.1.4322.573

Copyright (C) Microsoft Corporation 1998-2002.

All rights reserved.

Exception occurred while initializing the installation:

System.IO.FileLoadException: Unverifiable image 'CGNotifier.exe'

cannot be run.

这真是难以理解,不是吗?在C++中编写可验证代码向来都是不可能的,且非常难以实现。为什么工程向导创建了一个服务,但却没有提示你代码必须为可验证的呢?其实不必使你的服务程序产生可验证代码。

打开解决方案资源治理器,找到并打开相应的 .cpp文件,你将会发现隐藏在此的一个main()函数--正是这个main()函数以一种"聪明"的方式为你调用了InstallUtil,并产生了整个的"可验证代码"问题。现在回到命令提示符窗口,像以下这样安装服务:

CGNotifier.exe -Install

你可看到服务轻松、流畅地安装上去了。

为进行测试,现在打开"计算机治理",并展开"服务和应用程序"项,选择"服务",你可看到新安装上去的服务:右键单击它选择启动。一旦服务启动,切换回Visual Studio,选择服务器资源治理器查看此服务:依次选择视图、服务器资源治理器,展开你的计算机名,再展开服务,你将看到一个新服务,而带有的绿色三角形表明它正在运行。

在服务器资源治理器中右击此服务,选择停止。现在,请在"事件查看器"中查看事件记录,可看到二个日志记录:一个告诉你服务已启动,而另一个告诉你服务已停止。假如你不想产生事件日志记录,请在服务的设计视图中修改AutoLog属性为False。

卸载服务

假如你从Debug目录中安装此服务,在对它进行修改期间,并不需要卸载,把它停止,重新生成,再启动就行了。但是,假如你想卸载它,请回到Visual Studio命令提示符窗口,定位到Debug目录,输入以下命令:

CGNotifier.exe -Install /u

现在,服务就会从"服务器资源治理器"和"计算机治理"的服务列表中消失了,也许,需要刷新列表才能看到变化。

唤醒后做一些事情

当然,以上所示的服务到目前为止并不能做任何事情,为把它变成一个"在设定时刻唤醒"的服务,第一步应在工程中加入一个配置文件,示例如下:

<configuration>

<appSettings>

<add key="runhour" value="22" />

</appSettings>

</configuration>

另外,还需要复制带应用程序名如app.config文件到目标工程目录(Debug或Release):

copy app.config $(ConfigurationName)\$(TargetFileName).config

为了读取配置,可在OnStart()或mainLoop()中循环之前加入相应的代码,在此倾向于尽可能地保持OnStart()为空,因此在mainLoop()中加入以下代码:

String* sHour = Configuration::ConfigurationSettings::

AppSettings->get_Item("runhour");

int runHour = System::Int32::Parse(sHour);

bool rantoday = false;

而循环则如下所示:

stopping = false;

while (!stopping)

{

if (DateTime::Now.Hour == runHour && !rantoday)

{

//执行相应的任务

rantoday = true;

}

else

rantoday = false;

Threading::Thread::Sleep(loopsleep);

}

因为到了事先约定的时间,只想要上述代码运行一次,因此,在服务执行完相应的任务之后,必须把rantoday标志设为true,只要在其他时间,都会被设为false。

你可以在服务中查找数据库的新记录、或查找过期的文件并删除它们,当然,在服务中可以做的事情远远不只这些。但不管要执行的任务是什么,都需要告诉其他人你做过什么,因为服务不具备一个用户界面,所以也不能弹出一个消息框,因此,使用事件日志是一个不错的方法。

请在mainLoop()的循环之前加入以下代码,以用于设置事件日志记录:

Diagnostics::EventLog* log ;

if (! Diagnostics::EventLog::SourceExists("CGNotifierService") )

Diagnostics::EventLog::CreateEventSource("CGNotifierService",CGNotifierLog");

log = new Diagnostics::EventLog("CGNotifierLog");

log->Source = "CGNotifierService";

虽然不用同时设置日志和源代码,但这样做的话,消息会在服务器浏览器的事件日志之下,创建它们自己的节点。

为向日志中写入,通常只需一行代码--可把它放在"执行相应任务"的注释之后: log->WriteEntry("服务的运行时间到了。",

Diagnostics::EventLogEntryType::Information);

现在,我们大功告成:一个可以安装、卸载、启动、停止,并天天向事件日志中写入一条信息的服务诞生了!从此以后,你将无坚不摧,用C++编写的Windows服务可不像其他那些 .NET应用程序,它只局限于你的想像力。另外,在创建服务工程时,还要注重分清C++与VB及C#之间的细微差别。还等什么呢,赶紧动手啊!

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有