系列文章目录链接:
- windows 服务程序和桌面程序集成(一)概念介绍
- windows 服务程序和桌面程序集成(二)服务程序
- windows 服务程序和桌面程序集成(三)UDP监控工具
- windows 服务程序和桌面程序集成(四)桌面程序
- windows 服务程序和桌面程序集成(五)集成为一个EXE
- windows 服务程序和桌面程序集成(六)集成安装、启动、卸载功能
- windows 服务程序和桌面程序集成(七)效果演示及源程序下载
在我的 windows 服务程序和桌面程序集成(一)博文中介绍了Windows 中服务程序和桌面程序的基本概念、两种程序的特点。
备注:
Windows服务程序和桌面程序是两种不同的应用程序类型,它们在运行方式、交互方式和执行环境等方面存在差异。
运行方式:
桌面程序通常是由用户手动启动的,用户可以通过图形用户界面(GUI)与其交互。而Windows服务程序通常在Windows操作系统启动时就开始运行,并在后台持续运行,通常不需要用户手动启动,也没有GUI。
交互方式:
桌面程序通常通过GUI与用户进行交互,例如显示窗口、菜单、按钮、文本框等,用户可以使用鼠标、键盘等输入设备与其交互。而Windows服务程序通常不需要用户交互,它们在后台运行,执行特定的任务或提供服务,例如运行网络服务器、备份数据等。
执行环境:
桌面程序通常在用户交互的环境中运行,例如Windows桌面、移动设备等。而Windows服务程序通常在Windows操作系统的服务控制器中运行,它们以系统级别运行,具有更高的权限和安全性,可以在系统启动时启动,一直运行到系统关闭。
总的来说,Windows服务程序和桌面程序的区别在于它们的运行方式、交互方式和执行环境等方面。Windows服务程序是一种后台程序,通常在Windows操作系统启动时就开始运行,并在后台持续运行,提供特定的服务或执行特定的任务。而桌面程序则是用户手动启动的程序,通常在GUI中显示窗口、菜单、按钮等,用户可以使用鼠标、键盘等输入设备与其交互。
本文详细介绍用Delphi 开发环境创建Windows服务程序和桌面程序于一体的双模EXE。也就是说一个EXE文件,既可以表现出Windows服务程序的属性,又可以表现出桌面程序的属性,就是所谓的双模程序。
以下Delphi环境为:Delphi 11.3。如果版本不一样,也基本上是相同的,可能部分界面或者代码稍有不同而已。
一、Delphi环境下Windows服务程序创建、安装、运行步骤
这一节介绍单独的windows服务程序的创建,安装、运行,后面会介绍双模程序的创建:
1. 创建一个 Windows 服务程序:WindowsService_Demo.exe
打开Delphi IDE,选择 File -> New -> Other
然后选择 Delphi -> Windows 窗口中的 Windos Service 选项,然后 OK 即可!
生成好文件后,保存unit1.pas文件为:uWindows_Service.pas,保存工程文件为:WindowsService_Demo.dproj
此时可以编译生成为EXE文件,但是这是一个空Service,没有任何功能,就相当于你创建了一个VCL桌面程序,只有一个空白的Form,没有什么实际功能。注意uWindows_Service.pas就相当于VCL桌面程序的主Form。去到源程序目录看,它也包含一个对应的dfm文件 uWindows_Service.dfm。
对于Windows服务程序,在Delphi 的 IDE中是不能运行的,也不能进行调试运行,按下运行键或者调试键是没有反应的。只能通过工程文件右键菜单中的 Build 命令来生成EXE文件。
至此我们已经成功生成了一个Windows服务程序!
重点:
- 所有的windows服务管理接口已经被TService类实现了。在uWindows_Service.pas单元中,我们通过TService1继承了TService,它通过事件和操作系统进行交互。
- 通常我们需要一个线程来独立的做我们的实际工作,TService.OnExecute事件不需要做实际的工作。例如我们创建一个uWorkerThread.pas后台线程单元来完成我们的实际工作。
- 后台线程(uWorkerThread.pas)随着服务的启动(OnStart事件)而启动,随着服务的停止(OnStop事件)而停止。OnExecute事件等待和处理 ServiceController命令(也就是操作系统的命令)而无需处理我们的实际工作。
一般情况下,我们的OnExecute事件处理代码如下(并没有处理我们的实际任务):
procedure TService1.ServiceExecute(Sender: TService);
begin
while not Terminated do
begin
ServiceThread.ProcessRequests(false);
TThread.Sleep(1000);
end;
end;
2. 给 Windows 服务程序:WindowsService_Demo.exe 增加实际工作线程
给Windows服务程序增加一个新单元,名称:uWorkerThread.pas,放在当前工程文件目录的上一级目录中的子目录Public目录下(为什么放在这里?请思考...)。
工作线程单元uWorkerThread.pas实现的目的是创建一个线程,在线程中每秒通过UDP发送一条消息。由于Windows服务程序是没有界面的,所以通过UDP通信发送一条消息到外边,这样外边的UDP监控程序就可以收到Windows服务程序发送的UDP消息,每秒钟一条消息!
uWorkerThread.pas单元代码:
unit uWorkerThread;
interface
uses
System.Classes,
IdUDPClient,
IdGlobal,
System.SysUtils;
//Winapi.Windows;
type
//实际工作线程类
TWorkThread = Class(TThread)
private
FPaused : Boolean; //
protected
constructor Create;
procedure Execute; override;
public
procedure Pause;
procedure Continue;
End;
//服务执行的函数,UDP发送消息函数
procedure Send_UDP_Info(str : string);
var
//工作线程变量
WorkThread : TWorkThread;
implementation
procedure Send_UDP_Info(str : string);
var
UDPClient: TIdUDPClient;
B : TBytes;
begin
UDPClient := TIdUDPClient.Create(nil);
try
UDPClient.BroadcastEnabled := True;
B := TEncoding.UTF8.GetBytes(str);
//只给本机发送,这个地方只需要给本机发送广播消息即可 2023-03-04
UDPClient.Broadcast(TidBytes(B),8192,'127.0.0.1'); //端口号
//广播到任何地方
//UDPClient.Broadcast(TidBytes(B),G_UDPPort); //端口号
finally
UDPClient.Free;
end;
end;
{ TWorkThread }
procedure TWorkThread.Continue;
begin
FPaused := False;
Send_UDP_Info('服务继续工作....');
end;
constructor TWorkThread.Create;
begin
FPaused := False;
end;
procedure TWorkThread.Execute;
var
S : string;
begin
inherited;
while not Terminated do
begin
if not FPaused then
begin
S := FormatDateTime('YYYY-MM-DD hh:mm:ss',Now);
Send_UDP_Info(S);
end;
TThread.Sleep(1000);
end;
Send_UDP_Info('********** 服务终止工作 **********');
end;
procedure TWorkThread.Pause;
begin
FPaused := True;
Send_UDP_Info('服务暂停工作!!!');
end;
end.
3. 给 Windows 服务程序:WindowsService_Demo.exe 修改属性,增加事件代码
- 修改Windows服务程序的DisplayName属性: AAA,便于在系统服务列表程序中查看。
- 增加Windows服务程序在系统服务中显示的属性:在ServiceAfterInstall事件中增加如下代码:
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('SYSTEM\CurrentControlSet\Services\' + Name,False {如果没有就不创建了}) then
begin
Reg.WriteString('Description','WindowsService Demo(by SensorWU)') ;
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
这样在服务程序显示列表中会显示出我们的服务程序的属性信息。
- 增加Windows服务程序的其他事件代码:
OnStart代码:
procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
//创建工作线程
WorkThread := TWorkThread.Create;
WorkThread.FreeOnTerminate := True; //完成后直接释放
end;
OnPause代码:
procedure TService1.ServicePause(Sender: TService; var Paused: Boolean);
begin
WorkThread.Pause;
Paused := True;
end;
OnContinue代码:
procedure TService1.ServiceContinue(Sender: TService; var Continued: Boolean);
begin
WorkThread.Continue;
Continued := True;
end;
至此,Windows服务程序(独立程序)已经完全写好,通过工程文件右键的Build菜单就可以编译出来服务程序的EXE文件了。其功能是服务程序启动后每隔一秒钟发送一个UDP消息出来,下一节我们需要做一个UDP服务来监测这个消息,这样就能够清晰的看到Windows服务程序的运行,暂停和继续了!
4. Windows服务程序的安装、启动、暂停、继续、卸载
通过上面的步骤,我们已经编译出了Windows服务程序WindowsService_Demo.exe,下面我们来演示Windows服务程序的安装、启动、暂停、继续、卸载。
为了方便(主要是Windows系统不允许网络磁盘上的程序作为Windows服务程序),我们将编译好的WindowsService_Demo.exe拷贝到 C:\Temp目录中。
安装:
通过管理员权限运行CMD,然后进入到C:\Temp目录中
C:\Temp\WindowsService_Demo.exe /install
安装完成后,在系统的服务列表中就可以看到:
启动、暂停、继续:
服务启动后就可以出现:停止、暂停、重启动;
服务暂停后就可以出现:停止、恢复、重启动
在服务中可以分别实现 停止、暂停、恢复、重启动。
卸载:
卸载成功后,在系统的服务列表中将看不到了。
下一篇:windows 服务程序和桌面程序集成(三)UDP监控工具