Delphi中带有数据库连接的Windows服务

我只想要一个关于情况的小费。

我创build了一个Windows服务来完成我的应用程序的任务pipe理。

该服务连接到数据库(Firebird)并调用执行任务pipe理的组件。

该过程工作正常,但是,在Windows 10中,服务不会在重新启动计算机后自动启动。 在其他版本的Windows中,一切正常。 在testing中,我发现如果我对调用任务执行的方法发表评论,服务通常从Windows 10开始。

Procedure TDmTaskService.ServiceExecute(Sender: TService); Begin Inherited; While Not Terminated Do Begin //Process; Sleep(3000); ServiceThread.ProcessRequests(False); End; End; 

问题是组件或服务中没有任何exception。

通过分析Windows事件监视器,我发现与我的服务发生的错误是超时,在这种情况下服务无法连接到服务pipe理器的时间限制。 没有更多的例外生成。

有没有人有任何关于在Delphi中连接到数据库的Windows服务?

我的源代码示例:

 **Base class:** unit UnTaskServiceDmBase; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs; type TDmTaskServicosBase = class(TService) private { Private declarations } public function GetServiceController: TServiceController; override; { Public declarations } end; var DmTaskServiceBase: TDmTaskServicosBase; implementation {$R *.DFM} procedure ServiceController(CtrlCode: DWord); stdcall; begin DmJBServicosBase.Controller(CtrlCode); end; function TDmTaskServicosBase.GetServiceController: TServiceController; begin Result := ServiceController; end; end. **Service Class:** Unit UnTaskServiceDm; Interface Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, UnJBTask, UnJBReturnTypes, UnJBUtilsFilesLog, UnTaskServiceDmConfig, UnTaskServiceDmConnection, ExtCtrls, IniFiles; Type TDmTaskService = Class(TDmTaskServicosBase) Procedure ServiceExecute(Sender: TService); Procedure ServiceCreate(Sender: TObject); Procedure ServiceStop(Sender: TService; Var Stopped: Boolean); Private FTaskServiceConfig: TDmTaskServiceConfig; FStatus: TResultStatus; FDmConnection: TDmTaskServiceConnection; FJBTask: TJBTask; FLog: TJBUtilsFilesLog; Procedure ExecuteTasksSchedule; Procedure UpdateServiceInformation; Procedure Process; Procedure UpdateConnection; Public Function GetServiceController: TServiceController; Override; End; Implementation {$R *.DFM} Procedure ServiceController(CtrlCode: DWord); Stdcall; Begin DmTaskService.Controller(CtrlCode); End; Procedure TDmTaskService.UpdateConnection; Begin Try FDmConnection.SqcCon.Connected := False; FDmConnection.SqcCon.Connected := True; FLog.Adicionar('Conexão com banco restabelecida.'); FLog.FinalizarLog; Except On E: Exception Do Begin FLog.Adicionar('Erro ao restabelecer conexão com o banco de dados.' + sLineBreak + sLineBreak + E.Message); FLog.FinalizarLog; End; End; End; Procedure TDmTaskService.UpdateServiceInformation; Begin Inherited; Try Try FTaskServiceConfig.Load; FLog.Adicionar('Dados registro serviço.'); FLog.Adicionar('Nome: ' + FTaskServiceConfig.ServiceName); FLog.Adicionar('Descrição: ' + FTaskServiceConfig.ServiceDescription); If (FTaskServiceConfig.ServiceName <> EmptyStr) And (FTaskServiceConfig.ServiceDescription <> EmptyStr) Then Begin Name := FTaskServiceConfig.ServiceName ; DisplayName := FTaskServiceConfig.ServiceDescription; End; FTaskServiceConfig.Close; Except On E: Exception Do Begin FLog.Adicionar('Erro adicionar dados registro serviço.'); FLog.Adicionar('Erro ocorrido: ' + sLineBreak + sLineBreak + E.Message); End; End; Finally FLog.Adicionar('Name: ' + Name); FLog.Adicionar('DisplayName: ' + DisplayName); FLog.FinalizarLog; End; End; Procedure TDmTaskService.Process; Begin Try If FDmConnection.SqcCon.Connected Then Begin ExecuteTasksSchedule; End Else UpdateConnection; Except On E: Exception Do Begin FLog.Adicionar('Ocorreu um erro ao checar as tarefas.' + sLineBreak + 'Erro ocorrido: ' + sLineBreak + E.Message); FLog.FinalizarLog; UpdateConnection; End; End; End; Procedure TDmTaskService.ExecutarTarefasAgendadas; Begin If FJBTask.ExistTaskDelayed Then Begin Try FJBTask.ExecuteTasks; Except On E: Exception Do Begin FLog.Adicionar('Ocorreu um erro ao executar as tarefas agendadas.' + sLineBreak + 'Erro ocorrido: ' + sLineBreak + E.Message); FLog.FinalizarLog; UpdateConnection; End; End; End; End; Function TDmTaskService.GetServiceController: TServiceController; Begin Result := ServiceController; End; Procedure TDmTaskService.ServiceCreate(Sender: TObject); Begin Inherited; Try FLog := TJBUtilsFilesLog.Create; FLog.ArquivoLog := IncludeTrailingPathDelimiter(FLog.LogFolder) + 'TaksService.log'; FDmConnection := TDmTaskServiceConexao.Create(Self); FDmConnection.Log := FLog; FJBTask := TJBTarefa.Create(Self); FJBTask.SQLConnection := FDmConnection.SqcConexao; FTaskServiceConfig := TDmTaskServiceConfig.Create(Self); FTaskServiceConfig.SQLConnection := FDmConnection.SqcConexao; FStatus := FDmConnection.ConfigurouConexao; If FStatus.ResultValue Then Begin UpdateServiceInformation; End Else Begin FLog.Adicionar(FStatus.MessageOut); FLog.FinalizarLog; End; Except On E: Exception Do Begin FLog.Adicionar('Não foi possível iniciar o serviço.' + sLineBreak + 'Erro ocorrido: ' + sLineBreak + sLineBreak + E.Message); FLog.FinalizarLog; Abort; End; End; End; Procedure TDmTaskService.ServiceExecute(Sender: TService); Begin Inherited; While Not Terminated Do Begin Process; Sleep(3000); ServiceThread.ProcessRequests(False); End; End; Procedure TDmTaskService.ServiceStop(Sender: TService; Var Stopped: Boolean); Begin Inherited; If Assigned(FDmConnection) Then Begin FLog.Adicionar('Finalizando serviço.'); FLog.Adicionar('Fechando conexão.'); Try FDmConnection.SqcConexao.Close; Finally FLog.FinalizarLog; End; End; End; End. 

通过分析Windows事件监视器,我发现与我的服务发生的错误是超时,在这种情况下服务无法连接到服务管理器的时间限制。 没有更多的例外生成。

TService.OnCreate事件中,不要连接到数据库或执行任何其他冗长的操作。 这种逻辑属于TService.OnStart事件。 或者更好,为它创建一个工作线程,然后在TService.OnStart事件中启动该线程,并在TService.On(Stop|Shutdown)事件中终止它。

当SCM开始你的服务进程时,它只等待新进程调用StartServiceCtrlDispatcher()的一小段时间, StartServiceCtrlDispatcher()将进程连接到SCM,以便它可以开始接收服务请求。 所有TService对象完全构建完成后,由TServiceApplication.Run()调用StartServiceCtrlDispatcher() 。 由于OnCreate事件是在您的进程尝试初始化自身时调用的,因此在调用StartServiceCtrlDispatcher()之前,服务构造中的任何延迟都可能导致SCM超时并终止进程。

此外,你应该完全摆脱你的TService.OnExecute事件处理程序。 你甚至不应该使用这个事件,而且你现在拥有的东西TService在内部没有分配任何处理器的时候OnExecute

在您的服务代码中: – 您可以尝试在Firebird服务中添加依赖关系 – 您可以增加WaitHint

如果还不行的话:可以自动启动,但是“延迟”

但是,我发现,否则要解决,但我感谢大家的提示,因为及时你会改善我的服务。

解决方案是通过Windows ServicesPipeTimeout注册表项来扩展服务启动超时。

对于我的情况,它完美的工作。 我将ServicesPipeTimeout的值增加到了120000(2分钟)。 默认值是30000(30秒)或更少。

手动编辑:

1)打开Windows注册表编辑器; 2)找到并单击下面的注册表子项: – HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control在面板值中找到ServicesPipeTimeout条目。

 ** Note **: If the ServicesPipeTimeout entry does not exist, you must create it. To do this, follow these steps: 

– 在编辑菜单上,指向新建,然后单击DWORD值。 键入ServicesPipeTimeout,然后按ENTER键。 3)右键单击ServicesPipeTimeout,然后单击修改。 4)单击十进制,键入120000,然后单击确定。 ** 120000毫秒= 2分钟5)重新启动计算机。

在德尔福(示例注册表值):

 Procedure TForm3.JBButton3Click(Sender: TObject); Const CKeyConfigTimeout = 'SYSTEM\CurrentControlSet\Control'; CValueConfigTimeout = 'ServicesPipeTimeout'; Var LReg: TRegistry; Begin LReg := TRegistry.Create; Try LReg.RootKey := HKEY_LOCAL_MACHINE; LReg.OpenKey(CKeyConfigTimeout, False); LReg.WriteInteger(CValueConfigTimeout, 120000); Finally LReg.CloseKey; FreeAndNil(LReg); End; End; 

注:具有注册表更新代码的delphi应用程序需要以Windows Vista / server或Superior版本的管理员模式运行;