如何使用Delphi调试windows服务?

如何使用Delphi调试windows服务?,delphi,debugging,windows-services,service,Delphi,Debugging,Windows Services,Service,有没有办法用Delphi完全调试windows服务?有,有: 您是否使用Delphi创建服务? 那么也许你也对这件事感到恼火 耗时的启动方式, 重新启动、终止并连接到 每个应用程序的服务流程 时间嗯,有补救办法 你不需要这样做。而是跑 Delphi作为一个系统的应用和做 对服务进行了一些小的调整 代码 有 在您的dpr中: Application.CreateForm(TMyService, MyService); _ServiceInDebugMode := SysUtils.FindCm

有没有办法用Delphi完全调试windows服务?

有,有:

您是否使用Delphi创建服务? 那么也许你也对这件事感到恼火 耗时的启动方式, 重新启动、终止并连接到 每个应用程序的服务流程 时间嗯,有补救办法

你不需要这样做。而是跑 Delphi作为一个系统的应用和做 对服务进行了一些小的调整 代码

在您的dpr中:

Application.CreateForm(TMyService, MyService);

_ServiceInDebugMode := SysUtils.FindCmdLineSwitch('DEBUG', True);
if _ServiceInDebugMode then
  DebugService(MyService)
else
  SvcMgr.Application.Run;
DebugService是一个创建调试窗体、服务控制线程并通过调用Forms.Application.Run启动一切的过程

您可以将服务控制线程与Windows的SCM(服务控制管理器)进行比较,将调试窗体与与与SCM(如services.msc)对话以启动和停止服务的应用程序进行比较。服务还应该有自己的线程(服务线程)来响应来自SCM或我们的服务控制线程的控制代码,并有一个或多个单独的线程来执行其实际工作。您需要为实际工作使用单独的线程(而不是在TService子代的事件处理程序中对其进行编码),以确保TService本身运行的线程始终可以自由响应SCM的控制代码,并且即使在每个工作线程冻结时,您仍然可以停止和启动服务

这种方法也允许您调试服务应用程序代码,但需要相当多的代码,并在windows api函数中放置几个钩子才能正常工作。太多了,不能在短时间内展示。也许有一天我会把它写在一篇文章里


同时,如果你不想自己编写代码,你有两个选择。可以使用SVCOM或Mick提到的库,它们可以为您完成所有工作,或者在调试模式下,将服务代码全部传递到一起,并“简单地”作为“普通”表单应用程序启动您的服务。您必须将服务的真正功能与TService子代的代码/事件处理程序分离开来,但出于上述原因,我还是建议您这样做。

您可以使用unitDebugService.pas from(页面已消失,)

然后在朝鲜:

begin
  if (paramCount > 0) and (SameText(ParamStr(1), '-DEBUG')) then
  begin
    FreeAndNil (Application);
    Application := TDebugServiceApplication.Create(nil);
  end;

  //... the rest of the normal DPR code
end.

通过这种方式,您可以在Delphi中通过调试运行(通过设置项目调试器参数),将EXE用作服务,或者使用
-DEBUG
开关从命令行运行,以及使用运行->附加到进程。通过这种方式,您可以调试服务,而无需对其代码进行任何更改。唯一棘手的部分可能是调试服务启动代码,因为连接可能需要一些时间,并且启动必须在30秒内完成(尽管您可以调整窗口以允许更长的时间)。您可以使用延迟(sleep…)来及时附加,或者如果您只需要查看发生了什么,可以使用OutputDebugString()打印到调试输出(使用Delphi事件视图查看)


我已经试过了,但只在cpu窗口中显示汇编代码

那么你只需要解决这个问题

基本上,要调试Win2服务,有几种方法:

  • 使用“附加到进程”命令将调试器附加到已经运行的服务。如果一开始就需要附加调试器,可以插入启动延迟以有时间附加调试器。但是,您还必须调整系统以增加服务超时
  • 用于在服务启动时强制运行Delphi的调试器。关于系统超时的考虑同样适用
  • 将服务临时转换为常用应用程序,并在调试器下正常运行。您可以在不同的用户帐户下重新启动IDE,以获得“服务”的更多特权
如果由于某种原因,您在启动调试后只有服务的CPU视图,这意味着Delphi的调试器无法找到服务的调试信息。这是一个不同的问题,您应该寻找解决方案

通常,您需要执行以下操作:

  • 确保服务应用程序的输出文件夹设置为系统将从中加载服务应用程序的文件夹。例如,如果您的服务位于C:\Windows\System32中,则将输出文件夹设置为C:\Windows\System32。不要将.exe文件从输出文件夹复制到其他位置。对于64个系统,您可以尝试别名(sysnative/SysWOW64)或实名。我认为最好将输出路径设置为项目文件夹,并重新注册要从项目文件夹加载的服务
  • (可选)将DCU的输出路径设置为与.exe文件相同的文件夹
  • 删除所有DCU文件
  • 确保在项目选项的“编译器”页面上启用调试选项
  • (可选)此外,您还可以在“链接器”页面上包括TD32/RSM/MAP选项
  • 确保没有IDE专家/扩展名,它可能会修改这些文件的.exe文件、调试信息或文件修改日期
  • 确保其他位置没有旧文件(DCU/exe)
  • 进行完整重建(项目/全部生成)
  • 运行您的服务

  • 其实很简单。只需使用标准的DEBUG编译器指令作为控制台应用程序而不是服务启动服务

    program MyServiceApp;
    
    {$ifdef DEBUG}
      {$APPTYPE CONSOLE}
    {$endif}
    
    uses
      System.SysUtils,
    
    [……]


    RunAsSys听起来不错。您链接的文章中的示例代码使用条件定义来选择“模式”。你知道RunAsSys是否允许选择调试模式而不必重新构建应用程序吗?Ie使用命令行参数进入调试模式,而不是作为服务运行?我不确定,但您似乎可以自己将该功能添加到其中。链接不再工作。我尝试过此操作,但仅显示带有汇编代码的cpu窗口。
    附加到进程
    工作正常。当调试器第一次连接到服务进程时,确实会出现CPU窗口,但您可以简单地将其关闭,然后按F9或Run按钮继续正常执行服务,然后
    begin
      {$ifdef DEBUG}
      try
        // In debug mode the server acts as a console application.
        WriteLn('MyServiceApp DEBUG mode. Press enter to exit.');
    
        // Create the TService descendant manually.
        ServerContainer1 := TServerContainer.Create(nil);
    
        // Simulate service start.
        ServerContainer1.ServiceStart(ServerContainer1, MyDummyBoolean);
    
        // Keep the console box running (ServerContainer1 code runs in the background)
        ReadLn;
    
        // On exit, destroy the service object.
        FreeAndNil(ServerContainer1);
      except
        on E: Exception do
        begin
          Writeln(E.ClassName, ': ', E.Message);
          WriteLn('Press enter to exit.');
          ReadLn;
        end;
      end;
      {$else}
      // Run as a true windows service (release).
      if not Application.DelayInitialize or Application.Installing then
        Application.Initialize;
      Application.CreateForm(TServerContainer, ServerContainer1);
      Application.Run;
      {$endif}
    end.