确定是否作为 VCL 表单或服务运行
我有在服务和 VCL Form 应用程序(win32 应用程序)中使用的代码。如何确定底层应用程序是作为 NT 服务还是作为应用程序运行?
谢谢。
I have code which is used both in services and within VCL Form applications (win32 application). How can I determine if the underlying application is running as a NT Service or as an application?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
开始编辑
由于这似乎仍然引起了一些关注,我决定用缺少的信息和更新的 Windows 补丁来更新答案。在任何情况下,您都不应该复制/粘贴代码。该代码只是展示应该如何完成这些事情。
编辑结束:
您可以检查父进程是否是SCM(服务控制管理器)。如果您作为服务运行,则始终是这种情况,而如果作为标准应用程序运行,则绝不会出现这种情况。另外我认为单片机总是有相同的PID。
您可以这样检查:
TProcessList 是这样实现的(同样不包括 THashTable,但任何哈希表都应该没问题):
BEGIN OF EDIT
Since this still seems to be getting some attention I decided to update the answer with missing info and newer windows patches. In any case you should not copy / paste the code. The code is just a showcase on how the things should be done.
END OF EDIT:
You can check if the parent process is SCM (service control manager). If you are running as service this is always the case and never the case if running as standard application. Also I think that SCM has always the same PID.
You can check it like this:
The TProcessList is implemented like this (again THashTable is not included but any hash table should be fine):
如果应用程序对象 (Forms.application) mainform 不是基于表单的应用程序,则它将为 nil。
The application object (Forms.application) mainform will be nil if it is not a forms based application.
如何匹配
GetCurrentProcessId
反对EnumServicesStatusEx
?lpServices
参数指向一个缓冲区,该缓冲区接收ENUM_SERVICE_STATUS_PROCESS
结构。匹配是针对枚举的服务进程 ID 完成的:
ServiceStatusProcess.dwProcessId
。另一种选择是使用
WMI
查询Win32_Service
实例,其中ProcessId=GetCurrentProcessId
。How about matching
GetCurrentProcessId
againstEnumServicesStatusEx
?The
lpServices
parameter points to a buffer that receives an array ofENUM_SERVICE_STATUS_PROCESS
structures.The match is done against the enumerated service process ID:
ServiceStatusProcess.dwProcessId
in that structure.Another option is using
WMI
to query forWin32_Service
instances whereProcessId=GetCurrentProcessId
.我怀疑这
会给你带来预期的结果。
我能想到的就是将 Application 对象作为 TObject 传递给需要进行区分并测试传递对象的类名是否为 a 的
方法您可以知道您的代码是在服务中运行还是在 GUI 中运行。您可能应该重新考虑您的设计,并让调用者传递一个对象来处理您想要(或不想要)显示的消息。 (我认为它是用于显示您想知道的消息/异常)。
I doubt that
will give you the expected results.
All I can think of is to pass an Application object as TObject to the method where you need to make that distinction and test for the passed object's classname being a
That said, there shouldn't be a need for you to know if your code is running in a service or a GUI. You should probably rethink your design and make the caller to pass an object to handle messages you want (or don't want) to show. (I assume it is for showing messages/exceptions you'd like to know).
你可以尝试这样的事情
You can try something like this
单个项目不能(或者我应该说理想情况下不能)同时是服务和表单应用程序,至少如果您能够区分 Forms 应用程序对象和 SvcMgr 应用程序对象 - 您必须为表单代码和服务代码拥有单独的项目。
因此,也许最简单的解决方案是项目条件定义。即在服务项目的项目设置中将“SERVICEAPP”添加到条件定义中。
然后,每当您需要简单地更改行为时:
对于腰带和大括号,您可以在某些启动代码中采用前面描述的测试之一,以确保您的项目已使用定义的预期符号进行编译。
您的Forms应用程序实际上可能作为服务运行,使用允许任何应用程序作为服务运行的原始技术。
当然,在这种情况下,您的应用程序将始终是一个表单应用程序,处理这种情况的最简单方法是使用仅在服务定义中指定的命令行开关对于您的可执行文件,以便您的应用程序可以通过测试该命令行开关来做出适当的响应。
当然,这确实允许您更轻松地测试您的“服务模式”行为,因为您可以使用从 IDE 中定义的开关在“调试”模式下运行您的应用程序,但这不是构建服务应用程序的理想方法,因此我仅凭这一点不会推荐它。这种技术通常仅在您希望将 EXE 作为服务运行但无法修改源代码以将其转换为“正确的”服务时使用。
A single project cannot (or I should say ideally is not) both a service and a forms application, at least not if you are able to distinguish between the Forms Application object and the SvcMgr Application object - you must presumably have separate projects for the forms code and the service code.
So perhaps the easiest solution is a project conditional define. i.e. in your project settings for the service project add "SERVICEAPP" to the Conditional Defines.
Then whenever you need to change behaviour simply:
For belts and braces you might adopt one of the previously described tests within some startup code to ensure that your project has been compiled with the expected symbol defined.
It is possible that your Forms app is actually running as a service, using the crude technique that allows any application to be running as a service.
In that case of course your app will always be a Forms application and the easiest way to handle that situation is to have a command line switch that you specify only in the service definition for your executable so that your app can respond appropriate by testing for that command line switch.
This does allow you to more easily test your "service mode" behaviour of course, since you can run your app in "debug" mode with that switch defined from within the IDE, but it's not an ideal way to build a service application so I would not recommend it on the strength of that alone. It's a technique that is usually only used when you have an EXE that you wish to run as a service but have no way to modify the source code to turn it into a "proper" service.
“Runner”( https://stackoverflow.com/a/1568462 )的答案看起来非常有帮助,但我无法使用因为 TProcessList 和 CreateSnapshot 都没有定义。在Google中搜索“TProcessList CreateSnapshot”只会找到7个页面,包括这一页以及该页的镜像/引用。不存在代码。唉,我的名气太低了,没法给他发评论,问哪里可以找到TProcessList的代码。
另一个问题:在我的计算机(Win7 x64)上,“services.exe”不在“winlogon.exe”内。它位于“wininit.exe”内部。由于它似乎是 Windows 的实现细节,我建议不要查询祖父母。此外,services.exe 不需要是直接父进程,因为进程可以分叉。
所以这是我直接使用 TlHelp32 的版本,解决了所有问题:
该代码即使在没有 MainForm 的应用程序(例如 CLI 应用程序)中也可以工作。
The answer from "Runner" ( https://stackoverflow.com/a/1568462 ) looked very helpful, but I could not use it since neither TProcessList, nor CreateSnapshot is defined. Searching for "TProcessList CreateSnapshot" in Google will just find 7 pages, including this one and mirrors/quotes of this page. No code exists. Alas, my reputation is too low to send him a comment, asking where I can find the code of TProcessList.
Another problem: At my computer (Win7 x64), the "services.exe" is NOT inside "winlogon.exe". It is inside "wininit.exe". Since it seems to be an implementation detail of Windows, I would suggest not querying the grand parent. Also, services.exe does not need to be the direct parent, since processes could be forked.
So this is my version using TlHelp32 directly, solving all the problems:
This code works, also even in applications without MainForm (e.g. CLI apps).
您可以使用 GetStdHandle 方法获取控制台句柄。当应用程序作为 Windows 服务运行时没有输出控制台。如果 GetStdHandle 等于 0 意味着您的应用程序作为 Windows 服务运行。
you can use GetStdHandle method for get out console handle.when applications run as windows service has not output console.if GetStdHandle equals zero means your application run as windows service.
我没有找到可以轻松使用且不需要重新编译并允许使用一个 exe 作为服务和应用程序的简单答案。您可以使用命令行参数(例如“...\myapp.exe –s”)将程序安装为服务,然后从程序中检查它:
I didn't find the simple answer which can be used easily and does not require recompilation and allows using one exe as a service and an application. You can install your program as a service with the command line parameter like “…\myapp.exe –s” and then check it from the program:
您可以根据检查当前进程的会话 ID 进行检查。所有服务均以会话 ID = 0 运行。
You can base the check on checking the session ID of the current process. All services runs with session ID = 0.
我实际上最终检查了 application.showmainform 变量。
skamradt 的 isFormBased 的问题在于,其中一些代码在创建主表单之前被调用。
我正在使用 aldyn-software 的一个名为 SvCom_NTService 的软件库。目的之一是为了避免错误;记录它们或显示消息。我完全同意@Rob;我们的代码应该得到更好的维护,并在函数之外处理这个问题。
另一个目的是针对失败的数据库连接和查询;我的函数中有不同的逻辑来打开查询。如果它是一个服务,那么它将返回 nil,但继续该过程。但是,如果应用程序中发生失败的查询/连接,那么我想显示一条消息并停止应用程序。
I actually ended up checking the application.showmainform variable.
The problem with skamradt's isFormBased is that some of this code is called before the main form is created.
I am using a software library called SvCom_NTService from aldyn-software. One of purposes is for errors; either to log them or show a message. I totally agree with @Rob; our code should be better maintained and handle this outside of the functions.
The other intention is for failed database connections and queries; I have different logic in my functions to open queries. If it is a service then it will return nil but continue the process. But if failed queries/connections occur in an application then I would like to display a messaage and halt the application.
检查您的 Applicatoin 是否是 TServiceApplication 的实例:
Check if your Applicatoin is an instance of TServiceApplication: