确定是否作为 VCL 表单或服务运行

发布于 2024-08-07 05:52:36 字数 88 浏览 12 评论 0原文

我有在服务和 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

凉薄对峙 2024-08-14 05:52:36

开始编辑

由于这似乎仍然引起了一些关注,我决定用缺少的信息和更新的 Windows 补丁来更新答案。在任何情况下,您都不应该复制/粘贴代码。该代码只是展示应该如何完成这些事情。

编辑结束

您可以检查父进程是否是SCM(服务控制管理器)。如果您作为服务运行,则始终是这种情况,而如果作为标准应用程序运行,则绝不会出现这种情况。另外我认为单片机总是有相同的PID。

您可以这样检查:

type
  TAppType = (atUnknown, atDesktop, atService);

var
  AppType: TAppType;

function InternalIsService: Boolean;
var
  PL: TProcessList;
  MyProcessId: DWORD;
  MyProcess: PPROCESSENTRY32;
  ParentProcess: PPROCESSENTRY32;
  GrandParentProcess: PPROCESSENTRY32;
begin
  Result := False;

  PL := TProcessList.Create;
  try
    PL.CreateSnapshot;
    MyProcessId := GetCurrentProcessId;

    MyProcess := PL.FindProcess(MyProcessId);
    if MyProcess <> nil then
    begin
      ParentProcess := PL.FindProcess(MyProcess^.th32ParentProcessID);
      if ParentProcess <> nil then
      begin
        GrandParentProcess := PL.FindProcess(ParentProcess^.th32ParentProcessID);

        if GrandParentProcess <> nil then
        begin
          Result := SameText(string(ParentProcess^.szExeFile), 'services.exe') and
            (SameText(string(GrandParentProcess^.szExeFile), 'winlogon.exe') or
             SameText(string(GrandParentProcess^.szExeFile), 'wininit.exe'));
        end;
      end;
    end;
  finally
    PL.Free;
  end; 
end;

function IsService: Boolean;
begin
  if AppType = atUnknown then
  begin
    try
      if InternalIsService then
        AppType := atService
      else
        AppType := atDesktop;
    except
      AppType := atService;
    end;
  end;

  Result := AppType = atService;
end;

initialization
  AppType := atUnknown;

TProcessList 是这样实现的(同样不包括 THashTable,但任何哈希表都应该没问题):

type
  TProcessEntryList = class(TList)
  private
    function Get(Index: Integer): PPROCESSENTRY32;
    procedure Put(Index: Integer; const Value: PPROCESSENTRY32);
  public
    property Items[Index: Integer]: PPROCESSENTRY32 read Get write Put; default;
    function Add(const Entry: TProcessEntry32): Integer; reintroduce;
    procedure Clear; override;
  end;

  TProcessList = class
  private
    ProcessIdHashTable: THashTable;
    ProcessEntryList: TProcessEntryList;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure CreateSnapshot;
    function FindProcess(const ProcessId: DWORD): PPROCESSENTRY32;
  end;

implementation

{ TProcessEntryList }

procedure TProcessEntryList.Clear;
var
  i: Integer;
begin
  i := 0;
  while i < Count do
  begin
    FreeMem(Items[i]);
    Inc(i);
  end;

  inherited;
end;

procedure TProcessEntryList.Put(Index: Integer; const Value: PPROCESSENTRY32);
var
  Item: Pointer;
begin
  Item := inherited Get(Index);
  CopyMemory(Item, Value, SizeOf(tagPROCESSENTRY32));
end;

function TProcessEntryList.Get(Index: Integer): PPROCESSENTRY32;
begin
  Result := PPROCESSENTRY32(inherited Get(Index));
end;

function TProcessEntryList.Add(const Entry: TProcessEntry32): Integer;
var
  EntryCopy: PPROCESSENTRY32;
begin
  GetMem(EntryCopy, SizeOf(tagPROCESSENTRY32));
  CopyMemory(EntryCopy, @Entry, SizeOf(tagPROCESSENTRY32));

  Result := inherited Add(EntryCopy);  
end;

{ TProcessList }

constructor TProcessList.Create;
begin
  inherited;

  ProcessEntryList := TProcessEntryList.Create;
  ProcessIdHashTable := THashTable.Create;
end;

destructor TProcessList.Destroy;
begin
  FreeAndNil(ProcessIdHashTable);
  FreeAndNil(ProcessEntryList);

  inherited;
end;

function TProcessList.FindProcess(const ProcessId: DWORD): PPROCESSENTRY32;
var
  ItemIndex: Integer;
begin
  Result := nil;
  if not ProcessIdHashTable.ContainsKey(IntToStr(ProcessId)) then
    Exit;

  ItemIndex := Integer(ProcessIdHashTable.Item[IntToStr(ProcessId)]);
  Result := ProcessEntryList.Items[ItemIndex];
end;

procedure TProcessList.CreateSnapshot;
var
  SnapShot: THandle;
  ProcessEntry: TProcessEntry32;
  ItemIndex: Integer;
begin
  SnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if SnapShot <> 0 then
  try
    ProcessEntry.dwSize := SizeOf(ProcessEntry);
    if Process32First(SnapShot, ProcessEntry) then
    repeat
      ItemIndex := ProcessEntryList.Add(ProcessEntry);
      ProcessIdHashTable.Add(IntToStr(ProcessEntry.th32ProcessID), TObject(ItemIndex));
    until not Process32Next(SnapShot, ProcessEntry);
  finally
    CloseHandle(SnapShot);
  end;
end;

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:

type
  TAppType = (atUnknown, atDesktop, atService);

var
  AppType: TAppType;

function InternalIsService: Boolean;
var
  PL: TProcessList;
  MyProcessId: DWORD;
  MyProcess: PPROCESSENTRY32;
  ParentProcess: PPROCESSENTRY32;
  GrandParentProcess: PPROCESSENTRY32;
begin
  Result := False;

  PL := TProcessList.Create;
  try
    PL.CreateSnapshot;
    MyProcessId := GetCurrentProcessId;

    MyProcess := PL.FindProcess(MyProcessId);
    if MyProcess <> nil then
    begin
      ParentProcess := PL.FindProcess(MyProcess^.th32ParentProcessID);
      if ParentProcess <> nil then
      begin
        GrandParentProcess := PL.FindProcess(ParentProcess^.th32ParentProcessID);

        if GrandParentProcess <> nil then
        begin
          Result := SameText(string(ParentProcess^.szExeFile), 'services.exe') and
            (SameText(string(GrandParentProcess^.szExeFile), 'winlogon.exe') or
             SameText(string(GrandParentProcess^.szExeFile), 'wininit.exe'));
        end;
      end;
    end;
  finally
    PL.Free;
  end; 
end;

function IsService: Boolean;
begin
  if AppType = atUnknown then
  begin
    try
      if InternalIsService then
        AppType := atService
      else
        AppType := atDesktop;
    except
      AppType := atService;
    end;
  end;

  Result := AppType = atService;
end;

initialization
  AppType := atUnknown;

The TProcessList is implemented like this (again THashTable is not included but any hash table should be fine):

type
  TProcessEntryList = class(TList)
  private
    function Get(Index: Integer): PPROCESSENTRY32;
    procedure Put(Index: Integer; const Value: PPROCESSENTRY32);
  public
    property Items[Index: Integer]: PPROCESSENTRY32 read Get write Put; default;
    function Add(const Entry: TProcessEntry32): Integer; reintroduce;
    procedure Clear; override;
  end;

  TProcessList = class
  private
    ProcessIdHashTable: THashTable;
    ProcessEntryList: TProcessEntryList;
  public
    constructor Create; reintroduce;
    destructor Destroy; override;
    procedure CreateSnapshot;
    function FindProcess(const ProcessId: DWORD): PPROCESSENTRY32;
  end;

implementation

{ TProcessEntryList }

procedure TProcessEntryList.Clear;
var
  i: Integer;
begin
  i := 0;
  while i < Count do
  begin
    FreeMem(Items[i]);
    Inc(i);
  end;

  inherited;
end;

procedure TProcessEntryList.Put(Index: Integer; const Value: PPROCESSENTRY32);
var
  Item: Pointer;
begin
  Item := inherited Get(Index);
  CopyMemory(Item, Value, SizeOf(tagPROCESSENTRY32));
end;

function TProcessEntryList.Get(Index: Integer): PPROCESSENTRY32;
begin
  Result := PPROCESSENTRY32(inherited Get(Index));
end;

function TProcessEntryList.Add(const Entry: TProcessEntry32): Integer;
var
  EntryCopy: PPROCESSENTRY32;
begin
  GetMem(EntryCopy, SizeOf(tagPROCESSENTRY32));
  CopyMemory(EntryCopy, @Entry, SizeOf(tagPROCESSENTRY32));

  Result := inherited Add(EntryCopy);  
end;

{ TProcessList }

constructor TProcessList.Create;
begin
  inherited;

  ProcessEntryList := TProcessEntryList.Create;
  ProcessIdHashTable := THashTable.Create;
end;

destructor TProcessList.Destroy;
begin
  FreeAndNil(ProcessIdHashTable);
  FreeAndNil(ProcessEntryList);

  inherited;
end;

function TProcessList.FindProcess(const ProcessId: DWORD): PPROCESSENTRY32;
var
  ItemIndex: Integer;
begin
  Result := nil;
  if not ProcessIdHashTable.ContainsKey(IntToStr(ProcessId)) then
    Exit;

  ItemIndex := Integer(ProcessIdHashTable.Item[IntToStr(ProcessId)]);
  Result := ProcessEntryList.Items[ItemIndex];
end;

procedure TProcessList.CreateSnapshot;
var
  SnapShot: THandle;
  ProcessEntry: TProcessEntry32;
  ItemIndex: Integer;
begin
  SnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if SnapShot <> 0 then
  try
    ProcessEntry.dwSize := SizeOf(ProcessEntry);
    if Process32First(SnapShot, ProcessEntry) then
    repeat
      ItemIndex := ProcessEntryList.Add(ProcessEntry);
      ProcessIdHashTable.Add(IntToStr(ProcessEntry.th32ProcessID), TObject(ItemIndex));
    until not Process32Next(SnapShot, ProcessEntry);
  finally
    CloseHandle(SnapShot);
  end;
end;
永不分离 2024-08-14 05:52:36

如果应用程序对象 (Forms.application) mainform 不是基于表单的应用程序,则它将为 nil。

uses
  Forms, ... ;

function IsFormBased : boolean;
begin
  Result := Assigned(Forms.Application.MainForm);
end;

The application object (Forms.application) mainform will be nil if it is not a forms based application.

uses
  Forms, ... ;

function IsFormBased : boolean;
begin
  Result := Assigned(Forms.Application.MainForm);
end;
忘羡 2024-08-14 05:52:36

如何匹配 GetCurrentProcessId 反对 EnumServicesStatusEx
lpServices 参数指向一个缓冲区,该缓冲区接收 ENUM_SERVICE_STATUS_PROCESS 结构。
匹配是针对枚举的服务进程 ID 完成的: ServiceStatusProcess.dwProcessId

另一种选择是使用 WMI 查询 Win32_Service 实例,其中 ProcessId=GetCurrentProcessId

How about matching GetCurrentProcessId against EnumServicesStatusEx?
The lpServices parameter points to a buffer that receives an array of ENUM_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 for Win32_Service instances where ProcessId=GetCurrentProcessId.

牵你手 2024-08-14 05:52:36

我怀疑这

System.IsConsole
System.IsLibrary

会给你带来预期的结果。

我能想到的就是将 Application 对象作为 TObject 传递给需要进行区分并测试传递对象的类名是否为 a 的

TServiceApplication 
or
TApplication

方法您可以知道您的代码是在服务中运行还是在 GUI 中运行。您可能应该重新考虑您的设计,并让调用者传递一个对象来处理您想要(或不想要)显示的消息。 (我认为它是用于显示您想知道的消息/异常)。

I doubt that

System.IsConsole
System.IsLibrary

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

TServiceApplication 
or
TApplication

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).

双马尾 2024-08-14 05:52:36

你可以尝试这样的事情

Function IsMyformInsideaWindowsService(aForm:TObject) :Boolean;
Begin
   Result:=aForm.ClassParent.ClassName='TService';  //When a form is running under a service the Class Parent is a TService
End;

You can try something like this

Function IsMyformInsideaWindowsService(aForm:TObject) :Boolean;
Begin
   Result:=aForm.ClassParent.ClassName='TService';  //When a form is running under a service the Class Parent is a TService
End;
潦草背影 2024-08-14 05:52:36

单个项目不能(或者我应该说理想情况下不能)同时是服务和表单应用程序,至少如果您能够区分 Forms 应用程序对象和 SvcMgr 应用程序对象 - 您必须为表单代码和服务代码拥有单独的项目。

因此,也许最简单的解决方案是项目条件定义。即在服务项目的项目设置中将“SERVICEAPP”添加到条件定义中。

然后,每当您需要简单地更改行为时:

{$ifdef SERVICEAPP}
{$else}
{$endif}

对于腰带和大括号,您可以在某些启动代码中采用前面描述的测试之一,以确保您的项目已使用定义的预期符号进行编译。

program ... ;

 :

begin
{$ifdef SERVICEAPP}
  // test for service app - ASSERT if not
{$else}
  // test for forms app - ASSERT if not
{$endif}
  :
end.

您的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:

{$ifdef SERVICEAPP}
{$else}
{$endif}

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.

program ... ;

 :

begin
{$ifdef SERVICEAPP}
  // test for service app - ASSERT if not
{$else}
  // test for forms app - ASSERT if not
{$endif}
  :
end.

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.

长不大的小祸害 2024-08-14 05:52:36

“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 的版本,解决了所有问题:

uses
  Classes, TlHelp32;

function IsRunningAsService: boolean;

  function FindProcess(FSnapshotHandle: THandle; PID: DWORD; var lppe: TProcessEntry32): boolean;
  var
    ContinueLoop: BOOL;
  begin
    ContinueLoop := Process32First(FSnapshotHandle, lppe);
    while Integer(ContinueLoop) <> 0 do
    begin
      if lppe.th32ProcessID = PID then
      begin
        result := true;
        Exit;
      end;
      ContinueLoop := Process32Next(FSnapshotHandle, lppe);
    end;
    result := false;
  end;

var
  CurProcessId: DWORD;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
  ExeName, PrevExeName: string;
  DeadlockProtection: TList<Integer>;
begin
  Result := false;

  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  DeadlockProtection := TList<Integer>.Create;
  try
    CurProcessId := GetCurrentProcessId;
    FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
    ExeName := '';
    while FindProcess(FSnapshotHandle, CurProcessId, FProcessEntry32) do
    begin
      if DeadlockProtection.IndexOf(FProcessEntry32.th32ProcessID) > -1 then break;
      DeadlockProtection.Add(FProcessEntry32.th32ProcessID);

      PrevExeName := ExeName;
      ExeName     := FProcessEntry32.szExeFile;

      (*
      Result := SameText(PrevExeName, 'services.exe') and // Parent
                SameText(ExeName,     'winlogon.exe');    // Grandparent
      *)

      Result := SameText(ExeName, 'services.exe'); // Parent

      if Result then Exit;

      CurProcessId := FProcessEntry32.th32ParentProcessID;
    end;
  finally
    CloseHandle(FSnapshotHandle);
    DeadlockProtection.Free;
  end;
end;

该代码即使在没有 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:

uses
  Classes, TlHelp32;

function IsRunningAsService: boolean;

  function FindProcess(FSnapshotHandle: THandle; PID: DWORD; var lppe: TProcessEntry32): boolean;
  var
    ContinueLoop: BOOL;
  begin
    ContinueLoop := Process32First(FSnapshotHandle, lppe);
    while Integer(ContinueLoop) <> 0 do
    begin
      if lppe.th32ProcessID = PID then
      begin
        result := true;
        Exit;
      end;
      ContinueLoop := Process32Next(FSnapshotHandle, lppe);
    end;
    result := false;
  end;

var
  CurProcessId: DWORD;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
  ExeName, PrevExeName: string;
  DeadlockProtection: TList<Integer>;
begin
  Result := false;

  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  DeadlockProtection := TList<Integer>.Create;
  try
    CurProcessId := GetCurrentProcessId;
    FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
    ExeName := '';
    while FindProcess(FSnapshotHandle, CurProcessId, FProcessEntry32) do
    begin
      if DeadlockProtection.IndexOf(FProcessEntry32.th32ProcessID) > -1 then break;
      DeadlockProtection.Add(FProcessEntry32.th32ProcessID);

      PrevExeName := ExeName;
      ExeName     := FProcessEntry32.szExeFile;

      (*
      Result := SameText(PrevExeName, 'services.exe') and // Parent
                SameText(ExeName,     'winlogon.exe');    // Grandparent
      *)

      Result := SameText(ExeName, 'services.exe'); // Parent

      if Result then Exit;

      CurProcessId := FProcessEntry32.th32ParentProcessID;
    end;
  finally
    CloseHandle(FSnapshotHandle);
    DeadlockProtection.Free;
  end;
end;

This code works, also even in applications without MainForm (e.g. CLI apps).

温柔戏命师 2024-08-14 05:52:36

您可以使用 GetStdHandle 方法获取控制台句柄。当应用程序作为 Windows 服务运行时没有输出控制台。如果 GetStdHandle 等于 0 意味着您的应用程序作为 Windows 服务运行。

{$APPTYPE CONSOLE} // important

uses
   uServerForm in 'uServerForm.pas' {ServerForm},
 uWinService in 'uWinService.pas' {mofidWinServer: TService},

  Windows,
  System.SysUtils,
  WinSvc,
  SvcMgr,
  Forms,etc;
function RunAsWinService: Boolean;
var
  H: THandle;
begin
  if FindCmdLineSwitch('install', ['-', '/'], True) then
    Exit(True);
  if FindCmdLineSwitch('uninstall', ['-', '/'], True) then
    Exit(True);
  H := GetStdHandle(STD_OUTPUT_HANDLE);
  Result := H = 0;
end;


begin       
  if RunAsWinService then
  begin

    SvcMgr.Application.Initialize;
    SvcMgr.Application.CreateForm(TmofidWinServer, mofidWinServer);
    SvcMgr.Application.Run;
  end
  else
  begin
    Forms.Application.Initialize;
    Forms.Application.CreateForm(TServerForm, ServerForm);
    Forms.Application.Run;
  end;
end.

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.

{$APPTYPE CONSOLE} // important

uses
   uServerForm in 'uServerForm.pas' {ServerForm},
 uWinService in 'uWinService.pas' {mofidWinServer: TService},

  Windows,
  System.SysUtils,
  WinSvc,
  SvcMgr,
  Forms,etc;
function RunAsWinService: Boolean;
var
  H: THandle;
begin
  if FindCmdLineSwitch('install', ['-', '/'], True) then
    Exit(True);
  if FindCmdLineSwitch('uninstall', ['-', '/'], True) then
    Exit(True);
  H := GetStdHandle(STD_OUTPUT_HANDLE);
  Result := H = 0;
end;


begin       
  if RunAsWinService then
  begin

    SvcMgr.Application.Initialize;
    SvcMgr.Application.CreateForm(TmofidWinServer, mofidWinServer);
    SvcMgr.Application.Run;
  end
  else
  begin
    Forms.Application.Initialize;
    Forms.Application.CreateForm(TServerForm, ServerForm);
    Forms.Application.Run;
  end;
end.
眼睛会笑 2024-08-14 05:52:36

我没有找到可以轻松使用且不需要重新编译并允许使用一个 exe 作为服务和应用程序的简单答案。您可以使用命令行参数(例如“...\myapp.exe –s”)将程序安装为服务,然后从程序中检查它:

如果 ParamStr(ParamCount) = '-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:

if ParamStr(ParamCount) = '-s' then

暖风昔人 2024-08-14 05:52:36

您可以根据检查当前进程的会话 ID 进行检查。所有服务均以会话 ID = 0 运行。

function IsServiceProcess: Boolean;
var
  LSessionID, LSize: Cardinal;
  LToken: THandle;
begin
  Result := False;
  LSize := 0;
  if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, LToken) then
    Exit;

  try
    if not GetTokenInformation(LToken, TokenSessionId, @LSessionID, SizeOf(LSessionID), LSize) then
      Exit;

    if LSize = 0 then
      Exit;

    Result := LSessionID = 0;
  finally
    CloseHandle(LToken);
  end;
end;

You can base the check on checking the session ID of the current process. All services runs with session ID = 0.

function IsServiceProcess: Boolean;
var
  LSessionID, LSize: Cardinal;
  LToken: THandle;
begin
  Result := False;
  LSize := 0;
  if not OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, LToken) then
    Exit;

  try
    if not GetTokenInformation(LToken, TokenSessionId, @LSessionID, SizeOf(LSessionID), LSize) then
      Exit;

    if LSize = 0 then
      Exit;

    Result := LSessionID = 0;
  finally
    CloseHandle(LToken);
  end;
end;
掀纱窥君容 2024-08-14 05:52:36

我实际上最终检查了 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.

冷默言语 2024-08-14 05:52:36

检查您的 Applicatoin 是否是 TServiceApplication 的实例:

IsServiceApp := Application is TServiceApplication;

Check if your Applicatoin is an instance of TServiceApplication:

IsServiceApp := Application is TServiceApplication;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文