Delphi 在Windows 11中开发Windows服务:最佳实践和工具

当你开发Windows软件时,你会遇到某些种类的应用程序需要24小时运行,或者,事实上,在计算机运行时持续不断地运行。通常情况下,这些计算机是网络服务器或台式机上的监控应用程序。在这些情况下,你可能会考虑创建一个控制台应用程序,它要么有最小的交互量,要么完全无声,但有比这更好的解决方案。

简单的控制台或最小化的普通GUI应用程序可能面临Windows会话终止、重启和用户权限等问题。解决这个问题的方法是开发Windows服务。在本教程中,我们将深入研究使用Delphi构建Windows服务。在这之后,你可以把它作为你的Windows服务的模板。

目录

为什么我需要创建一个Windows服务?

开发Windows服务的先决条件是什么?

Windows服务应用程序是如何工作的?

你什么时候需要编写Windows服务?

如何在Delphi中创建一个Windows服务项目?

如何用Delphi实现一个Windows服务应用?

我们如何实现Windows服务应用功能?

使用Windows服务管理器测试服务

调试服务应用程序


为什么我需要创建一个Windows服务?

创建长期运行的服务有许多原因,如::

  • 处理CPU密集型数据。
  • 在后台排队的工作项目。
  • 在时间表上执行基于时间的操作
  • 对 "助手 "或实用程序功能进行不引人注目的操作

后台服务处理通常不涉及用户界面(UI),但可以围绕它们建立UI

开发Windows服务的先决条件是什么?

在开始潜心研究服务应用开发之前,我建议你看看最新版本的Delphi IDE。有了大量的增强功能和新特性,开发过程会更加顺利。此外,你可以免费使用Delphi社区版,熟悉Delphi编程语言和它的语法。

Windows服务应用程序是如何工作的?

服务应用程序从客户端应用程序接收请求,处理这些请求,并将信息返回给客户端应用程序。一个Web、FTP或电子邮件服务器就是一个服务应用程序的例子。

一个Windows服务应用程序是一个可以运行而不需要用户登录的Windows应用程序。Windows服务应用程序很少与桌面互动。在本教程中,我们将用Delphi创建一个Windows服务应用程序。

当一个服务程序运行时,它可以被设置为使用一套默认的用户权限,既可以作为一个虚拟的 "服务用户",也可以作为一个实际的具有访问系统权限的普通用户。重要的是,这些用户权限会影响到你的服务应用程序有权访问的文件夹,以及任何 "映射 "的网络文件夹--如果有人创建了一个映射,比如一个指向网络文件夹的 "Z: "驱动器很可能无法使用。在可能的情况下,你应该总是使用完整的UNC路径名称--或者使用Windows系统调用或Delphi的运行时TPath类型函数来获得特殊文件夹的正确位置,如%APPDATA%文件夹的位置和 "我的文档 "类型的虚拟路径。

Windows服务应用程序可以提供很多功能。当你启动服务实用工具时,请看列表。这些服务可以成为你使用的主要GUI应用程序的核心。

你什么时候需要编写Windows服务?

前段时间,我需要一个系统监控工具来监控我们文件服务器上的可用磁盘空间。我写了一个工具,每分钟检查一次,然后把这些信息写到一个日志文件中。然而,它需要一个用户登录,而当用户退出时,我的应用程序就关闭了。解决办法是将该程序重新创建为一个Windows服务,然后在计算机开机时一直运行,即使用户没有登录。

虽然RAD Studio与Delphi或C++ Builder对典型的面向用户的交互式应用程序进行了优化,但它更有能力轻松创建服务应用程序。

如何在Delphi中创建一个Windows服务项目?

要在Delphi中创建一个新的项目,你需要在你的计算机上安装Delphi开发环境。一旦你安装并运行了Delphi,你就可以开始创建一个新的项目。

要在RAD Studio中用Delphi创建一个新的Windows服务项目,采取以下步骤:

  1. 点击文件菜单,选择新建->其他。这将弹出 "新建项目 "对话框。
  2. 在对话框的左侧,选择Delphi,然后从Windows类别中选择Windows服务项目类型。
  3. 点击 "确定 "来创建新项目。

 请注意,对于那些使用C++ Builder的人来说,这个过程是类似的。

一旦新项目被创建,你将需要设置项目属性和选项。要做到这一点,在 "项目管理器 "窗口中右击项目名称,选择 "选项"。这将弹出 "项目选项 "对话框。在这个对话框中,你可以为你的项目设置各种选项,如目标平台、输出目录和编译器选项。在继续开发你的服务之前,请确保根据你的项目要求设置这些选项。

如何用Delphi实现一个Windows服务应用?

在Delphi中创建一个新的Windows服务项目并设置项目属性和选项后,下一步是实现服务本身。这涉及到向主要的服务单元添加代码,该单元通常被默认为 "Unit1.pas"。

要开始,在 "项目管理器 "窗口中双击 "Unit1.pas "文件,在代码编辑器中打开它。这将显示该单元的代码文件,它包含了Delphi服务应用程序的骨架代码。

要在单元中添加自己的代码,你需要通过处理各种服务事件来定义服务的行为。这些事件包括启动、停止和暂停事件,它们分别在服务启动、停止或暂停时被触发。

为了处理这些事件,你可以使用Delphi内置的服务组件,"TService "类。这个组件提供了各种方法和属性,你可以用它们来控制服务的行为,比如 "开始 "和 "停止 "方法。

例如,你可以使用 "OnStart "事件处理程序来定义服务启动时应该执行的代码。要做到这一点,你可以在 "Unit1.pas "文件中添加以下代码:

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  // code you want to execute when the service starts
  // such as making a log entry
end;

除了启动和停止事件外,你可能还需要处理暂停事件,它是在服务暂停时触发的。要做到这一点,你可以使用 "OnPause "事件处理程序,它类似于 "OnStart "和 "OnStop "事件处理程序。

例如,你可以在 "Unit1.pas "文件中添加以下代码来处理暂停事件:

procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
  // Add your code here for things to happen when the user
  // chooses "pause" from the service menu or uses the service control
  // commands to pause your service.
  // This is NOT the same as the service being stopped!
end;

在每一个事件处理程序中,你可以添加你自己的代码来定义当各自事件被触发时服务的行为。例如,你可以使用 "TService "组件的 "Start "和 "Stop "方法来启动或停止一个定时器或一个线程,或者你可以使用 "Pause "方法来暂停一个任务的执行。

一旦你将代码添加到主服务单元并处理了服务的事件,你就可以使用Delphi调试器和Windows服务管理器继续调试和测试服务。

我们如何实现Windows服务应用功能?

在单元的Uses子句中添加Vcl.SvcMgr单元。

通过修改TForm类的声明,将主窗体的祖先改为TService,如下

type
  TService1 = class(TService)

这里有完整的源代码:

unit ServiceUnit;

interface

uses
  Winapi.Windows
, Winapi.Messages
, System.SysUtils
, System.Classes
, Vcl.Graphics
, Vcl.Controls
, Vcl.SvcMgr
, Vcl.Dialogs
, BackgroundThreadUnit
, System.Win.Registry;

type
  TService1 = class(TService)
    procedure ServiceExecute(Sender: TService);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServicePause(Sender: TService; var Paused: Boolean);
    procedure ServiceContinue(Sender: TService; var Continued: Boolean);
    procedure ServiceAfterInstall(Sender: TService);
  private
    FBackgroundThread: TBackgroundThread;
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

{$R *.dfm}

var
  MyService: TService1;

implementation

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  MyService.Controller(CtrlCode);
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  while not Terminated do
  begin
    ServiceThread.ProcessRequests(false);
    TThread.Sleep(1000);
  end;
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ServiceContinue(Sender: TService;
  var Continued: Boolean);
begin
  FBackgroundThread.Continue;
  Continued := True;
end;

procedure TService1.ServiceAfterInstall(Sender: TService);
var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create(KEY_READ or KEY_WRITE);
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKey('SYSTEMCurrentControlSetServices' + name, false) then
    begin
      Reg.WriteString('Description', 'Blogs.Embarcadero.com');
      Reg.CloseKey;
    end;
  finally
    Reg.Free;
  end;
end;

procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
  FBackgroundThread.Pause;
  Paused := True;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  FBackgroundThread.Terminate;
  FBackgroundThread.WaitFor;
  FreeAndNil(FBackgroundThread);
  Stopped := True;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  FBackgroundThread := TBackgroundThread.Create(True);
  FBackgroundThread.Start;
  Started := True;
end;

end.

这里的后台线程代码如下:

unit BackgroundThreadUnit;

interface

uses
  System.Classes;

type
  TBackgroundThread = class(TThread)
  private
    FPaused: Boolean;
    // FTerminated: Boolean;
    // FOnTerminate: TNotifyEvent;
  protected
    procedure Execute; override;
  public
    procedure Pause;
    procedure Continue;
    // procedure Terminate;
    // property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
  end;

implementation

uses
  System.SysUtils, System.IOUtils;

procedure TBackgroundThread.Continue;
begin
  FPaused := False;
end;

  // process something here
procedure TBackgroundThread.Execute;
var
  LogFile: TextFile;
begin
  try
    FPaused := False;
    AssignFile(LogFile, 'C:TempLogs.log');
    Rewrite(LogFile);

    while not Terminated do
    begin
      if not FPaused then
      begin
        WriteLn(LogFile, 'Logs From Background Thread: ' + DateTimeToStr(Now));
      end;
      TThread.Sleep(1000);
    end;
  finally
    CloseFile(LogFile);
  end;
end;

procedure TBackgroundThread.Pause;
begin
  FPaused := True;
end;

end.

现在你可以建立服务了。在项目窗口,你可以打开上下文菜单,像这样建立服务:

Delphi构建Windows服务

使用Windows服务管理器测试服务

你可以使用Windows服务管理器来测试服务。这个工具允许你启动、停止、暂停和恢复一个服务,并查看其状态和可能遇到的任何错误。

要安装该服务,你应该遵循以下步骤:

  • 进入文件夹,在终端打开(可执行文件应配置为以管理员权限运行)
  • 用"/install "命令输入服务名称

进入该文件夹并在终端打开

使用终端安装Windows服务

一旦服务被安装,你可以使用Windows服务管理器启动、停止、暂停或恢复该服务。要做到这一点,在Windows控制面板的管理工具文件夹中打开服务应用程序。在服务列表中找到该服务,右键单击它,并从上下文菜单中选择所需的操作。

在这里你可以启动、停止和暂停服务

几秒钟后,从服务管理器中停止该服务并检查日志文件。

附录:

调试服务应用程序

你可以在服务应用程序已经运行的情况下,通过附加到服务应用程序进程来调试服务应用程序(也就是说,先启动服务,然后附加到调试器上)。要附加到服务应用程序进程,选择 Run > Attach To Process,并在出现的对话框中选择服务应用程序。

在某些情况下,由于权限不足,这种方法可能会失败。如果发生这种情况,你可以使用服务控制管理器来使你的服务能够与调试器一起工作:

要进行调试:

  1. 首先在以下注册表位置创建一个名为图像文件执行选项的键: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion
  2. 创建一个与你的服务同名的子键(例如,MYSERV.EXE)。在这个子键上,添加一个REG_SZ类型的值,名为Debugger。使用bds.exe的完整路径作为字符串值。
  3. 在服务控制面板小程序中,选择你的服务,点击启动并勾选允许服务与桌面互动。

对于Windows NT:

在Windows NT系统上,你可以使用另一种方法来调试服务应用程序。然而,这种方法可能很棘手,因为它需要很短的时间间隔:

  1. 首先,在调试器中启动该应用程序。等待几秒钟,直到它完成加载。
  2. 从控制面板或命令行中快速启动服务:启动MyServ

你必须快速启动服务(在应用程序启动后15-30秒内),因为如果没有启动服务,应用程序将终止。

How to Delete a Windows Service?

Open PowerShell as an administrator and type this command sc delete ServiceName

How do I silently install a Windows service app?

You can avoid the message box appearing by adding “/silent” to the end of the command line like so: myserviceapp.exe /install /silent

Can 32bit services run on 64bit Windows?

Yes, a 32bit service can run normally on a 64bit version of Windows. Note, however, that some network system administrators may have a Windows group policy option set up which will prevent anything other than 64bit services from running. Most don’t do this, but it is possible.

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值