分步创建C#服务课程II






4.85/5 (28投票s)
2003 年 4 月 10 日
3分钟阅读

199129

1661
一个多篇文章的贡献,详细描述了如何逐步创建您自己的服务,并集成了对安装程序和自定义事件日志的支持。 在本课程中,我们将添加多个子服务,并更新安装程序以安装这些服务。
引言
最近,我试图使用 .NET 框架编写一个 C# 服务,遇到了许多文档中未涵盖的问题,或者即使涵盖了,解决方案也很难推断出来。 因此,我决定编写一系列文章,逐步详细描述我是如何解决遇到的问题的。
这是一篇多篇文章的贡献,其分解如下
- 创建项目、初始服务和安装
- 向应用程序添加其他服务
- 添加对自定义事件日志记录的支持
使用代码
从我们在上一课中开发的代码开始,我们将创建一些负责 Spades 游戏服务器不同方面的子服务。 在本课程中,我们将创建登录服务、大厅服务和游戏服务。 所有这些服务都将派生自我们在上一课中开发的 SpadesChildServiceBase
类。 我们还需要在 Application
对象中创建这些类的实例,并创建不同的 ServiceInstaller
类来安装 3 种不同的子服务。
所以事不宜迟,让我们开始创建我们的第一个子进程。 所有子进程的结构都相似,因此我不会列出每个子进程的源代码,但基本区别只是类名。 因此,让我们继续创建一个新类,并将其命名为 SpadesLoginService
并使其从 SpadesChildServiceBase
派生。 完成后,您的代码应如下所示。
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
namespace CodeBlooded.Spades.Services
{
public class SpadesLoginService : SpadesChildServiceBase
{
public SpadesLoginService()
{
this.ServiceName = "SpadesLoginSvc";
}
/// <SUMMARY>
/// Set things in motion so your service can do its work.
/// </SUMMARY>
protected override void OnStart(string[] args)
{
base.OnStart( args );
}
/// <SUMMARY>
/// Stop this service.
/// </SUMMARY>
protected override void OnStop()
{
base.OnStop();
}
/// <SUMMARY>
///
/// </SUMMARY>
/// <RETURNS></RETURNS>
protected override int Execute() {
// for right now we'll just log a message in the
// Application message log to let us know that
// our service is working
System.Diagnostics.EventLog.WriteEntry(ServiceName,
ServiceName + "::Execute()");
return 0;
}
}
}
由于我们希望子服务在管理员服务尚未启动时启动它,因此我们需要将此代码添加到 SpadesChildServiceBase
类中。 我们还需要向 SpadesAdminService
类添加一些关闭代码,以便在停止管理员服务时关闭所有子服务。
打开 *SpadesChildServiceBase.cs* 文件,并在 OnStart
方法中,我们将使用 ServiceController
来访问特定计算机上的特定服务,并通过 Start
或 Stop
方法控制该服务。 只需将以下代码添加到 SpadesChildServiceBase
类的 OnStart
方法中。
protected override void OnStart(string[] args)
{
// gain access to the SpadesAdminSvc on the local computer.
ServiceController controlAdmin =
new ServiceController("SpadesAdminSvc", ".");
if( controlAdmin.Status !=
System.ServiceProcess.ServiceControllerStatus.Running &&
controlAdmin.Status != ServiceControllerStatus.StartPending ) {
// start the admin service
controlAdmin.Start();
}
base.OnStart( args );
}
我们需要在 Application
类的 Main
方法中创建 SpadesLoginService
类的实例。 代码如下所示
servicesToRun = new ServiceBase[]{ new SpadesAdminService(),
new SpadesLoginService() };
此时我们剩下要做的一切是,在我们的 SpadesInstaller
类中创建另一个 ServiceInstaller
类。 该代码如下所示,与我们在上一篇文章中编写的代码类似。 子服务中唯一不同的是,我们配置安装程序以将 ServiceDependOn
键添加到注册表。 由于此键是注册表中的 MULTI_SZ
类型,因此它可以保存多个字符串,因此我们在 .NET 中的方法是创建一个字符串数组并将不同的字符串添加到该数组。 由于我们实际上只有一个子服务依赖的服务,因此我们只需要 SpadesAdminSvc
这一个条目。
public SpadesInstaller()
{
// ...
ServiceInstaller serviceLogin = new ServiceInstaller();
serviceLogin.StartType = ServiceStartMode.Manual;
serviceLogin.ServiceName = "SpadesLoginSvc";
serviceLogin.DisplayName = "Spades Login Service";
serviceLogin.ServicesDependedOn
= new string[] { "SpadesAdminSvc" };
// ...
Installers.Add( serviceLogin );
}
继续创建另外 2 个类,一个是 SpadesGameService
,另一个是 SpadesLobbyService
,它们都派生自 SpadesChildServiceBase
。 确保更改构造函数中的 ServiceName
属性。 在我们的例子中,它们应该是 SpadesGameSvc
和 SpadesLobbySvc
。
您还需要在 Application::Main
方法中创建每个实例,并将它们添加到 servicesToRun
变量中。
您还需要在 SpadesInstaller
类中创建两个 ServiceInstaller
类实例,就像我们上面所做的那样。 完成后,Main
方法和 SpadesInstaller
文件应如下所示
static void Main(string[] args)
{
// we'll go ahead and create an array so that
// we can add the different services that
// we'll create over time.
ServiceBase[] servicesToRun;
// to create a new instance of a new service,
// just add it to the list of services
// specified in the ServiceBase array constructor.
servicesToRun = new ServiceBase[]{
new SpadesAdminService(),
new SpadesLoginService(),
new SpadesGameService(),
new SpadesLobbyService() };
// now run all the service that we have created.
// This doesn't actually cause the services
// to run but it registers the services with the
// Service Control Manager so that it can
// when you start the service the SCM will
// call the OnStart method of the service.
ServiceBase.Run( servicesToRun );
}
以及 SpadesInstaller
类。
public SpadesInstaller()
{
ServiceProcessInstaller process =
new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
ServiceInstaller serviceAdmin = new ServiceInstaller();
serviceAdmin.StartType = ServiceStartMode.Manual;
serviceAdmin.ServiceName = "SpadesAdminSvc";
serviceAdmin.DisplayName = "Spades Administration Service";
ServiceInstaller serviceLogin = new ServiceInstaller();
serviceLogin.StartType = ServiceStartMode.Manual;
serviceLogin.ServiceName = "SpadesLoginSvc";
serviceLogin.DisplayName = "Spades Login Service";
serviceLogin.ServicesDependedOn =
new string[] { "SpadesAdminSvc" };
ServiceInstaller serviceGame = new ServiceInstaller();
serviceGame.StartType = ServiceStartMode.Manual;
serviceGame.ServiceName = "SpadesGameSvc";
serviceGame.DisplayName = "Spades Game Service";
serviceGame.ServicesDependedOn =
new string[] { "SpadesAdminSvc" };
ServiceInstaller serviceLobby = new ServiceInstaller();
serviceLobby.StartType = ServiceStartMode.Manual;
serviceLobby.ServiceName = "SpadesLobbySvc";
serviceLobby.DisplayName = "Spades Lobby Service";
serviceLobby.ServicesDependedOn =
new string[] { "SpadesAdminSvc" };
// Microsoft didn't add the ability to add a
// description for the services we are going to install
// To work around this we'll have to add the
// information directly to the registry but I'll leave
// this exercise for later.
// now just add the installers that we created to
// our parents container, the documentation
// states that there is not any order that you need
// to worry about here but I'll still
// go ahead and add them in the order that makes sense.
Installers.Add( process );
Installers.Add( serviceAdmin );
Installers.Add( serviceLogin );
Installers.Add( serviceGame );
Installers.Add( serviceLobby );
}
在我的下一篇文章中,我们将添加对拥有我们自己的自定义事件日志的支持,以便我们可以将我们的消息记录到其中,但要利用它,我们还需要确保它已安装。 因此,我们将必须为它创建一个安装程序。 我在做这件事时遇到了问题,并将详细介绍我是如何解决这个问题的。
历史
- 1.0 - 2003 年 4 月 10 日
- 第一个发行版本。