16 Stimmen

Bestimmen Sie, ob als VCL-Formulare oder Service ausgeführt wird

Ich habe Code, der sowohl in Diensten als auch in VCL-Formanwendungen (Win32-Anwendungen) verwendet wird. Wie kann ich feststellen, ob die zugrunde liegende Anwendung als NT-Dienst oder als Anwendung ausgeführt wird?

Danke.

11voto

Runner Punkte 5953

ANFANG DER BEARBEITUNG

Da dies immer noch Aufmerksamkeit zu bekommen scheint, habe ich beschlossen, die Antwort mit fehlenden Informationen und neueren Windows-Patches zu aktualisieren. Auf jeden Fall sollte der Code nicht einfach kopiert und eingefügt werden. Der Code dient nur als Beispiel, wie die Dinge gemacht werden sollten.

ENDE DER BEARBEITUNG:

Sie können überprüfen, ob der übergeordnete Prozess der SCM (Service Control Manager) ist. Wenn Sie als Dienst ausgeführt werden, ist dies immer der Fall und niemals der Fall, wenn Sie als Standardanwendung ausgeführt werden. Außerdem denke ich, dass SCM immer die gleiche PID hat.

Sie können dies wie folgt überprüfen:

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;

Die TProcessList ist wie folgt implementiert (nochmals: THashTable ist nicht enthalten, aber eine beliebige Hashtabelle sollte geeignet sein):

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;
... (Rest of the translation remains the same) ...

10voto

skamradt Punkte 15128

Das Anwendungsobjekt (Forms.application) mainform ist nil, wenn es sich nicht um eine Formular-basierte Anwendung handelt.

uses
  Forms, ... ;

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

6voto

kobik Punkte 20720

Wie wäre es, GetCurrentProcessId mit EnumServicesStatusEx abzugleichen?
Der Parameter lpServices zeigt auf einen Puffer, der ein Array von ENUM_SERVICE_STATUS_PROCESS-Strukturen empfängt. Der Abgleich erfolgt anhand der aufgelisteten Dienstprozess-ID: ServiceStatusProcess.dwProcessId in dieser Struktur.

Eine weitere Option ist die Verwendung von WMI zur Abfrage von Win32_Service-Instanzen, bei denen ProcessId=GetCurrentProcessId.

5voto

Lieven Keersmaekers Punkte 55277

Ich bezweifle das

System.IsConsole
System.IsLibrary

wird Ihnen die erwarteten Ergebnisse liefern.

Alles, woran ich denken kann, ist, ein Anwendungs-Objekt als TObject zu übergeben an die Methode, wo Sie diese Unterscheidung treffen müssen und prüfen, ob der Klassenname des übergebenen Objekts ist ein

TServiceApplication 
oder
TApplication

Das gesagt, es sollte kein Bedarf für Sie bestehen zu wissen, ob Ihr Code in einem Dienst oder einer GUI läuft. Sie sollten wahrscheinlich Ihr Design überdenken und den Aufrufer dazu bringen, ein Objekt zu übergeben, um mit den Nachrichten umzugehen, die Sie zeigen möchten (oder nicht zeigen möchten). (Ich nehme an, es geht darum, Nachrichten/Ausnahmen anzuzeigen, die Sie wissen möchten).

4voto

RRUZ Punkte 132753

Sie können etwas Ähnliches versuchen

Funktion IsMyformInsideaWindowsService(aForm: TObject): Boolean;
Begin
   Result := aForm.ClassParent.ClassName = 'TService';  // Wenn ein Formular unter einem Dienst läuft, ist der Klassenelternteil ein TService
End;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X