Delphi Windows服务devise

Delphi Windows服务devise

我从来没有创build一个Windows服务,但一直在读我find的一切。 我所遇到的所有文章或例子都是非常基础的,并且在范围上是有限的。 没有看到任何超越这个或那个特定场景的东西。 所以,我有可能find的所有理论,现在我准备好深入这个项目。 我喜欢布置我的想法,并获得一些人们的想法反馈。 我将从应用程序描述我需要的内容,以及我打算如何构build它。 我会感谢任何有经验的build筑窗口服务和任何意见,他们会分享的意见。

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

如果有更新,则UPDATEAPPLICATION以EXE或ZIP格式下载更新,并replace相应的文件以更新目标应用程序。 之后,UPDATEAPPLICATION执行ShellExecute来启动所需的应用程序,然后closuresUPDATEAPPLICATION。

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

[问题]随着Vista和Windows 7的转移,安全性发生了巨大的变化。 由于UPDATEAPPLICATION的性质,UAC将不允许应用程序在没有pipe理权限或UAC完全closures的情况下运行。 我们正在将许多应用程序升级到.NET,在此过程中,我希望应用程序以及UPDATEAPPLICATION符合UAC标准。 从我研究过的唯一方法是通过创buildUPDATEAPPLICATION作为Windows服务。 所以,实质上,我需要将UPDATEAPPLICATION的function复制到Windows Service体系结构中。

[我的devise]我正在使用DelphiXE2。 我的devise将由三部分组成,形成一个单一的解决scheme:一个Windows服务,一个小的托盘应用程序与Windows服务进行交互,我的重新devise的应用程序将发送消息到Windows服务。

  1. 我的Windows服务(我将调用UPDATESERVICE)将作为Windows服务运行,并创build一个TCP服务器来侦听请求。
  2. 托盘应用程序(我将调用TRAYAPP)将使用TCP客户端来configuration/pipe理UPDATESERVICE。
  3. 我的USERAPPLICATION,启动时,将发送一个TCP消息到UPDATESERVICE,说“这个应用程序”已经启动。

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

现在我已经解释了我想要做的事情的基本知识,我可以问我需要回答的具体问题。 这些都与我应该如何build立我的Windows服务有关。 我也计划使用OmniThread进行我的线程pipe理。

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

  1. TCP服务是否应该在自己的线程上创build?
  2. 如果TCP服务是它自己的线程,我该如何保持线程活着? 否则,我可以启动TCP服务,但是我不确定在TCP服务单元中使用什么代码来保持线程运行?
  3. 什么Windows服务事件应该创buildTCP服务? OnExecute? 的OnStart? 在OnCreate? 毕竟我已经读了不清楚应该使用什么事件。
  4. 当TCP服务收到消息做某事时,应该在TCP服务线程中执行工作还是在主要的UPDATESERVICE产生一个新的线程? 例如:
    • 如果TCP服务获取消息来检查使用HTTP的更新,TCP服务线程产生一个新线程来完成这项工作
    • 或者,如果TCP服务线程向UPDATESERVICE发送消息以产生一个新的线程来完成这项工作
    • 它甚至重要吗?
  5. 在Delphi代码中可以启动/停止/注册/取消注册Windows服务吗?

这是我所有的问题。 这可能不是一个正确/错误的答案,而只是基于经验的偏好。 如果你用Delphi构build了服务,你可能会有一些我认为有用的东西。 如果你有一个更健壮的项目,那么一个基本的“开始一个服务和睡眠”,并愿意分享它 – 即使我不运行或只是伪代码 – 我相信这是非常宝贵的。 感谢您阅读我冗长的问题。 如果你能想到一个更好的方法去分享你的想法。 我将补充说,我们的几个应用程序可以被公众下载和运行,所以我没有完全控制预期的环境。 任何意见/意见/帮助将不胜感激。

快速回答:

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

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

while not Terminated do begin // do something end; 

4)通常每个客户端连接都将在它自己的线程上运行。 (即TCP服务器为每个客户端产生一个新的线程)。 使用像Indy或ICS这样的知名堆栈。 关于HTTP更新,您可以在派生的客户端连接线程中执行此操作。

5)是的,请注意,您需要提升权利才能做到这一点。

在我的职业生涯中,我做了不少服务,到目前为止,我总是使用同样的骨架进行服务应用:

 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.