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

C# 泛型动态 Windows 服务使用 .NET 反射

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.05/5 (9投票s)

2008年1月22日

CPOL

3分钟阅读

viewsIcon

82940

downloadIcon

722

一种使用 .NET 反射创建可配置和动态的 Windows 服务应用程序的技术。

引言

本文展示了一种设计 Windows 服务应用程序的技术,其目标如下:

  1. 可以像“控制台”应用程序一样进行调试,而无需调用“安装程序”。
  2. 主要的“Windows 服务”代码应该是可重用的,并且可以快速应用于任何新的“Windows 服务”应用程序开发。
  3. 分离“服务器逻辑”并允许在需要时进行动态切换。
  4. 安装期间“Windows 服务”的详细信息可以通过 XML 文件进行配置。
  5. 服务器逻辑的加载可以通过 XML 文件进行配置。

我在此处提供的示例代码是基本框架,已使用 Visual Studio 2008 和 .NET 3.5 Framework 进行了测试。 但这个概念也应该适用于其他版本。

背景

如果您从未创建过 Windows 服务应用程序,则 MSDN 库站点上有一篇“演练”文章。 显然,The Code Project 上也有许多有用的文章。

Using the Code

通过使用所提供的技术,创建 Windows 服务应用程序只需要您使用 Visual Studio 创建一个新的“控制台”项目即可。

示例 Visual Studio 解决方案包含 4 个项目。 它们是

  1. GDWS.Common - 这是 Windows 服务的通用可重用代码所在的位置。
  2. GDWS.ExampleService - 这是一个示例“服务器”逻辑,旨在构建为 .NET 程序集。
  3. GDWS.ExampleServiceProgram - 这是一个示例“控制台”应用程序,它实际调用了服务器逻辑。
  4. SetupExampleService - 这是一个典型的“安装和部署”项目,用于演示此技术也可以与标准安装机制无缝协作。

简而言之,Main() 程序非常简单,如下所示

namespace GDWS.ExampleServiceProgram
{
  class Program
  {
    static void Main(string[] args)

    {
      ServiceMainProgram.ServiceMain(args);
    }
  }

  [RunInstaller(true)]
  public class ExampleServiceInstaller : CustomServiceInstaller
  {
  }
}

类 - CustomServiceInstaller

此类主要读取 ServiceInstall.xml 文件,以便告诉 Windows Installer “服务名称”、“服务显示名称”和“服务描述”。

类 - ServiceMainProgram

为了动态加载“服务器逻辑”,使用 .NET 反射来加载封装服务器逻辑的 DLL。 这两个构造函数是 private,并且您可以使用两个 static 方法来调用该过程。

public class ServiceMainProgram
{
  ...
  ...
  public static void Service(string[] args, Type type)...

  public static void Service(string[] args)...
  ...
} 

采用两个参数的 static 方法要求您提交一个 Type,以便在内部创建对象期间,可以使用反射来找出是否存在两个必需的 static 方法 StartThreadProcStopThreadProc

仅采用一个参数的第二个 static 方法从 ServiceConfig.xml 文件中读取 Type 的名称,该文件还会告诉它在哪里可以找到服务器逻辑程序集 DLL。 找到 DLL 的路径后,将使用反射加载该程序集。

private Type LoadAssemby(string configFileName)
{
  ...
  ...
  Assembly asm = Assembly.LoadFile(Path.GetFullPath(assemblyFullPath));
  ...
  Type type = asm.GetType(typeName);
  ...
}

一旦找到并检查了 Type,以下代码就可以利用反射的方法

private void Run()
{
  if (debugMode) RunDebug();
  else
  {
    ServiceBase[] servicesToRun = new ServiceBase[]
        { new GenericService(threadProcType) };
    ServiceBase.Run(servicesToRun);
  }
}

private void RunDebug()
{
  ...
  ...
  MethodInfo mStart = threadProcType.GetMethod(START_THREAD_PROC);
  // assuming static method, hence no need to pass any instantiated object
  mStart.Invoke(null, null);
  bool stop = false;
  while (!stop)
  {
    ...
    if (k.KeyChar == 'q' || k.KeyChar == 'Q')
    {
      ...
      MethodInfo mStop = threadProcType.GetMethod(STOP_THREAD_PROC);
      // assuming static method, hence no need to pass any instantiated object
      mStop.Invoke(null, null);
      stop = true;
    }
    ...
  }
}

调试

出于调试目的,定义一个命令行参数 debug,或者在命令提示符下,只需键入 yourservice.exe debug 即可将程序调用到调试模式。

安装为适当的 Windows 服务

您可以使用捆绑的 Setup 项目安装示例服务应用程序。

要快速测试应用程序作为 Windows 服务,您可以使用 installutil.exe 并通过调用 installutil.exe /u 来卸载它。

ServiceConfig.xml

<?xml version="1.0" encoding="utf-8" ?>
<ConfigService>
<ServiceName value="ExampleService"/>
<ServiceDisplayName value="An Example Service"/>
<ServiceDescription value="An example service
    that demonstrates a generic and dynamic technique."/>
</ConfigService>

ServiceInstall.xml

<?xml version="1.0" encoding="utf-8" ?>
<InstallService>
<ServiceAssemblyFullPath value=
    "..\..\..\GDWS.ExampleService\bin\Debug\GDWS.ExampleService.dll"/>
<TypeName value="GDWS.ExampleService.ThreadProcExample"/>
</InstallService>

请注意,尽管 ServiceAssemblyFullPath 被指定为相对路径,但如果代码在第一个实例中找不到它,它将尝试在本地找到它。

历史

  • 2008-01-22: 文章创建
© . All rights reserved.