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

创建基本的 Windows 服务(C#)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (224投票s)

2006年6月5日

CPOL

4分钟阅读

viewsIcon

1918908

downloadIcon

29155

一个简单易用的 C# 2.0 Windows 服务模板。

引言

在 Google 上搜索“Services”和“C#”,你会发现很多关于 Web 服务的 ASP.NET 参考。对于想要了解 Windows 服务的新手来说,这没什么用!我也有过同样的问题,所以我决定用老方法自己来做。本文无意与其他文章争论。它只是一个基本的 Windows 服务骨架,仅此而已。

我知道 Visual Studio 2003 中有一个 Windows 服务模板,但我还记得微软在这方面做得相当混乱。现在我使用的是 Visual C# 2005 Express Edition,其中没有 Windows 服务模板。所以,我从那里开始,创建了一个适用于 .NET 2.0 的模板。

流程

这是一个关于如何用 C# 构建 Windows 服务的小例子。

首先,打开你选择的 IDE(我使用的是免费的 Visual C# 2005 Express Edition)并创建一个空项目。你可以在 VC# 2005 EE 中通过点击菜单:[文件] -> [新建项目],选择“空项目”,然后随意命名(我的是 WindowsService,很原创!)。点击确定创建项目。你的项目已创建,最初它只保存在一个临时位置,所以请前往 [文件] -> [全部保存],然后通过对话框确认。这将正式保存你的项目。

接下来,向你的项目添加 System.ServiceProcessSystem.Configuration.Install 引用。在解决方案资源管理器中,右键单击“引用”,然后选择“添加引用...”。在弹出的对话框中,确保选中了“.NET”选项卡,然后在列表中向下滚动找到“System.ServiceProcess”,选中它,然后点击确定。你会在引用下看到一个新的条目。这将允许你编写引用 System.ServiceProcess 类的代码。重复此过程以添加 System.Configuration.Install 引用。

现在,向项目中添加两个新的类文件,一个命名为:“WindowsService.cs”,另一个命名为:“WindowsServiceInstaller.cs”。在解决方案资源管理器中,右键单击项目名称,然后转到:[添加] -> [类]。将类命名为“WindowsService.cs”,然后按确定。对“WindowsServiceInstaller.cs”重复此过程。现在你有了两个全新的、包含基本内容的文件。

在“WindowsService.cs”类中,复制以下内容

using System;
using System.Diagnostics;
using System.ServiceProcess;

namespace WindowsService
{
    class WindowsService : ServiceBase
    {
        /// <summary>
        /// Public Constructor for WindowsService.
        /// - Put all of your Initialization code here.
        /// </summary>
        public WindowsService()
        {
            this.ServiceName = "My Windows Service";
            this.EventLog.Log = "Application";
            
            // These Flags set whether or not to handle that specific
            //  type of event. Set to true if you need it, false otherwise.
            this.CanHandlePowerEvent = true;
            this.CanHandleSessionChangeEvent = true;
            this.CanPauseAndContinue = true;
            this.CanShutdown = true;
            this.CanStop = true;
        }

        /// <summary>
        /// The Main Thread: This is where your Service is Run.
        /// </summary>
        static void Main()
        {
            ServiceBase.Run(new WindowsService());
        }

        /// <summary>
        /// Dispose of objects that need it here.
        /// </summary>
        /// <param name="disposing">Whether
        ///    or not disposing is going on.</param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        /// <summary>
        /// OnStart(): Put startup code here
        ///  - Start threads, get inital data, etc.
        /// </summary>
        /// <param name="args"></param>
        protected override void OnStart(string[] args)
        {
            base.OnStart(args);
        }

        /// <summary>
        /// OnStop(): Put your stop code here
        /// - Stop threads, set final data, etc.
        /// </summary>
        protected override void OnStop()
        {
            base.OnStop();
        }

        /// <summary>
        /// OnPause: Put your pause code here
        /// - Pause working threads, etc.
        /// </summary>
        protected override void OnPause()
        {
            base.OnPause();
        }

        /// <summary>
        /// OnContinue(): Put your continue code here
        /// - Un-pause working threads, etc.
        /// </summary>
        protected override void OnContinue()
        {
            base.OnContinue();
        }

        /// <summary>
        /// OnShutdown(): Called when the System is shutting down
        /// - Put code here when you need special handling
        ///   of code that deals with a system shutdown, such
        ///   as saving special data before shutdown.
        /// </summary>
        protected override void OnShutdown()
        {
            base.OnShutdown();
        }

        /// <summary>
        /// OnCustomCommand(): If you need to send a command to your
        ///   service without the need for Remoting or Sockets, use
        ///   this method to do custom methods.
        /// </summary>
        /// <param name="command">Arbitrary Integer between 128 & 256</param>
        protected override void OnCustomCommand(int command)
        {
            //  A custom command can be sent to a service by using this method:
            //#  int command = 128; //Some Arbitrary number between 128 & 256
            //#  ServiceController sc = new ServiceController("NameOfService");
            //#  sc.ExecuteCommand(command);

            base.OnCustomCommand(command);
        }

        /// <summary>
        /// OnPowerEvent(): Useful for detecting power status changes,
        ///   such as going into Suspend mode or Low Battery for laptops.
        /// </summary>
        /// <param name="powerStatus">The Power Broadcast Status
        /// (BatteryLow, Suspend, etc.)</param>
        protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
        {
            return base.OnPowerEvent(powerStatus);
        }

        /// <summary>
        /// OnSessionChange(): To handle a change event
        ///   from a Terminal Server session.
        ///   Useful if you need to determine
        ///   when a user logs in remotely or logs off,
        ///   or when someone logs into the console.
        /// </summary>
        /// <param name="changeDescription">The Session Change
        /// Event that occured.</param>
        protected override void OnSessionChange(
                  SessionChangeDescription changeDescription)
        {
            base.OnSessionChange(changeDescription);
        }
    }
}

在“WindowsServiceInstaller.cs”类中,复制以下内容

using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace WindowsService
{
    [RunInstaller(true)]
    public class WindowsServiceInstaller : Installer
    {
        /// <summary>
        /// Public Constructor for WindowsServiceInstaller.
        /// - Put all of your Initialization code here.
        /// </summary>
        public WindowsServiceInstaller()
        {
            ServiceProcessInstaller serviceProcessInstaller = 
                               new ServiceProcessInstaller();
            ServiceInstaller serviceInstaller = new ServiceInstaller();

            //# Service Account Information
            serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
            serviceProcessInstaller.Username = null;
            serviceProcessInstaller.Password = null;

            //# Service Information
            serviceInstaller.DisplayName = "My New C# Windows Service";
            serviceInstaller.StartType = ServiceStartMode.Automatic;

            //# This must be identical to the WindowsService.ServiceBase name
            //# set in the constructor of WindowsService.cs
            serviceInstaller.ServiceName = "My Windows Service";

            this.Installers.Add(serviceProcessInstaller);
            this.Installers.Add(serviceInstaller);
        }
    }
}

代码中的 ServiceAccount 部分对于你的代码运行的安全上下文很重要。对于 User 以外的所有枚举,将 UsernamePassword 属性设置为 null

你可以将 Account 属性设置为以下值(大部分来自微软的帮助文件)

  • LocalSystem 枚举定义了一个特权很高的账户,但大多数服务不需要如此高的权限级别。
  • LocalService 枚举为安全上下文提供了较低的权限级别。
  • NetworkService 枚举提供了广泛的本地权限,并向远程服务器提供计算机的凭据。
  • User 枚举允许使用用户名和密码组合来设置安全上下文的权限级别为指定用户的权限。

好了,这并不难!现在,在 WindowsService 类中,根据你创建此服务的目的,你可能需要包含一个后台工作线程来处理工作。我只是简单地保留了所有内容,只修改了 OnCustomCommand 方法,并且效果不错。如果你希望处理持续或定时处理,最好添加至少一个工作线程。

本文就此打住,关于设置 Windows 服务的内容就到这里:其余的基本上取决于程序员了。现在,简单/困难的部分:安装。

安装

要安装服务,你可以创建一个安装程序来封装可执行文件以便部署,我发现这在测试应用程序时既耗时又不高效。或者,你可以通过 .NET Framework 自带的 InstallUtil.exe 过程来安装服务。

我创建了一个简单的批处理文件 install.bat 来简化服务的安装(进而简化测试)。下面显示了该批处理脚本;卸载同样简单,只需创建一个另一个批处理文件 uninstall.bat,其中包含完全相同的脚本,只需将 /i(表示安装)替换为 /u(表示卸载)。

@ECHO OFF

REM The following directory is for .NET 2.0
set DOTNETFX2=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
set PATH=%PATH%;%DOTNETFX2%

echo Installing WindowsService...
echo ---------------------------------------------------
InstallUtil /i WindowsService.exe
echo ---------------------------------------------------
echo Done.

关注点

我注意到,其他关于创建服务的示例(如果我能找到的话)更多地关注于 Windows 服务本身的创建和结构之外的一切。由于我主要是为工作项目做的,我必须自己学习所有这些,这本身就很有价值,但我可以想象那些需要这种代码的人,并且没有时间去研究它。希望这对那些只需要基本知识并想在此基础上继续的人有所帮助。

© . All rights reserved.