Delphi Windows服务设计 [英] Delphi Windows Service Design

查看:160
本文介绍了Delphi Windows服务设计的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Delphi Windows服务设计



我从来没有创建过Windows服务,但一直在阅读我发现的一切。我所遇到的所有文章或例子在实施过程中都非常基础,范围有限。没有看到任何超越这一点或解决具体情况的事情。所以,我有我可能会找到的所有理论,现在我准备好深入这个项目。我喜欢布置我的想法,并得到一些关于人们的想法的反馈。我将从应用程序中描述我需​​要的内容,以及我打算如何构建它。我会感谢任何有建立Windows服务经验的人的意见,以及他们会分享的任何建议。



[SCENARIO]
现在我有一个应用程序(我会称之为UPDATEAPPLICATION),提供对所有其他应用程序的更新。为了运行我们的任何应用程序,您首先必须运行此UPDATEAPPLICATION程序并传递所需应用程序的参数。 UPDATEAPPLICATION调用一个WebService,返回关于所需应用程序是否有任何更新的XML信息。



如果有更新,UPDATEAPPLICATION将以EXE或ZIP格式下载更新,并替换适当的文件来更新目标应用程序。之后,UPDATEAPPLICATION执行ShellExecute启动所需的应用程序,然后UPDATEAPPLICATION关闭。



这是一个相当基本的过程,多年来一直运行良好。 UPDATEAPPLICATION程序是Delphi应用程序,我们的其他应用程序是混合的:Delphi,VB6,MS Access,.NET。



[问题]
随着Vista和Windows 7的转移,安全性发生了巨大变化。由于UPDATEAPPLICATION的性质,UAC将不允许应用程序在没有Admin acces或UAC完全关闭的情况下运行。我们正在将许多应用程序升级到.NET,在此过程中,我希望应用程序以及UPDATEAPPLICATION符合UAC标准。从我研究过的唯一方法是通过创建UPDATEAPPLICATION作为Windows服务。因此,本质上,我需要将UPDATEAPPLICATION的功能复制到Windows服务架构中。



[我的设计]
我使用的是DelphiXE2。我的设计将由3部分组成一个单一的解决方案:Windows服务,小托盘应用程序与Windows服务交互,以及我重新设计的应用程序将发送消息到Windows服务。


  1. 我的Windows服务(我将称之为UPDATESERVICE)将作为Windows服务运行,并创建一个TCP服务器来侦听请求。

  2. 托盘应用程序(我将称为TRAYAPP)将使用TCP Client配置/管理UPDATESERVICE。

  3. 我的USERAPPLICATION在启动时将会发送TCP消息到UPDATESERVICE,这是说这个应用程序已经开始。

[UPDATESERVICE]
将收听消息。如果收到USERAPPLICATION已经启动的消息,它会调用Web服务来查看是否有更新。如果有,将通知用户关闭应用程序,并允许UPDATESERVICE更新应用程序。 UPDATESERVICE将下载相应的文件并更新应用程序。



现在我已经解释了我正在做什么的基础知识,我可以问我的具体问题我需要回答这些都与我应该如何构建我的Windows服务有关。我也计划使用OmniThread进行线程管理。



当我的服务启动时,我需要创建TCP服务器。


  1. 应该在自己的线程上创建TCP服务?

  2. 如果TCP服务是自己的线程,我该如何保持线程活动?否则,我可以启动TCP服务,但我不知道在TCP服务单元中将使用什么代码来保持线程运行?

  3. 什么Windows服务事件应该创建TCP服务? OnExecute? OnStart? OnCreate?毕竟我已经阅读不清楚应该使用什么事件。

  4. 当TCP服务收到消息以执行某些操作时,应该在TCP Service线程或新线程中执行工作关闭主UPDATESERVICE?例如:

    • 如果TCP服务得到一个消息来检查使用HTTP的更新,如果TCP服务线程产生一个新线程来完成这项工作

    • 或者,如果TCP服务线程向UPDATESERVICE发送消息以产生一个新线程来执行此工作

    • 它甚至是否重要?


  5. 是否可以在Delphi代码中启动/停止/注册/注销Windows服务?

这是我所有的问题。这可能不是一个正确/错误的答案,而只是基于经验的偏好。如果您使用Delphi构建服务,您可能有一些我会发现有用的输入。如果你有一个更强大的项目,那么一个基本的开始一个服务和睡眠,并愿意分享它 - 即使我没有运行或只是伪装的代码 - 我相信这将是无价的。感谢您阅读我长久以来的问题。如果你能想到一个更好的方法来分享你的想法。我将补充说,我们的几个应用程序可以由公众下载和运行,所以我没有完全控制预期的环境。任何建议/意见/帮助将不胜感激。

解决方案

快速答案:



1& 3)是。根据经验,不要实现OnExecute服务事件。从OnStart服务事件中生成自己的线程。当您收到OnStop服务事件时,线程可以被终止。



2)你保持这样的线程(执行方法):

 而不是终止do 
begin
//做某事
end;

4)通常每个客户端连接都将生活在自己的线程上。 (即TCP服务器为每个客户端生成一个新线程)。使用一个众所周知的堆栈,如Indy或ICS。关于HTTP更新,您可以在生成的客户端连接线程中执行此操作。



5)是的,请注意,您需要提升权限才能执行此操作。

在我的职业生涯中,我已经做了不少服务,到现在为止,我总是使用相同的服务应用程序:

  unit u_svc_main; 

接口

使用
//自己的单位
u_globals,u_eventlog,u_MyThread,
//第三方单位
/ / Delphi单位
Windows,消息,注册表,SysUtils,类,SvcMgr;

type
TMyService = class(TService)
procedure ServiceCreate(Sender:TObject);
procedure ServiceAfterUninstall(Sender:TService);
procedure ServiceAfterInstall(Sender:TService);
程序ServiceShutdown(发件人:TService);
procedure ServiceStop(Sender:TService; var Stopped:Boolean);
procedure ServiceStart(Sender:TService; var Started:Boolean);
private
{私有声明}
MyThread:TMyThread;
public
{公开声明}
函数GetServiceController:TServiceController;覆盖
结束

var MyService:TMyService;

实现

{$ R * .DFM}

程序ServiceController(CtrlCode:DWord);标准
begin
MyService.Controller(CtrlCode);
结束

函数TMyService.GetServiceController:TServiceController;
begin
结果:= ServiceController;
结束

procedure TMyService.ServiceCreate(Sender:TObject);
begin
DisplayName:='myservice';
结束

程序TMyService.ServiceAfterInstall(Sender:TService);
var
注册:TRegistry;
ImagePath:string;
begin
//在服务安装后创建需要的注册表项
注意:= TRegistry.Create;
try
Reg.RootKey:= HKEY_LOCAL_MACHINE;
//设置服务描述
如果Reg.OpenKey(STR_REGKEY_SVC,False)然后
begin
ImagePath:= Reg.ReadString(STR_REGVAL_IMAGEPATH);
Reg.WriteString(STR_REGVAL_DESCRIPTION,STR_INFO_SVC_DESC);
Reg.CloseKey;
结束
//为eventlog
设置消息资源,如果Reg.OpenKey(STR_REGKEY_EVENTMSG,True)则
begin
Reg.WriteString(STR_REGVAL_EVENTMESSAGEFILE,ImagePath);
Reg.WriteInteger(STR_REGVAL_TYPESSUPPORTED,7);
Reg.CloseKey;
结束
// set installdir
如果ImagePath<> ''然后
如果Reg.OpenKey(STR_REGKEY_FULL,True)然后
begin
Reg.WriteString(STR_REGVAL_INSTALLDIR,ExtractFilePath(ImagePath));
Reg.CloseKey;
结束
finally
FreeAndNil(Reg);
结束
结束

程序TMyService.ServiceAfterUninstall(发件人:TService);
var
注册:TRegistry;
begin
注册:= TRegistry.Create;
尝试
//删除自己创建的注册表项
Reg.RootKey:= HKEY_LOCAL_MACHINE;
Reg.DeleteKey(STR_REGKEY_EVENTMSG);
finally
FreeAndNil(Reg);
结束
结束

procedure TMyService.ServiceShutdown(Sender:TService);
var
Stopped:boolean;
begin
//当Windows关闭时调用
ServiceStop(Self,Stopped);
结束

procedure TMyService.ServiceStart(Sender:TService; var Started:Boolean);
begin
开始:= False;
try
MyThread:= TMyThread.Create;
MyThread.Resume;
NTEventLog.Add(Eventlog_Success,STR_INFO_SVC_STARTED);
开始:= True;
除了
在E:Exception do
begin
//在事件日志中添加事件,为什么服务无法启动
NTEventLog.Add(Eventlog_Error_Type,Format STR_INFO_SVC_STARTFAIL,[E.Message]));
结束
结束
结束

procedure TMyService.ServiceStop(Sender:TService; var Stopped:Boolean);
begin
try
停止:= True; //始终停止服务,即使我们有异常,这是为了防止卡住服务(必须重新启动)
MyThread.Terminate;
//给MyThread 60秒终止
如果WaitForSingleObject(MyThread.ThreadEvent,60000)= WAIT_OBJECT_0然后
begin
FreeAndNil(MyThread);
NTEventLog.Add(Eventlog_Success,STR_INFO_SVC_STOPPED);
结束
除了
在E:Exception do
begin
//在事件日志中添加事件,为什么服务无法停止
NTEventLog.Add(Eventlog_Error_Type,Format STR_INFO_SVC_STOPFAIL,[E.Message]));
结束
结束
结束

结束。


Delphi Windows Service Design

I've never created a windows service but have been reading everything I've found. All the articles or examples I've run across are very basic in implementation and limited in their scope. Haven't seen anything that goes beyond this or that address specific scenarios. So, I have all the theory I'm probably going to find, and now I'm ready to dive into this project. I like to layout my ideas and get some feedback on what people think. I'll describe what I need from the application and how I intend to build it. I'd appreciate comments from anyone who has experience building windows services and any advice they would care to share.

[SCENARIO] Right now I have an application (I'll call this UPDATEAPPLICATION) that provides updates to all our other applications. In order to run any of our applications you first have to run this UPDATEAPPLICATION program and pass it a parameter of the desired application. The UPDATEAPPLICATION calls a WebService that returns XML information as to whether the desired application has any updates.

If there is an update, the UPDATEAPPLICATION downloads the update in EXE or ZIP format, and replaces the appropriate files to update the targeted application. Afterwards the UPDATEAPPLICATION does a ShellExecute to start the desired application and then the UPDATEAPPLICATION closes.

It's a fairly basic process that has worked well over the years. The UPDATEAPPLICATION program is a Delphi application, our other applications are mixed: Delphi, VB6, MS Access, .NET.

[THE PROBLEM] With the move to Vista and Windows 7, the security has changed dramatically. Because of the nature of the UPDATEAPPLICATION UAC won't allow the application to run under without Admin acces or UAC completely turned off. We are in the process of upgrading many of our applications to .NET and during this process I'd like the applications as well as the UPDATEAPPLICATION be UAC compliant. From what I've researched the only way to do this is by creating the UPDATEAPPLICATION as Windows Service. So, essentially, I need to duplicate the functionality of the UPDATEAPPLICATION into a Windows Service architecture.

[MY DESIGN] I'm using DelphiXE2. My design will consist of 3 parts to form a single solution: a Windows Service, a small tray Application to interact with the Windows Service, and my redesigned applications that will send messages to the Windows Service.

  1. My Windows Service (which I will call the UPDATESERVICE) will run as a Windows Service and create a TCP server to listen for requests.
  2. The tray application (which I will call TRAYAPP) will use TCP Client to configure/manage the UPDATESERVICE.
  3. My USERAPPLICATION, when started, will send a TCP message to UPDATESERVICE that's says "THIS APPLICATION" has started.

[UPDATESERVICE] Will listen for messages. If it receives a message that a USERAPPLICATION has started will it will call the web service to see if there are updates. If there are, the user will be notified to close the application and allow the UPDATESERVICE to update the application. The UPDATESERVICE will download the appropriate files and update the application.

Now that I've explained the basics of what I'm trying to do, I can ask my specific questions I need answered. These all have to do with how I should build my Windows Service. I also plan on using OmniThread for my thread management.

When my service starts, I need to create the TCP Server.

  1. Should the TCP Service be created on it's own thread?
  2. If the TCP Service is it's own thread, how do I keep the thread alive? Otherwise, I can start the TCP Service but I'm not sure what code I would use within the TCP Service unit to keep the thread running?
  3. What Windows Services event should create the TCP Service? OnExecute? OnStart? OnCreate? After all I've read it's unclear what event should be used.
  4. When the TCP Service receives a message to do something, should the work be executed within TCP Service thread or a new thread spawned off the main UPDATESERVICE? For example:
    • if the TCP Service gets a message to check for an update using HTTP should the TCP Service thread spawn a new thread to do this work
    • Or, should the TCP Service thread send a message to the UPDATESERVICE to spawn a new thread to do this work
    • Does it even matter?
  5. Is it possible to Start/Stop/Register/Unregister a windows service in Delphi Code?

This is all my questions. There probably isn't a right/wrong answer for this but simply a preference based on experience. If you've built services with Delphi you probably have some input that I would find useful. If you have a project that is more robust then a basic "start a service and sleep" and are willing to share it - even if I doesn't run or just psuedo code - I'm sure this would be invaluable. Thanks for reading my long-winded question. If you can think of a better way to go about this please share your thoughts. I'll add that several of our applications can be downloaded and run by the general public, so I don't have complete control over the expected environments. Any advice/comments/help would be appreciated.

解决方案

fast answers:

1&3) Yes. As a rule of thumb do not implement the OnExecute service event. Spawn your own thread from the OnStart service event. The thread can be terminated when you receive the OnStop service event.

2) you keep your thread alive like this (execute method):

while not Terminated do
begin
  // do something
end;

4) normally each client connection will live on it's own thread. (ie the TCP server spawns a new thread for each client). Use a well known stack like Indy or ICS. Concerning the HTTP update, you can do this in the spawned client connection thread.

5) yes, be aware that you need elevated rights to do this.

I have made quite a few services in my career and I always use the same skeleton for the service application up till now:

unit u_svc_main;

interface

uses
  // Own units
  u_globals, u_eventlog, u_MyThread, 
  // Third party units
  // Delphi units
  Windows, Messages, Registry, SysUtils, Classes, SvcMgr;

type
  TMyService = class(TService)
    procedure ServiceCreate(Sender: TObject);
    procedure ServiceAfterUninstall(Sender: TService);
    procedure ServiceAfterInstall(Sender: TService);
    procedure ServiceShutdown(Sender: TService);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
  private
    { Private declarations }
    MyThread : TMyThread;
  public
    { Public declarations }
    function GetServiceController: TServiceController; override;
  end;

var MyService : TMyService;

implementation

{$R *.DFM}

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

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

procedure TMyService.ServiceCreate(Sender: TObject);
begin
  DisplayName := 'myservice';
end;

procedure TMyService.ServiceAfterInstall(Sender: TService);
var
  Reg        : TRegistry;
  ImagePath  : string;
begin
  // create needed registry entries after service installation
  Reg := TRegistry.Create;
  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    // set service description
    if Reg.OpenKey(STR_REGKEY_SVC,False) then
    begin
      ImagePath := Reg.ReadString(STR_REGVAL_IMAGEPATH);
      Reg.WriteString(STR_REGVAL_DESCRIPTION, STR_INFO_SVC_DESC);
      Reg.CloseKey;
    end;
    // set message resource for eventlog
    if Reg.OpenKey(STR_REGKEY_EVENTMSG, True) then
    begin
      Reg.WriteString(STR_REGVAL_EVENTMESSAGEFILE, ImagePath);
      Reg.WriteInteger(STR_REGVAL_TYPESSUPPORTED, 7);
      Reg.CloseKey;
    end;
    // set installdir
    if ImagePath <> '' then
      if Reg.OpenKey(STR_REGKEY_FULL,True) then
      begin
        Reg.WriteString(STR_REGVAL_INSTALLDIR, ExtractFilePath(ImagePath));
        Reg.CloseKey;
      end;
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceAfterUninstall(Sender: TService);
var
  Reg : TRegistry;
begin
  Reg := TRegistry.Create;
  try
    // delete self created registry keys
    Reg.RootKey := HKEY_LOCAL_MACHINE;
    Reg.DeleteKey(STR_REGKEY_EVENTMSG);
  finally
    FreeAndNil(Reg);
  end;
end;

procedure TMyService.ServiceShutdown(Sender: TService);
var
  Stopped : boolean;
begin
  // is called when windows shuts down
  ServiceStop(Self, Stopped);
end;

procedure TMyService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  Started := False;
  try
    MyThread := TMyThread.Create;
    MyThread.Resume;
    NTEventLog.Add(Eventlog_Success, STR_INFO_SVC_STARTED);
    Started := True;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't start
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STARTFAIL, [E.Message]));
    end;
  end;
end;

procedure TMyService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  try
    Stopped := True; // always stop service, even if we had exceptions, this is to prevent "stuck" service (must reboot then)
    MyThread.Terminate;
    // give MyThread 60 seconds to terminate
    if WaitForSingleObject(MyThread.ThreadEvent, 60000) = WAIT_OBJECT_0 then
    begin
      FreeAndNil(MyThread);
      NTEventLog.Add(Eventlog_Success,STR_INFO_SVC_STOPPED);
    end;
  except
    on E : Exception do
    begin
      // add event in eventlog with reason why the service couldn't stop
      NTEventLog.Add(Eventlog_Error_Type, Format(STR_INFO_SVC_STOPFAIL, [E.Message]));
    end;
  end;
end;

end.

这篇关于Delphi Windows服务设计的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆