Delphi Windows 服务设计

2023-12-03

Delphi Windows 服务设计

我从未创建过 Windows 服务,但一直在阅读我发现的所有内容。我遇到的所有文章或示例在实现上都非常基础,并且范围有限。还没有看到任何超出此范围或解决特定场景的内容。所以,我已经掌握了我可能会找到的所有理论,现在我准备好深入研究这个项目了。我喜欢阐述我的想法并获取人们想法的反馈。我将描述我需要从应用程序中获得什么以及我打算如何构建它。我非常感谢任何有构建 Windows 服务经验的人的评论以及他们愿意分享的任何建议。

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

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

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

[问题] 随着迁移到 Vista 和 Windows 7,安全性发生了巨大变化。由于 UPDATEAPPLICATION UAC 的性质,不允许应用程序在没有管理员访问权限或 UAC 完全关闭的情况下运行。我们正在将许多应用程序升级到.NET,在此过程中我希望应用程序以及 UPDATEAPPLICATION 符合 UAC 要求。根据我的研究,执行此操作的唯一方法是将 UPDATEAPPLICATION 创建为 Windows 服务。因此,本质上,我需要将 UPDATEAPPLICATION 的功能复制到 Windows 服务体系结构中。

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

  1. 我的 Windows 服务(我将其称为 UPDATESERVICE)将作为 Windows 服务运行并创建一个 TCP 服务器来侦听请求。
  2. 托盘应用程序(我将其称为 TRAYAPP)将使用 TCP 客户端来配置/管理 UPDATESERVICE。
  3. 我的 USERAPPLICATION 启动后,将向 UPDATESERVICE 发送一条 TCP 消息,表示“此应用程序”已启动。

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

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

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

  1. TCP 服务应该在它自己的线程上创建吗?
  2. 如果 TCP 服务是它自己的线程,我如何保持线程活动?否则,我可以启动 TCP 服务,但我不确定在 TCP 服务单元中使用什么代码来保持线程运行?
  3. 哪个 Windows 服务事件应该创建 TCP 服务?执行时?开始?在创建时?毕竟我读过之后还不清楚应该使用什么事件。
  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:
    • 如果 TCP 服务收到一条消息以使用 HTTP 检查更新,则 TCP 服务线程应该生成一个新线程来执行此工作
    • 或者,TCP 服务线程是否应该向 UPDATESERVICE 发送消息以生成新线程来完成此工作
    • 这还重要吗?
  5. 是否可以在 Delphi 代码中启动/停止/注册/取消注册 Windows 服务?

这就是我所有的问题。对此可能没有正确/错误的答案,而只是基于经验的偏好。如果您使用 Delphi 构建过服务,您可能会得到一些我认为有用的输入。如果您有一个比基本的“启动服务并睡眠”更强大的项目,并且愿意分享它 - 即使我不运行或只是伪代码 - 我相信这将是无价的。感谢您阅读我的冗长问题。如果您能想到更好的方法来解决此问题,请分享您的想法。我要补充的是,我们的一些应用程序可以由公众下载和运行,因此我无法完全控制预期的环境。任何建议/评论/帮助将不胜感激。


快速回答:

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.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Delphi Windows 服务设计 的相关文章

  • 从 Powershell 更新计划任务脚本

    我正在尝试编写一个每天运行一次的 Powershell 脚本 它将执行的众多功能之一是确保脚本本身是最新的 我的问题是 由于我对脚本进行了版本控制 因此我需要更新从脚本中创建的计划任务 我在这里考虑了两种不同的方法 其中我也无法弄清楚 我最
  • Delphi中AsInteger和Value有什么区别?

    我想知道以下两个与delphi中的数据集相关的语句之间的区别 dsMyDataSet ParamByName ID AsInteger 1122 If ID is integer dsMyDataSet ParamByName ID AsS
  • 使用 IOmniTaskControl/TOmniWorker 时等待 Invoke 完成

    我使用 TOmniWorker 创建了 IOmniTaskControl 以便我可以定期在特定线程上运行代码块 因此 我将根据需要在此 IOmniTaskControl 上调用 Invoke 当我这样做时 有时需要等待与该工作相关的执行完成
  • 如何通过批处理文件打开Windows防火墙上的端口

    有没有办法在批处理文件中通过批处理文件打开 Windows 上的特定端口 如果让安装程序为我们的服务器应用程序执行此操作 而不是让用户手动执行此操作 那就太好了 Use 执行程序 http technet microsoft com en
  • 验证域用户凭据

    我需要一种方法来验证 Windows 上本机 C 的用户 密码对 输入的是用户名和密码 用户可以是 DOMAIN user 格式 基本上我需要编写一个函数 如果用户 密码是有效的本地帐户 则返回 true 第1部分 如果用户 密码在给定的域
  • 安装 JDK 时出错:keytool 命令需要已安装的 proc fs (/proc)。 Linux 的 Windows 子系统

    我尝试在 Linux 的 Windows 子系统 Ubuntu 14 04 上安装 Oracle JDK 1 7 但出现以下错误 the keytool command requires a mounted proc fs proc Jav
  • 如何使用 WinAPI 读取 MessageBox 文本

    如何读取标准Win消息框 Info 的消息 Using SendMessage this HandleControl WM GETTEXT builder Capacity builder 我只能读取消息框的标题或按钮的文本 但不能读取消息
  • MinGW g++ 在自己的包含目录中找不到标头

    所以我最近通过最新版本安装了 MinGWnuwen 的 MinGW 发行版 http nuwen net mingw html其中包括 boost C 库 具体来说 我正在寻找 boost 库提供的scoped ptr 但是 当我尝试包含s
  • 如何在Delphi中实现人工神经网络? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我想要一个人工神经网络 42 个输入神经元 168 个隐藏神经元 7个输出神经元 这个网络就是玩 连四子 的游戏 每场比赛结束时 网络
  • 第三方库应该放在哪里?

    我为一个相当规模的 C 项目 http github com nickgammon mushclient具有许多依赖关系 问题是 该项目包含其所有依赖项的源代码 例如 pcre zlib 等 我想将项目精简为与程序本身相关的内容 是否有一些
  • 如何将 .bat 文件中的变量获取到 PowerShell 脚本中?

    我正在用 PowerShell 替换 bat 脚本的部分内容 批处理文件的配置是通过以下文件完成的set适当的环境变量 我正在寻找一种方法将这些变量值加载到 ps1脚本 无需修改 bat文件 因为它们也在其他地方使用 一个例子 bat看起来
  • Delphi:如何检查是否按下了任何鼠标按钮 - 在鼠标事件之外?

    我有一个 TDrawGrid 想要处理单击单元格并使用鼠标滚轮滚动单元格的方式略有不同 使用鼠标滚轮滚动时 视图应以选定的单元格为中心 而仅单击单元格时 视图不应居中移动 因为这会令人困惑 用鼠标滚轮滚动会触发OnSelectCell事件
  • 为什么 %processor_architecture% 总是返回 x86 而不是 AMD64

    我正在尝试检索环境变量来检测系统是32位还是64位 但在64位服务器上环境变量 processor architecture 正在返回x86代替AMD64 有人对此有任何线索吗 您可能获得了错误的环境变量 如果您的应用程序是在 64 位操作
  • 有什么工具可以了解 Windows 中正在运行的进程的布局(段)?

    我一直很好奇 该进程在内存中到底是什么样子的 其中有哪些不同的部分 部分 程序 在磁盘上 和进程 在内存中 到底是如何相关的 我之前的问题 有关可执行程序 进程 的内存布局的更多信息 https stackoverflow com ques
  • 使用搜索词打开 Windows 资源管理器查找窗格

    我正在尝试创建一个应用程序 该应用程序打开 Windows 资源管理器搜索功能并搜索输入了指定术语的文件 文件夹 我已经有了 Windows Vista SP1 使用 Windows 索引服务引入的 search query 命令的代码 这
  • 如何仅回显“开”或“关”文本?

    大家都知道 在 Windows 命令文件中 cmd echo on echo off 启用和禁用回显 但是如何仅回显文本 on 或文本 off IE 如何发送文本 on off to stdout 目标系统 Windows XP 那么在 D
  • 如何使用 cython 编译扩展?

    我正在尝试从示例页面编译一个简单的 cython 扩展here http docs cython org src userguide tutorial html在我安装了 Python 2 6 64 位版本的 Windows 7 64 位计
  • 无法打开 Python。错误 0xc000007b

    我最近一直在学习 Python 3 我在我的上网本 32 位 Windows 7 上创建简单的小程序没有任何问题 当我将它安装在我的上网本上时 我没有遇到任何问题 但现在我已经开始使用它了 我想将它安装在我的台式机上 并且我有一个 我的桌面
  • win32 GUI 应用程序在作为“app.exe --help”调用时将使用文本写入标准输出

    如何创建执行以下操作的 Windows 应用程序 当不使用命令行参数调用时 它是一个常规的 GUI 应用程序 指定可选的 help 命令行参数会导致应用程序将使用文本写入标准输出 然后终止 它必须是单个可执行文件 让控制台应用程序执行第二个
  • 批处理文件 - 读取特定行,并将该行中的特定字符串保存为变量

    有没有办法让 for f 循环 或其他任何东西 读取特定行 这是我到目前为止的代码 它读取每一行的第一个单词 echo off set file readtest txt for f tokens 1 delims A in file do

随机推荐

  • 如何使用 html2canvas.js 将带有 @font-face 的 SVG 文本元素转换为画布?

    我正在尝试使用html2canvas js https html2canvas hertzen com 使用 JavaScript 截取屏幕截图 但是 当页面包含 SVG 文本元素时 我遇到了问题 正如您在下面的屏幕截图中看到的 文本元素是
  • gcc 中的扩展 asm:“asm”操作数具有不可能的约束

    这个函数 strcpy 的目的是复制内容src to dest 效果很好 显示两行 Hello src include
  • 如何在 SqlCommand 中设置未命名的 SQL 参数

    如果我将未命名的参数添加到我的SqlCommand 如何在 SqlCommand 上设置参数值 当我使用命名参数时 我没有任何问题 我可以这样做SqlCommand Parameters AddWithValue Example Selec
  • Prestashop - 根据付款方式设置不同的价格?

    我正在使用 Prestashop 1 5 6 我需要根据所选方法为订单设置不同的总价 某些模块可以与信用卡进行中介 例如 Mercado Pago 模块 我需要提高价格在这种情况下 是否可以使用商店本身的功能来做到这一点 是否有模块 解决方
  • 在 C 中实现多态性是否需要不兼容的指针分配

    我尝试用以下代码在C中模拟C 的多态性 include
  • 如何从 VBA 运行参数化查询。来自记录集的参数

    我有一个表单 用户可以从组合框中选择供应商的名称 该供应商的目录文件将被导入 然后 组合框选择驱动一个查询来创建一个单记录记录集 rsProfile 其中包含从所有供应商配置文件表中查询的多个配置文件变量 然后 这些变量用于一系列不同的查询
  • 为什么 Kotlin 中每个辅助构造函数都需要委托给主构造函数?

    作为 kotlin 参考类和继承 say 如果类具有主构造函数 则每个辅助构造函数都需要直接或通过另一个辅助构造函数间接委托给主构造函数 我不明白为什么 kotlin 辅助构造函数需要这样做 它可以防止Java中的一些问题吗 这是因为ini
  • SSRS 列表报告中的分组依据

    我正在制作 SSRS 报告 我必须按客户名称进行分组 数据集将是这样的 从客户中选择Column1 Column2 Column2 按客户名称分组 Expected Report Layout Customer Name Jim Mouse
  • 是否有一个 css 属性不会改变任何内容以及我们可以在哪里存储信息?

    是否有一个 css 属性不会改变任何内容 我在写作时经常需要这个来进行测试scss只是为了看看我是否正确创建了一个 css 选择器 例如 我很高兴有这样的东西foo helloworld1 稍后我就可以改变的值foo并检查开发人员工具中的值
  • Spring Security OAuth2:InsufficientAuthenticationException

    首先 我禁用基本身份验证 security basic enabled false 然后我访问授权页面 http localhost 8080 oauth authorize client id client response type c
  • 如果在 iframe 中则隐藏标题

    我需要隐藏基于 WordPress 的网站的标头 以防该网站加载到 iFrame 中 我应该用 javascript 函数还是 css 来实现 我该怎么做呢 我找到了这个
  • 电话号码的 UITextField

    我想知道如何格式化我用于电话号码的文本字段 即像iPhone上的 添加新联系人 页面 当我输入新手机时 例如1236890987 它将其格式设置为 123 689 0987 我已经将键盘设置为数字键盘 这是我的解决方案 效果很好 实时格式化
  • 在MATLAB中绘制椭圆和椭球体

    如何使用 MATLAB 绘制椭圆和椭球体 x 2 a 2 y 2 b 2 1 n 40 a 0 b 2 pi c 0 d 2 pi for i 1 n u a b a i 1 n 1 for j 1 m v a d c j 1 m 1 x
  • try catch 块中未捕获异常

    我做一个简单的抛出 测试抛出 它没有被我的catch std 异常 e 是因为我正在抓一个std 异常 e 我的意思是 只有从 std exception 派生的异常类才会被捕获吗 如果不是 是我做错了什么还是正常的 顺便说一句 两个 ca
  • 使用JAVA在HANA中插入数组

    我有一个对象数组列表 并尝试将该列表插入到 HANA 中 所以我的插入代码看起来像 PreparedStatement stmt conn prepareStatement INSERT INTO SCHEMA TABLE VALUES A
  • 在 inno Setup 中使用 DOM 添加节点到 xml - 奇怪的问题

    非常奇怪的问题 我使用 DOM 编辑 xml 文件 需要与我们交互的应用程序的 exe config 文件 但是由于我必须批量添加几个类似的部分 所以我创建了一个函数来插入整个需要的块 调用这个函数一次就完美了 之后使用不同的参数再次调用它
  • 使用 GROUP SEPARATOR 的 Explode() 函数

    根据发现通过MySQL GROUP CONCAT 转义这个我有用GROUP CONCAT SELECT topic response response GROUP CONCAT comment SEPARATOR 0x1D AS comme
  • 你能用 javascript (jxa) 编写文件夹操作吗?

    我正在寻找 AppleScript 的直接翻译 on adding folder items to this folder 但我似乎无法在任何地方找到它 无论是术语的用法还是关键字 this folder 本身 使用 Automator 我
  • 在引导输入字段中放置清除按钮

    我试图在输入字段内 搜索图标之前的右侧放置一个清除按钮 但它不起作用 x 显示在输入字段的前面 我使用绝对定位 right 0 和 top 4px 您可以在这里查看我的示例 http www bootply com YUwdJ5Kvx6 一
  • Delphi Windows 服务设计

    Delphi Windows 服务设计 我从未创建过 Windows 服务 但一直在阅读我发现的所有内容 我遇到的所有文章或示例在实现上都非常基础 并且范围有限 还没有看到任何超出此范围或解决特定场景的内容 所以 我已经掌握了我可能会找到的