65.9K
CodeProject 正在变化。 阅读更多。
Home

分步创建C#服务课程II

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (28投票s)

2003 年 4 月 10 日

3分钟阅读

viewsIcon

199129

downloadIcon

1661

一个多篇文章的贡献,详细描述了如何逐步创建您自己的服务,并集成了对安装程序和自定义事件日志的支持。 在本课程中,我们将添加多个子服务,并更新安装程序以安装这些服务。

引言

最近,我试图使用 .NET 框架编写一个 C# 服务,遇到了许多文档中未涵盖的问题,或者即使涵盖了,解决方案也很难推断出来。 因此,我决定编写一系列文章,逐步详细描述我是如何解决遇到的问题的。

这是一篇多篇文章的贡献,其分解如下

  1. 创建项目、初始服务和安装
  2. 向应用程序添加其他服务
  3. 添加对自定义事件日志记录的支持

使用代码

从我们在上一课中开发的代码开始,我们将创建一些负责 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 来访问特定计算机上的特定服务,并通过 StartStop 方法控制该服务。 只需将以下代码添加到 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 属性。 在我们的例子中,它们应该是 SpadesGameSvcSpadesLobbySvc

您还需要在 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 日
    • 第一个发行版本。
© . All rights reserved.