Delphi 中同一服务的多个实例
Multiple instances of the same service in Delphi
我有一个在 delphi 中制作的旧 windows 服务,现在必须在同一台服务器上多次安装,我正在尝试更改代码以便能够更改服务名称,因为我正在安装该服务,但我无法使其正常工作。
我找到了一些关于它的信息 here and some here,在研究了 post 并进行了必要的修改之后,我能够安装两个名称不同的服务,但是这些服务没有启动。
我 post class 负责控制下面的服务(继承的 TService),我知道这是相当多的代码,但我真的很感激任何帮助。
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
tvdAvalancheDataCenterService.Controller(CtrlCode);
end;
function TtvdAvalancheDataCenterService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TtvdAvalancheDataCenterService.ServiceLoadInfo(Sender : TObject);
begin
Name := ParamStr(2);
DisplayName := ParamStr(3);
end;
procedure TtvdAvalancheDataCenterService.ServiceBeforeInstall(Sender: TService);
begin
ServiceLoadInfo(Self);
end;
procedure TtvdAvalancheDataCenterService.ServiceCreate(Sender: TObject);
begin
ServiceLoadInfo(Self);
end;
procedure TtvdAvalancheDataCenterService.ServiceStart(Sender: TService;
var Started: Boolean);
begin
FtvdTrayIcon := TtvdEnvoyTrayIcon.Create(Self);
SetServiceDescription;
FtvdDataCenter.tvdActive := true;
end;
procedure TtvdAvalancheDataCenterService.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
FreeAndNil(FtvdTrayIcon);
FtvdDataCenter.tvdActive := False;
end;
procedure TtvdAvalancheDataCenterService.ServiceAfterInstall(Sender: TService);
begin
SetServiceDescription;
end;
procedure TtvdAvalancheDataCenterService.SetServiceDescription;
var
aReg: TRegistry;
begin
if FDescriptionUpdated then
Exit;
aReg := TRegistry.Create(KEY_READ or KEY_WRITE);
try
aReg.RootKey := HKEY_LOCAL_MACHINE;
if aReg.OpenKey(cnRegKey+ Name, true) then
begin
aReg.WriteString('Description', cnServiceDescription);
aReg.CloseKey;
end;
FDescriptionUpdated:= True;
finally
aReg.Free;
end;
end;
我正在使用 Delphi XE 并且服务需要 运行 在 windows 服务中。
提前致谢
这很简单。您只需为每个服务设置不同的名称即可。
您现在拥有:
Name := ParamStr(2);
DisplayName := ParamStr(3);
只需将其更改为:
Name := baseServiceName + '-' + GetLastDirName;
DisplayName := baseServiceDisplayName + ' (' + GetLastDirName + ')';
其中 baseServiceName 是服务名称的常量; baseServiceDisplayName 是一个带有显示名称的常量,GetLastDirName 是一个函数,returns 来自 ExtractFilePath(ParamStr(0))
```
function GetLastDirName: string;
var
aux: string;
p: Integer;
begin
aux := strDelSlash(ExtractFilePath(ParamStr(0)));
p := StrLastPos('\', aux);
if p > 0 then
result := Copy(aux, p + 1, Length(aux))
else
result := aux;
end;
```
strDelSlash 删除最后一个斜杠; StrLastPos 搜索斜线的最后位置
由于该服务不知道它在安装时收到的名称,您可以将该名称作为参数提供到它的 ImagePath 注册表值中。
这里是多实例的基本服务框架:
unit u_svc_main;
interface
uses
Winapi.Windows,
System.Win.Registry,
System.SysUtils,
System.Classes,
Vcl.Dialogs,
Vcl.SvcMgr;
type
TSvc_test = class(TService)
procedure ServiceAfterInstall(Sender: TService);
procedure ServiceBeforeInstall(Sender: TService);
procedure ServiceCreate(Sender: TObject);
procedure ServiceBeforeUninstall(Sender: TService);
private
{ Private declarations }
procedure GetServiceName;
procedure GetServiceDisplayName;
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Svc_test: TSvc_test;
implementation
{$R *.dfm}
procedure TSvc_test.GetServiceDisplayName;
var
ServiceDisplayName : String;
begin
if not FindCmdLineSwitch('display', ServiceDisplayName) then
raise Exception.Create('Please specify the service displayname with /display switch');
DisplayName := ServiceDisplayName;
end;
procedure TSvc_test.GetServiceName;
var
ServiceName : String;
begin
if not FindCmdLineSwitch('name', ServiceName) then
raise Exception.Create('Please specify the service name with /name switch');
Name := ServiceName;
end;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Svc_test.Controller(CtrlCode);
end;
function TSvc_test.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TSvc_test.ServiceAfterInstall(Sender: TService);
var
Reg : TRegistry;
ImagePath : String;
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
// set service description
Reg.WriteString('Description', 'Multi instance test for service '+Name);
// add name parameter to ImagePath value
ImagePath := ParamStr(0) + ' /name '+Name;
Reg.WriteString('ImagePath', ImagePath);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
procedure TSvc_test.ServiceBeforeInstall(Sender: TService);
begin
GetServiceName;
GetServiceDisplayName;
end;
procedure TSvc_test.ServiceBeforeUninstall(Sender: TService);
begin
GetServiceName;
end;
procedure TSvc_test.ServiceCreate(Sender: TObject);
begin
if not Application.Installing then
GetServiceName;
end;
end.
服务安装:
<path1>\MyService.Exe /install /name "test1" /display "test instance1"
<path2>\MyService.Exe /install /name "test2" /display "test instance2"
服务删除:
<path1>\MyService.Exe /uninstall /name "test1"
<path2>\MyService.Exe /uninstall /name "test2"
@whosrdaddy 建议的解决方案对我有用。
但是事件查看器将记录的消息 (MyService.LogMessage(...)) 显示为未定义或已卸载。
如果名称和显示名称与设计时相同,这些消息可以正常显示。
很少有预定义的消息类型,在 exetubale 中链接为资源。
使用 Eventwiver,用户可以在发生任何预定义事件时附加任何用户定义的操作。
我有一个在 delphi 中制作的旧 windows 服务,现在必须在同一台服务器上多次安装,我正在尝试更改代码以便能够更改服务名称,因为我正在安装该服务,但我无法使其正常工作。
我找到了一些关于它的信息 here and some here,在研究了 post 并进行了必要的修改之后,我能够安装两个名称不同的服务,但是这些服务没有启动。
我 post class 负责控制下面的服务(继承的 TService),我知道这是相当多的代码,但我真的很感激任何帮助。
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
tvdAvalancheDataCenterService.Controller(CtrlCode);
end;
function TtvdAvalancheDataCenterService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TtvdAvalancheDataCenterService.ServiceLoadInfo(Sender : TObject);
begin
Name := ParamStr(2);
DisplayName := ParamStr(3);
end;
procedure TtvdAvalancheDataCenterService.ServiceBeforeInstall(Sender: TService);
begin
ServiceLoadInfo(Self);
end;
procedure TtvdAvalancheDataCenterService.ServiceCreate(Sender: TObject);
begin
ServiceLoadInfo(Self);
end;
procedure TtvdAvalancheDataCenterService.ServiceStart(Sender: TService;
var Started: Boolean);
begin
FtvdTrayIcon := TtvdEnvoyTrayIcon.Create(Self);
SetServiceDescription;
FtvdDataCenter.tvdActive := true;
end;
procedure TtvdAvalancheDataCenterService.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
FreeAndNil(FtvdTrayIcon);
FtvdDataCenter.tvdActive := False;
end;
procedure TtvdAvalancheDataCenterService.ServiceAfterInstall(Sender: TService);
begin
SetServiceDescription;
end;
procedure TtvdAvalancheDataCenterService.SetServiceDescription;
var
aReg: TRegistry;
begin
if FDescriptionUpdated then
Exit;
aReg := TRegistry.Create(KEY_READ or KEY_WRITE);
try
aReg.RootKey := HKEY_LOCAL_MACHINE;
if aReg.OpenKey(cnRegKey+ Name, true) then
begin
aReg.WriteString('Description', cnServiceDescription);
aReg.CloseKey;
end;
FDescriptionUpdated:= True;
finally
aReg.Free;
end;
end;
我正在使用 Delphi XE 并且服务需要 运行 在 windows 服务中。
提前致谢
这很简单。您只需为每个服务设置不同的名称即可。
您现在拥有:
Name := ParamStr(2);
DisplayName := ParamStr(3);
只需将其更改为:
Name := baseServiceName + '-' + GetLastDirName;
DisplayName := baseServiceDisplayName + ' (' + GetLastDirName + ')';
其中 baseServiceName 是服务名称的常量; baseServiceDisplayName 是一个带有显示名称的常量,GetLastDirName 是一个函数,returns 来自 ExtractFilePath(ParamStr(0))
```
function GetLastDirName: string;
var
aux: string;
p: Integer;
begin
aux := strDelSlash(ExtractFilePath(ParamStr(0)));
p := StrLastPos('\', aux);
if p > 0 then
result := Copy(aux, p + 1, Length(aux))
else
result := aux;
end;
```
strDelSlash 删除最后一个斜杠; StrLastPos 搜索斜线的最后位置
由于该服务不知道它在安装时收到的名称,您可以将该名称作为参数提供到它的 ImagePath 注册表值中。
这里是多实例的基本服务框架:
unit u_svc_main;
interface
uses
Winapi.Windows,
System.Win.Registry,
System.SysUtils,
System.Classes,
Vcl.Dialogs,
Vcl.SvcMgr;
type
TSvc_test = class(TService)
procedure ServiceAfterInstall(Sender: TService);
procedure ServiceBeforeInstall(Sender: TService);
procedure ServiceCreate(Sender: TObject);
procedure ServiceBeforeUninstall(Sender: TService);
private
{ Private declarations }
procedure GetServiceName;
procedure GetServiceDisplayName;
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Svc_test: TSvc_test;
implementation
{$R *.dfm}
procedure TSvc_test.GetServiceDisplayName;
var
ServiceDisplayName : String;
begin
if not FindCmdLineSwitch('display', ServiceDisplayName) then
raise Exception.Create('Please specify the service displayname with /display switch');
DisplayName := ServiceDisplayName;
end;
procedure TSvc_test.GetServiceName;
var
ServiceName : String;
begin
if not FindCmdLineSwitch('name', ServiceName) then
raise Exception.Create('Please specify the service name with /name switch');
Name := ServiceName;
end;
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Svc_test.Controller(CtrlCode);
end;
function TSvc_test.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TSvc_test.ServiceAfterInstall(Sender: TService);
var
Reg : TRegistry;
ImagePath : String;
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
// set service description
Reg.WriteString('Description', 'Multi instance test for service '+Name);
// add name parameter to ImagePath value
ImagePath := ParamStr(0) + ' /name '+Name;
Reg.WriteString('ImagePath', ImagePath);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
procedure TSvc_test.ServiceBeforeInstall(Sender: TService);
begin
GetServiceName;
GetServiceDisplayName;
end;
procedure TSvc_test.ServiceBeforeUninstall(Sender: TService);
begin
GetServiceName;
end;
procedure TSvc_test.ServiceCreate(Sender: TObject);
begin
if not Application.Installing then
GetServiceName;
end;
end.
服务安装:
<path1>\MyService.Exe /install /name "test1" /display "test instance1"
<path2>\MyService.Exe /install /name "test2" /display "test instance2"
服务删除:
<path1>\MyService.Exe /uninstall /name "test1"
<path2>\MyService.Exe /uninstall /name "test2"
@whosrdaddy 建议的解决方案对我有用。
但是事件查看器将记录的消息 (MyService.LogMessage(...)) 显示为未定义或已卸载。
如果名称和显示名称与设计时相同,这些消息可以正常显示。 很少有预定义的消息类型,在 exetubale 中链接为资源。
使用 Eventwiver,用户可以在发生任何预定义事件时附加任何用户定义的操作。