Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Klasse zum Beenden eines Prozesses (https://www.delphipraxis.net/156073-klasse-zum-beenden-eines-prozesses.html)

Luckie 18. Nov 2010 23:24

Klasse zum Beenden eines Prozesses
 
Ich habe hier eine Klasse zum Beenden eine Prozesses, die ich mal zur Diskussion stellen wollte, ob ich da was vergessen habe:
Delphi-Quellcode:
// Klasse zum Benden eines Processes mittels TerminateProcess
// Class for terminating a process via TerminateProcess
// Michael Puff [http://www.michael-puff.de]

unit MpuKillProcessCls;

interface

uses
  Windows, SysUtils, TlHelp32;

type
  TKillProcess = class(TObject)
  private
    FProcessFile: string;
    FProcessID: Integer;
    FTimeOut: Cardinal;
    procedure GetProcessID;
  public
    property ProcessFile: string read FProcessFile write FProcessFile;
    property TimeOutMSecs: Cardinal read FTimeOut write FTimeOut;
    constructor Create(ProcessFile: string);
    procedure Kill;
  end;

implementation

constructor TKillProcess.Create(ProcessFile: string);
begin
  FProcessFile := ProcessFile;
  FTimeOut := 0;
  GetProcessID;
end;

procedure TKillProcess.GetProcessID;
var
  ProcessSnapShot: THandle;
  pe32: TProcessEntry32;
begin
  ProcessSnapShot := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  if ProcessSnapShot <> INVALID_HANDLE_VALUE then
  begin
    pe32.dwSize := SizeOf(ProcessEntry32);
    if Process32First(ProcessSnapShot, pe32) = true then
    begin
      while Process32Next(ProcessSnapShot, pe32) = true do
      begin
        if pos(LowerCase(FProcessFile), LowerCase(pe32.szExeFile)) <> 0 then
          FProcessID := pe32.th32ProcessID;
      end;
    end
    else
    begin
      RaiseLastOSError;
    end;
  end
  else
  begin
    RaiseLastOSError;
  end;
  CloseHandle(ProcessSnapShot);
  if FProcessID = 0 then
    raise Exception.Create('Process not found');
end;

procedure TKillProcess.Kill;
var
  ProcessHandle: Cardinal;
  WFSOReturnCode: DWORD;
begin
  ProcessHandle := OpenProcess(SYNCHRONIZE or PROCESS_TERMINATE, False, FProcessID);
  if ProcessHandle <> 0 then
  begin
    if TerminateProcess(ProcessHandle, 0) then
    begin
      WFSOReturnCode := WaitForSingleObject(ProcessHandle, FTimeOut);
      case WFSOReturnCode of
        WAIT_TIMEOUT:
          begin
            if FTimeOut > 0 then
              raise Exception.Create('Timeout');
          end;
        WAIT_FAILED:
          begin
            RaiseLastOSError;
          end;
      end;
    end
    else
    begin
      RaiseLastOSError;
    end;
  end
  else
  begin
    RaiseLastOSError;
  end;
end;

end.
Was mir noch etwas Kopfzerbrechen breitet ist, dass bei einem TimeOut von 0 Millisekunden WAIT_TIMEOUT aufgerufen wird und der Prozess trozdem beendet wird. Damit hängt auch zusammen, dass ich nicht recht weiß, wie ich zurückgeben kann, dass der Prozess erfolgreich beendet wurde. Möglich wäre eine Rückgabe von True bzw. Falls oder das auslösen eines entsprechenden Ereignisses.

s.h.a.r.k 18. Nov 2010 23:45

AW: Klasse zum Beenden eines Prozesses
 
Ich habe so etwas noch nie gebraucht, aber könnte der Benutzer des Codes evtl. auch andere Daten zum beenden eines Programms haben? Somit wäre ein weiterer Konstruktor zu bauen.

Es gibt doch zudem das Problem dass wenn eine Anwendung gleich zwei mal geöffnet ist: welche Instanz wird beendet? (habe mir den Code nicht ganz genau angeschaut)

Ebenso könntest du ein ForceKill einbauen, welches nach dem Timeout das Programm nach Möglichkeit wirklich schließt?! Geht sowas überhaupt?

Luckie 18. Nov 2010 23:49

AW: Klasse zum Beenden eines Prozesses
 
Es wird der Prozess beendet, der zu erst in der Liste gefunden wird.

Welche Daten meinst du? Den Fenstertitel? Dann wäre es aber ein Fenster-Schließer. ;)

Wenn der Prozess auch nach dem TimeOut nicht geschlossen werden konnte, dann wird es danach mit hoher Wahrscheinlichkeit auch nicht funktionieren.

s.h.a.r.k 18. Nov 2010 23:59

AW: Klasse zum Beenden eines Prozesses
 
Von diesem Schließen-Gedöns selbst habe ich keine Ahnung ;) Habe da nur an das Herunterfahren von Windows gedacht. Das gibt jeder Anwendung ja auch x Sekunden Zeit. Wenn die Anwendungen nicht "fähig" ist, sich in der Zeit zu beenden, dann wird es eben mit Gewalt beendet -- bist du nicht willig.... :mrgreen: Daher auch die Idee mit dem ForceKill.

Das mit dem ersten Programm in der Liste ist halt so eine Sache. Vielleicht ist es genau das Programm welches man nicht schließen will, wenn zwei offen sind ;) Vielleicht wäre hier ein gewissen Schalte gut, wobei ich nicht weiß, wie man das umgehen sollte. Ich denke, dass es zunächst echt dabei belassen solltest. Außer der Benutzer hat mehr Informationen, wie die ProcessID z.B.

Vielleicht wäre ein Filter auch hilfreich: Schließe Prozess...
  • mit Exe-Name xyz. (das hast du ja schon)
  • mit ProzessID xyz.
  • Prozess, der von Benutzer xyz gestartet wurde.
  • Schau mal die Info "Befehlszeile" im Taskmanager an (schaue bei mir gerade unter Windows 7)

Ist das Schließen eines Prozesses auf einem anderen Rechner irgendwie möglich? So à la Fernwartung.

-- EDIT
- Wie sieht es mit einer Lizenz für den Quellcode aus?
- und was mir gerade aufgefallen ist: TProcessEntry32 <- heißt das, dass nur 32-bit Programme erkannt werden? Oder klappt das auch mit 64-bit Programmen?!

Luckie 19. Nov 2010 00:07

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von s.h.a.r.k (Beitrag 1062463)
Von diesem Schließen-Gedöns selbst habe ich keine Ahnung ;) Habe da nur an das Herunterfahren von Windows gedacht. Das gibt jeder Anwendung ja auch x Sekunden Zeit. Wenn die Anwendungen nicht "fähig" ist, sich in der Zeit zu beenden, dann wird es eben mit Gewalt beendet -- bist du nicht willig....

Ich denke eher, da macht Windows einfach mit dem Runterfahren weiter.

Zitat:

Das mit dem ersten Programm in der Liste ist halt so eine Sache. Vielleicht ist es genau das Programm welches man nicht schließen will, wenn zwei offen sind ;)
Wer diese Klasse benutzt sollte schon wissen, was er tut und sich der Unlänglichkeiten bewusst sein.

Zitat:

Außer der Benutzer hat mehr Informationen, wie die ProcessID z.B.
Das wäre eine Option, dass man den Prozess über die ID identifiziert.

Zitat:

Ist das Schließen eines Prozesses auf einem anderen Rechner irgendwie möglich? So à la Fernwartung.
Das könnte man eventuell über WMI erreichen. Aber mit WMI stehe ich auf Kriegsfuß.

Zitat:

- Wie sieht es mit einer Lizenz für den Quellcode aus?
OpenSource.
Zitat:

- und was mir gerade aufgefallen ist: TProcessEntry32 <- heißt das, dass nur 32-bit Programme erkannt werden? Oder klappt das auch mit 64-bit Programmen?!
Dazu kann ich leider nichts sagen, weil ich mich dies bezüglich noch nicht eingearbeitet habe.

himitsu 19. Nov 2010 01:04

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Delphi-Quellcode:
if ... = true then

Das hätt ich jetzt nicht von dir erwartet.

Zitat:

Delphi-Quellcode:
if pos(LowerCase(FProcessFile), LowerCase(pe32.szExeFile)) <> 0 then

Hier wäre es wohl besser zu prüfen, ob sich der Name am Ende des Strings befindet,
nicht daß jemand eine Datei sucht und zufällig ein Verzeichnis genauso heißt.
Und ob es sich auch um den kompletten Namen handelt,
nicht daß man nach "ample.exe" sucht und die "example.exe" schließt.

eventuell so? (k.A. ob's richtig ist)
Delphi-Quellcode:
s := LowerCase(FProcessFile);
s2 := '\' + LowerCase(pe32.szExeFile);
if (s = '') or (s[1] <> '\') then Insert('\', s, 1);
if Pos(s, s2) = Length(s2) - Length(s1) + 1 then ...
oder
Delphi-Quellcode:
s := LowerCase(FProcessFile);
s2 := '\' + LowerCase(pe32.szExeFile);
if (s = '') or (s[1] <> '\') then Insert('\', s, 1);
Delete(s2, 1, Length(s2) - Length(s1));
if s = s2 then ...


Zitat:

Vielleicht wäre ein Filter auch hilfreich: Schließe Prozess...
- von dem ein Fenster den Text xyz enhält

Luckie 19. Nov 2010 06:13

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von himitsu (Beitrag 1062465)
Zitat:

Delphi-Quellcode:
if ... = true then

Das hätt ich jetzt nicht von dir erwartet.

Autsch, das passiert, wenn man alten Code per Copy and paste übernimmt.

Zitat:

Hier wäre es wohl besser zu prüfen, ob sich der Name am Ende des Strings befindet,
nicht daß jemand eine Datei sucht und zufällig ein Verzeichnis genauso heißt.
Eine Datei oder Verzeichnis ist kein Prozess und taucht nict in der Prozessliste auf. Was soll da passieren, außer, dass der Prozess nicht gefunden wird?
Zitat:

Und ob es sich auch um den kompletten Namen handelt,
nicht daß man nach "ample.exe" sucht und die "example.exe" schließt.
Das wäre wohl sinnvoll.

Zitat:

eventuell so? (k.A. ob's richtig ist)
Delphi-Quellcode:
s := LowerCase(FProcessFile);
s2 := '\' + LowerCase(pe32.szExeFile);
if (s = '') or (s[1] <> '\') then Insert('\', s, 1);
if Pos(s, s2) = Length(s2) - Length(s1) + 1 then ...
oder
Delphi-Quellcode:
s := LowerCase(FProcessFile);
s2 := '\' + LowerCase(pe32.szExeFile);
if (s = '') or (s[1] <> '\') then Insert('\', s, 1);
Delete(s2, 1, Length(s2) - Length(s1));
if s = s2 then ...

Warum so umständlich?
Delphi-Quellcode:
if LowerCase(FProcessFile) = LowerCase(pe32.szExeFile)...

alzaimar 19. Nov 2010 06:49

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062464)
Zitat:

Zitat von s.h.a.r.k (Beitrag 1062463)
...Habe da nur an das Herunterfahren von Windows gedacht. Das gibt jeder Anwendung ja auch x Sekunden Zeit. Wenn die Anwendungen nicht "fähig" ist, sich in der Zeit zu beenden, dann wird es eben mit Gewalt beendet -- bist du nicht willig....

Ich denke eher, da macht Windows einfach mit dem Runterfahren weiter.

Nee, nee. Windows hat da schon einen Trick, einen hängenden Prozess zu killen. Ich habe hier eine Anwendung, die sich manchmal aufhängt und dann nicht mehr aus dem Speicher verschwindet. Über den Taskmanager kann ich sie *nicht* abschießen.

So kann ich einen hängenden Prozess immer killen:
1. NotePad.EXE öffnen, irgendwas eintippen,
2. User ausloggen.
3. NotePad.EXE fragt mich, ob ich die ungesicherten Änderungen speichern will,
4. kurz warten, denn
5. der hängende Prozess verschwindet GARANTIERT.
6. Dann breche ich den ganzen Logout-Vorgang ab, indem ich
7. bei der Notpad-Frage auf 'Abbrechen' klicke.

Funktioniert immer! Natürlich werden auch diverse andere Programme beendet, auch Dienste. Das ist also die letzte aller Möglichkeiten.

Aber wie macht Windows das?

mleyen 19. Nov 2010 07:03

AW: Klasse zum Beenden eines Prozesses
 
Ein setter für
Delphi-Quellcode:
ProcessFile
ist nötig, da die PID nur im Konstruktor gehohlt wird. Nicht das falsche Prozesse ungewollt beendet werden.

Luckie 19. Nov 2010 07:07

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von mleyen (Beitrag 1062482)
Ein setter für
Delphi-Quellcode:
ProcessFile
ist nötig, da die PID nur im Konstruktor gehohlt wird. Nicht das falsche Prozesse ungewollt beendet werden.

Da muss ich um eine nähere Erklärung bitten. Man könnte aber einen Setter einführen, um die Klasse bei mehreren Gebrauch hintereinander nicht immer neu erstellt werden muss. Aber macht man das auch?

s.h.a.r.k 19. Nov 2010 07:08

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von alzaimar (Beitrag 1062478)
Funktioniert immer! Natürlich werden auch diverse andere Programme beendet, auch Dienste. Das ist also die letzte aller Möglichkeiten.

Aber wie macht Windows das?

Ich weiß dies bzgl, dass Windows ein Patent auf das Herunterfahren des Rechners angemeldet hat. Ich suche gerade den Eintrag, den ich gelesen habe. Da stand nämlich etwas mehr drin, vielleicht auch genau das.

--EDIT
Nein, war nur eine darstellende Grafik...

mleyen 19. Nov 2010 07:17

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062483)
Da muss ich um eine nähere Erklärung bitten.

Nunja, also das write in:
Delphi-Quellcode:
public
  property ProcessFile: string read FProcessFile write FProcessFile;
bringt momentan nichts, außer evtl ungewollte Verhaltensweisen.

Luckie 19. Nov 2010 07:21

AW: Klasse zum Beenden eines Prozesses
 
Oh. Danke für den Hinweis.

himitsu 19. Nov 2010 09:12

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062473)
Eine Datei oder Verzeichnis ist kein Prozess und taucht nict in der Prozessliste auf. Was soll da passieren, außer, dass der Prozess nicht gefunden wird?

Hab ich das behauptet?

Delphi-Quellcode:
C:\Programme\Example.exetus\Demo.exe
und jetzt lass mal dein
Delphi-Quellcode:
POS('ample.exe'
darauf los. :stupid:

Zitat:

Zitat von Luckie (Beitrag 1062473)
Warum so umständlich?
Delphi-Quellcode:
if LowerCase(FProcessFile) = LowerCase(pe32.szExeFile)...

Könnt ich dich auch fragen :roll:
Delphi-Quellcode:
if AnsiSameText(FProcessFile, pe32.szExeFile) then...
.
PS: LowerCase = nur A bis Z wird übersetzt
AnsiLowerCase = alle Zeichen (ab D2009 ist AnsiLowerCase standardmäßig Unicode)

Und warum so lang ... so könnte man auch nach
Delphi-Quellcode:
Demo.exe
,
Delphi-Quellcode:
Example.exetus\Demo.exe
oder
Delphi-Quellcode:
C:\Programme\Example.exetus\Demo.exe
suchen. :angel2:

(und wehe einer sagt was über meine exetus-Wortschöpfung :warn: )

mirage228 19. Nov 2010 10:13

AW: Klasse zum Beenden eines Prozesses
 
Zwei Vorschläge auch von mir:
1. Der Parameter im Konstruktor sollte const sein, da er nicht verändert wird.
2. Ich würde eine statische Klassenfunktion mit Dateiname und Timeout vorschlagen, die ohne Umwege über eine Instanziierung einen Prozess killen kann. Für einfach gelagerte Fälle oder falls man keine Wiederverwendung anstrebt, fände ich das ganz praktisch.

Viele Grüße

Luckie 19. Nov 2010 10:54

AW: Klasse zum Beenden eines Prozesses
 
@himitsu: Wenn ich mich recht erinnere steht pe32.szExeFile nur der Prozessname, also kein Pfad.

@Mirage: Erster Vorschlag angenommen, zweiter muss noch überdacht werden.

himitsu 19. Nov 2010 11:29

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062528)
@himitsu: Wenn ich mich recht erinnere steht pe32.szExeFile nur der Prozessname, also kein Pfad.

OK, dann halt doch nur AnsiSameText.
Hätte den vollständigen Pfad erwartet (wie bei Application.ExeName :stupid: ),
da ja mehrere Dateien auf einer Festplatte den selben Namen besitzen können.

Luckie 21. Nov 2010 01:35

AW: Klasse zum Beenden eines Prozesses
 
Anbei die überarbeitet Version mit euren Vorschlägen:
Delphi-Quellcode:
// Klasse zum Benden eines Processes mittels TerminateProcess
// Class for terminating a process via TerminateProcess
// Michael Puff [http://www.michael-puff.de]
// 2010-11-21

unit MpuKillProcessCls;

interface

uses
  Windows, SysUtils, TlHelp32;

type
  TOnTerminated = procedure(Sender: TObject) of Object;
  TKillProcess = class(TObject)
  private
    FFilename: string;
    FProcessID: Cardinal;
    FTimeOut: Cardinal;
    FOnTerminated: TOnTerminated;
    procedure GetProcessID;
    procedure SetFilename(const Value: string);
    procedure SetPID(Value: Cardinal);
  public
    property Filename: string read FFilename write SetFilename;
    property PID: Cardinal read FProcessID write SetPID;
    property TimeOutMSecs: Cardinal read FTimeOut write FTimeOut;
    property OnTerminated: TOnTerminated read FOnTerminated write FOnTerminated;
    constructor Create;
    procedure Kill;
  end;

implementation

constructor TKillProcess.Create;
begin
  FTimeOut := 0;
end;

procedure TKillProcess.GetProcessID;
var
  ProcessSnapShot: THandle;
  pe32: TProcessEntry32;
begin
  ProcessSnapShot := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  if ProcessSnapShot <> INVALID_HANDLE_VALUE then
  begin
    pe32.dwSize := SizeOf(ProcessEntry32);
    if Process32First(ProcessSnapShot, pe32) then
    begin
      while Process32Next(ProcessSnapShot, pe32) do
      begin
        if AnsiSameText(FFilename, pe32.szExeFile) then
          FProcessID := pe32.th32ProcessID;
      end;
    end
    else
    begin
      RaiseLastOSError;
    end;
  end
  else
  begin
    RaiseLastOSError;
  end;
  CloseHandle(ProcessSnapShot);
  if FProcessID = 0 then
    raise Exception.Create('Process not found');
end;

procedure TKillProcess.Kill;
var
  ProcessHandle: Cardinal;
  WFSOReturnCode: DWORD;
begin
  ProcessHandle := OpenProcess(SYNCHRONIZE or PROCESS_TERMINATE, False, FProcessID);
  if ProcessHandle <> 0 then
  begin
    if TerminateProcess(ProcessHandle, 0) then
    begin
      WFSOReturnCode := WaitForSingleObject(ProcessHandle, FTimeOut);
      case WFSOReturnCode of
        WAIT_TIMEOUT:
          begin
            if FTimeOut > 0 then
              raise Exception.Create('Timeout');
          end;
        WAIT_FAILED:
          begin
            RaiseLastOSError;
          end;
        WAIT_OBJECT_0:
        begin
          if Assigned(OnTerminated) then
            OnTerminated(self);
        end;
      end;
    end
    else
    begin
      RaiseLastOSError;
    end;
  end
  else
  begin
    RaiseLastOSError;
  end;
end;

procedure TKillProcess.SetFilename(const Value: string);
begin
  FFilename := Value;
  GetProcessID;
end;

procedure TKillProcess.SetPID(Value: Cardinal);
begin
  FProcessID := Value;
end;

end.
Demo:
Delphi-Quellcode:
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  MpuKillProcessCls in 'MpuKillProcessCls.pas';

type
  TMain = class(TObject)
  public
    procedure OnTerminated(Sender: TObject);
  end;

procedure TMain.OnTerminated(Sender: TObject);
begin
  Writeln('Prozess beendet');
end;

var
  Main: TMain;
  KillProcess: TKillProcess;
begin
  Main := TMain.Create;
  try
    KillProcess := TKillProcess.Create;
    try
      try
        KillProcess.OnTerminated := Main.OnTerminated;
        KillProcess.TimeOutMSecs := 5000;
        KillProcess.Filename := 'notePad++.exe';
        //KillProcess.PID := 2696;
        KillProcess.Kill;
      except
        on E: Exception do
          Writeln(E.Message);
      end;
    finally
      KillProcess.Free;
    end;
  finally
    Main.Free;
  end;
  Readln;
end.
Bestehendes Problem ist noch die Benachrichtigung beim erfolgreichen Beenden. Da habe ich noch keine Idee, die mir gefällt. Problem ist der TimeOut von 0. Da geht er auch in den WAIT_TIMEOUT Zweig rein und der Prozess könnte trotzdem erfolgreich beendet worden sein - oder auch nicht. Das ist das Problem. Und bis das Problem nicht gelöst ist, kann und will ich den Code nicht offiziell veröffentlichen. Zur Zeit habe ich nur ein Ereignis, wenn das Objekt signalisiert wird.

toms 21. Nov 2010 04:28

AW: Klasse zum Beenden eines Prozesses
 
1. OpenProcess .... CloseHandle
2. Evtl. noch erweitern mit SE_DEBUG_NAME Privileg setzen

Luckie 21. Nov 2010 07:21

AW: Klasse zum Beenden eines Prozesses
 
Gut, dass ihr noch mal drüberguckt. Das CloseHandle habe ich übersehen.

Braucht man für SE_DEBUG_NAME nicht bestimmte rechte? Ich werde es aber mal ausprobieren. Scheint zu gehen.

Danke für die Hinweise.

Allerdings habe ich immer noch das Problem mit der Vollzugsmeldung beim erfolgreichen beenden bei WAIT_TIMEOUT.

Basilikum 21. Nov 2010 08:29

AW: Klasse zum Beenden eines Prozesses
 
irgendwie vermisse ich noch an geeigneter Stelle ein
Delphi-Quellcode:
FProcessID:=0;
, sonst kann die Konstallation eintreten, dass willkürlich ein Prozess terminiert wird (Prozess nicht gefunden, FProcessID steht zufällig auf einem Wert, der ein existierenden Prozess bezeichnet).

toms 21. Nov 2010 09:29

AW: Klasse zum Beenden eines Prozesses
 
Es ist ja bekannt, dass TerminateProcess() einen Prozess nicht unbedingt ordnungsgemäß beendet.
Darum werfe ich die Funktion SafeTerminateProcess vom Artikel A Safer Alternative to TerminateProcess() auf Dr. Dobb's in den Raum.

Siehe auch eine Delphi Umsetzung.

Anbei noch 2 Funktionen zum Überprüfen, ob ein Prozess beendet wurde:
  • GetExitCodeProcess()
  • if not CloseHandle() then ..;

Sir Rufo 21. Nov 2010 10:26

AW: Klasse zum Beenden eines Prozesses
 
Irgendwie verstehe ich nicht, warum das hier in eine Klasse gegossen wird.
Eine schnöde function/procedure hätte es auch getan.

Aber nun erzeuge ich eine Instanz um eine andere Instanz zu beenden und dann beende ich diese Beende-Instanz. Klingt irgendwie nicht logisch.

Was für mich Sinn ergeben würde, wäre eine Klasse, die zu einem Prozess Informationen gibt.
Wieviele Instanzen sind aktiv, wie hoch ist der Speicherverbrauch, etc.
Ja, die Möglichkeit einen Prozess zu killen gehört da auch rein.

Als Events würden sich anbieten, wenn einen neue Instanz auftaucht und wenn eine Instanz verschwindet.

Dann würde sich auch eine Klasse rechtfertigen.

Wäre also eine TaskManagerKlasse für jeweils eine Anwendung.

Assarbad 21. Nov 2010 10:37

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von toms (Beitrag 1062963)
Darum werfe ich die Funktion SafeTerminateProcess vom Artikel A Safer Alternative to TerminateProcess() auf Dr. Dobb's in den Raum.

Ähem, das ist Mumpitz, oder? Denn erstens braucht man mindestens die gleichen Rechte wie für TerminateProcess und zweitens wird diese Methode hier nicht zwischen Sitzungen funktionieren (Vista aufwärts). Sagen wir mal so, mit einem Datum von 1999 ist der Code mehr oder weniger "abgelaufen".

toms 21. Nov 2010 11:03

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Assarbad (Beitrag 1062986)
Zitat:

Zitat von toms (Beitrag 1062963)
Darum werfe ich die Funktion SafeTerminateProcess vom Artikel A Safer Alternative to TerminateProcess() auf Dr. Dobb's in den Raum.

Ähem, das ist Mumpitz, oder? Denn erstens braucht man mindestens die gleichen Rechte wie für TerminateProcess und zweitens wird diese Methode hier nicht zwischen Sitzungen funktionieren (Vista aufwärts). Sagen wir mal so, mit einem Datum von 1999 ist der Code mehr oder weniger "abgelaufen".

Die Grundidee ist noch nicht "abgelaufen". Die Methode mit "CreateRemoteThread, ExitProcess" wird nebst 13 anderen von "Process Hacker" implementiert und auch bei MySQL zu finden.

Luckie 21. Nov 2010 12:03

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Sir Rufo (Beitrag 1062978)
Irgendwie verstehe ich nicht, warum das hier in eine Klasse gegossen wird.
Eine schnöde function/procedure hätte es auch getan.

Es ist eben nicht nur eine Funktion, sondern mindestens zwei, wenn man den Prozess über den Dateinamen beenden will. Da diese zusammen gehören, bietet sich eine Klasse an.

Zitat:

Aber nun erzeuge ich eine Instanz um eine andere Instanz zu beenden und dann beende ich diese Beende-Instanz. Klingt irgendwie nicht logisch.
Falls du die Klasse TMain meinst, die wird in der Konsolenanwendung nur benötigt, wegen des Ereignisses.

Zitat:

Was für mich Sinn ergeben würde, wäre eine Klasse, die zu einem Prozess Informationen gibt.
Wieviele Instanzen sind aktiv, wie hoch ist der Speicherverbrauch, etc.
Das wäre eine andere Baustelle.
Zitat:

Ja, die Möglichkeit einen Prozess zu killen gehört da auch rein.
Meiner Meinung nach nicht, da das Ermitteln von Informationen und das Beenden eines Prozesses zwei unterschiedliche Aufgaben sind.

@Toms: Richtig mittels GetExitCodeProzess könnte ich bei einem TimeOut von 0 Millisekunden prüfen, ob der Prozess beendet wurde.

Sir Rufo 21. Nov 2010 13:21

AW: Klasse zum Beenden eines Prozesses
 
Man muss eine Instanz von
Delphi-Quellcode:
TKillProcess
erzeugen um eine andere Instanz zu beenden.
Das halte ich für unlogisch.

Mit der Klasse
Delphi-Quellcode:
TKillProcess
fragst du eh schon die Eigenschaften des Prozesses ab, weil du ja zu einem ExeNamen die ProcessID ermitteln möchtest.

Für mich würde die Klasse nur dann einen Sinn ergeben, wenn die Klasse eine längere Daseinsberechtigung als nur zum reinen Killen des Prozesses.

Eine Möglichkeit wäre es doch erst beim Aufrufen der Methode Kill die ProcessID zu ermitteln und dann zu killen.

Dann erzeugt man sich die Klasse, gibt den ExeNamen an und immer wenn man diese Exe aus dem Speicher haben möchte ruft man einfach Kill auf und die Prozesse werden gelöscht.

Das vermisse ich auch irgendwie, wenn eine Anwendung n-fach gestartet wurde, dann wird nur eine gelöscht. Welche das ist, hängt davon ab, welche als letzte in der Prozess-Liste steht.
Somit würde ich erwarten, dass bei der Angabe eines ExeNamens alle Prozesse gelöscht werden, die dazu gehören.

Dann wäre das Verhalten auch eindeutig und nicht ein Zufallsprodukt.

Ei Argument, was noch gegen die Verwendung einer Klasse spricht ist, dass es bei dem jetzigen Aufbau zu Fehlern kommen kann, die man so eigentlich nicht haben will.

Delphi-Quellcode:
KP := TKillProcess.Create;
try
  KP.FileName := 'notepad++.exe'; // Jetzt ermittelt die Klasse die PID
 
  // Ich muss jetzt noch ein paar Sachen prüfen, die so eine gewisse Zeit dauern
  Sleep( 10000 ); // So als Dummy-Beschäftigung

  KP.Kill; // Ja, ist denn die PID von eben immer noch die richtige?
 
finally
  KP.Free;
end;
Dieses ist durch dein Design so möglich und kann dazu führen, dass

a) Der Prozess ist schon beendet worden (von wem auch immer)
Wenn Kill jetzt einen Fehler wirft, ist der Fehler dann nicht verwirrend.
Ich möchte das als Resultat der Prozess nicht mehr im System läuft.
Das ist ja auch erreicht worden (zwar nicht ursächlich durch die Klasse, aber das Resultat ist das Gleiche) aber ich bekomme einen Fehler.
Könnte man evtl. noch mit leben

b) Der Prozess ist schon beendet worden( von wem auch immer) und ein völlig anderer Prozess (z.B. outlook.exe) hat jetzt die gleiche PID bekommen wie notepad++.exe vorher hatte. Nun wird mit Kill nicht notepad++.exe sondern outlook.exe geschlossen

Assarbad 21. Nov 2010 14:44

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von toms (Beitrag 1063000)
Die Grundidee ist noch nicht "abgelaufen". Die Methode mit "CreateRemoteThread, ExitProcess" wird nebst 13 anderen von "Process Hacker" implementiert und auch bei MySQL zu finden.

Wobei Process Hacker für mich nicht das Maß der Dinge ist :lol:

Ich sage nur, daß es ab Vista größere Probleme aufgrund der Sitzungsisolation und der Integritätsniveaus kommen wird. Ob und/oder wie das beherzigt wird, ist mir an Ende egal :zwinker:

Luckie 21. Nov 2010 15:10

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Sir Rufo (Beitrag 1063048)
Man muss eine Instanz von
Delphi-Quellcode:
TKillProcess
erzeugen um eine andere Instanz zu beenden.
Das halte ich für unlogisch.

Ein Prozess ist mich keine Instanz.

Zitat:

Für mich würde die Klasse nur dann einen Sinn ergeben, wenn die Klasse eine längere Daseinsberechtigung als nur zum reinen Killen des Prozesses.
Für mich hat eine Klasse schon dann eine Daseinsberechtigung, wenn sie mehrere zusammen gehörende Routinen kapselt.

Zitat:

Eine Möglichkeit wäre es doch erst beim Aufrufen der Methode Kill die ProcessID zu ermitteln und dann zu killen.
Das wäre eine Möglichkeit, die es sich lohnt noch mal zu überdenken. Aber selbst dann hast du, wenn du sauber programmieren willst mindestens zwei Routinen.
Zitat:

Dann erzeugt man sich die Klasse, gibt den ExeNamen an und immer wenn man diese Exe aus dem Speicher haben möchte ruft man einfach Kill auf und die Prozesse werden gelöscht.
Das ergibt Sinn.

Zitat:

Das vermisse ich auch irgendwie, wenn eine Anwendung n-fach gestartet wurde, dann wird nur eine gelöscht. Welche das ist, hängt davon ab, welche als letzte in der Prozess-Liste steht.
Somit würde ich erwarten, dass bei der Angabe eines ExeNamens alle Prozesse gelöscht werden, die dazu gehören.

Dann wäre das Verhalten auch eindeutig und nicht ein Zufallsprodukt.
Dann müsste ich nach dem Beenden die Prozessliste immer wieder durchlaufen. Mal gucken, wie sich das realisieren lässt.

Ei Argument, was noch gegen die Verwendung einer Klasse spricht ist, dass es bei dem jetzigen Aufbau zu Fehlern kommen kann, die man so eigentlich nicht haben will.

Zitat:

Delphi-Quellcode:
KP := TKillProcess.Create;
try
  KP.FileName := 'notepad++.exe'; // Jetzt ermittelt die Klasse die PID
 
  // Ich muss jetzt noch ein paar Sachen prüfen, die so eine gewisse Zeit dauern
  Sleep( 10000 ); // So als Dummy-Beschäftigung

  KP.Kill; // Ja, ist denn die PID von eben immer noch die richtige?
 
finally
  KP.Free;
end;
Dieses ist durch dein Design so möglich und kann dazu führen, dass

a) Der Prozess ist schon beendet worden (von wem auch immer)
Wenn Kill jetzt einen Fehler wirft, ist der Fehler dann nicht verwirrend.
Ich möchte das als Resultat der Prozess nicht mehr im System läuft.
Das ist ja auch erreicht worden (zwar nicht ursächlich durch die Klasse, aber das Resultat ist das Gleiche) aber ich bekomme einen Fehler.
Könnte man evtl. noch mit leben

b) Der Prozess ist schon beendet worden( von wem auch immer) und ein völlig anderer Prozess (z.B. outlook.exe) hat jetzt die gleiche PID bekommen wie notepad++.exe vorher hatte. Nun wird mit Kill nicht notepad++.exe sondern outlook.exe geschlossen
Dann setze den Prozessnamen nach dem Code:
Delphi-Quellcode:
KP := TKillProcess.Create;
try
  // Ich muss jetzt noch ein paar Sachen prüfen, die so eine gewisse Zeit dauern
  Sleep( 10000 ); // So als Dummy-Beschäftigung

  KP.FileName := 'notepad++.exe'; // Jetzt ermittelt die Klasse die PID
  KP.Kill; // Ja, ist denn die PID von eben immer noch die richtige?
 
finally
  KP.Free;
end;

Sir Rufo 21. Nov 2010 15:23

AW: Klasse zum Beenden eines Prozesses
 
Hmmm, wenn man eine Klasse nur dann korrekt benutzen kann, indem man x Vorgaben einhält, und diese Vorgaben aber nur durch ein ungeschicktes Design der Klasse herrühren, dann würde ich immer das Design der Klasse ändern.

Ansonsten muss man an solche Klassen immer den Hinweis kleben:
Zitat:

Ask the old and wise before use!
Da gab es vor kurzem ein sehr geiles Video genau zu diesem Thema, mal sehen ob ich den Link dazu noch finde. War irgendwas mit Singletons.

Hier der Beitrag mit dem Link zum Video

Prozess und Instanz

Wenn eine Anwendung x-fach gestartet ist, und man findet somit im System auch x Prozesse, so kann man im übertragenen Sinne auch von Instanzen sprechen.
Ich wollte damit nur sprachlich darstellen, dass hier etwas erzeugt wird um etwas anderes zu zerstören und dann muss ich diesen Zerstören selber wieder zerstören, weil dieser nutzlos geworden ist (es sei denn ich weise diesem wieder einen neuen Wert zu)

Luckie 21. Nov 2010 15:32

AW: Klasse zum Beenden eines Prozesses
 
OK, überredet. ;)

stOrM 21. Nov 2010 17:18

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Ähem, das ist Mumpitz, oder? Denn erstens braucht man mindestens die gleichen Rechte wie für TerminateProcess und zweitens wird diese Methode hier nicht zwischen Sitzungen funktionieren (Vista aufwärts). Sagen wir mal so, mit einem Datum von 1999 ist der Code mehr oder weniger "abgelaufen".
Naja als ich damals den Artikel über Winlogon Notification Packages geschrieben hatte stolperte ich immer wieder bei der Recherche über das besagte SafeTerminateProcess, was dann irgendwann meinen Jagdinstinkt geweckt hat :mrgreen: Als ich dann die Erklärung dazu endlich gefunden hatte dachte ich mir gut, jetzt wo ich es weiss setzt ich es nach Delphi um. Mehr oder weniger hat die Umsetzung auf meinen Blog auch nicht zu sagen...

Dezipaitor 21. Nov 2010 23:13

AW: Klasse zum Beenden eines Prozesses
 
Madshi hatte glaube ich eine CreateRemoteThread, die auch über Sitzungen funkz. Naja, ich sehe schon wieder ne Menge Virenscanner, die da anschlagen würde.
Und dann einfach PostQuitMessage aufrufen muss auch nicht funkzen. Irgendein Verrückter könnte ein Fenster pro Thread gebaut haben, um Einfrieren zu verhindern.

Assarbad 21. Nov 2010 23:17

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Dezipaitor (Beitrag 1063172)
Madshi hatte glaube ich eine CreateRemoteThread, die auch über Sitzungen funkz.

Den Code rückt er aber nicht so freizügig raus, soweit ich weiß. Damit eher ungeeignet als Basis für Michael's Klasse ;)

Luckie 22. Nov 2010 12:28

AW: Klasse zum Beenden eines Prozesses
 
Aktuelle Version:
Delphi-Quellcode:
// Klasse zum Benden eines Processes mittels TerminateProcess
// Class for terminating a process via TerminateProcess
// Michael Puff [http://www.michael-puff.de]
// 2010-11-22

unit MpuKillProcessCls;

interface

uses
  Windows,
  Messages,
  SysUtils,
  TlHelp32;

type
  TOnTerminated = procedure(HasTerminated: Boolean; ProcessExefile: string) of object;
  TOnProcessNotFound = procedure(ProcessExefile: string) of object;
  TKillProcess = class(TObject)
  private
    FProcessExefile: string;
    FProcessID: Cardinal;
    FTimeOut: Cardinal;
    FOnTerminated: TOnTerminated;
    FOnProcessNotFound: TOnProcessNotFound;
    function GetProcessID: Cardinal;
    procedure SetProcessExefile(const Value: string);
    procedure SetPID(Value: Cardinal);
    function EnablePrivilege(const Privilege: string; fEnable: Boolean; out
      PreviousState: Boolean): DWORD;
  public
    property ProcessExefile: string read FProcessExefile write SetProcessExefile;
    property PID: Cardinal read FProcessID write SetPID;
    property TimeOutMSecs: Cardinal read FTimeOut write FTimeOut;
    property OnTerminated: TOnTerminated read FOnTerminated write FOnTerminated;
    property OnProcessNotFound: TOnProcessNotFound read FOnProcessNotFound write FOnProcessNotFound;
    constructor Create;
    procedure Kill;
  end;

implementation

constructor TKillProcess.Create;
begin
  FTimeOut := 0;
end;

function TKillProcess.EnablePrivilege(const Privilege: string;
  fEnable: Boolean; out PreviousState: Boolean): DWORD;
var
  Token: THandle;
  NewState: TTokenPrivileges;
  Luid: TLargeInteger;
  PrevState: TTokenPrivileges;
  Return: DWORD;
begin
  PreviousState := True;
  if (GetVersion() > $80000000) then
    // Win9x
    Result := ERROR_SUCCESS
  else
  begin
    // WinNT
    if not OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, Token) then
      Result := GetLastError()
    else
    try
      if not LookupPrivilegeValue(nil, PChar(Privilege), Luid) then
        Result := GetLastError()
      else
      begin
        NewState.PrivilegeCount := 1;
        NewState.Privileges[0].Luid := Luid;
        if fEnable then
          NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
        else
          NewState.Privileges[0].Attributes := 0;
        if not AdjustTokenPrivileges(Token, False, NewState,
          SizeOf(TTokenPrivileges), PrevState, Return) then
          Result := GetLastError()
        else
        begin
          Result := ERROR_SUCCESS;
          PreviousState :=
            (PrevState.Privileges[0].Attributes and SE_PRIVILEGE_ENABLED <> 0);
        end;
      end;
    finally
      CloseHandle(Token);
    end;
  end;
end;

function TKillProcess.GetProcessID: Cardinal;
var
  ProcessSnapShot: THandle;
  pe32: TProcessEntry32;
  ProcessID: Cardinal;
begin
  ProcessID := 0;
  ProcessSnapShot := CreateToolHelp32SnapShot(TH32CS_SNAPPROCESS, 0);
  if ProcessSnapShot <> INVALID_HANDLE_VALUE then
  begin
    pe32.dwSize := SizeOf(ProcessEntry32);
    if Process32First(ProcessSnapShot, pe32) then
    begin
      while Process32Next(ProcessSnapShot, pe32) do
      begin
        if AnsiSameText(FProcessExefile, pe32.szExeFile) then
        begin
          ProcessID := pe32.th32ProcessID;
          Break;
        end;
      end;
    end
    else
    begin
      RaiseLastOSError;
    end;
  end
  else
  begin
    RaiseLastOSError;
  end;
  CloseHandle(ProcessSnapShot);
  if ProcessID = 0 then
  begin
    if Assigned(OnProcessNotFound) then
      OnProcessNotFound(FProcessExefile);
  end;
  Result := ProcessID;
end;

procedure TKillProcess.Kill;
var
  EnablePrivelege: DWORD;
  PreviousPriviliegeState: Boolean;
  ProcessHandle: Cardinal;
  WFSOReturnCode: DWORD;
begin
  FProcessID := GetProcessID;
  if FProcessID <> 0 then
  begin
    repeat
      EnablePrivelege := EnablePrivilege('SE_DEBUG_NAME', true, PreviousPriviliegeState);
      if EnablePrivelege <> 0 then
      begin
        ProcessHandle := OpenProcess(SYNCHRONIZE or PROCESS_TERMINATE, False, FProcessID);
        if ProcessHandle <> 0 then
        begin
          if TerminateProcess(ProcessHandle, 0) then
          begin
            WFSOReturnCode := WaitForSingleObject(ProcessHandle, FTimeOut);
            case WFSOReturnCode of
              WAIT_TIMEOUT:
                begin
                  if GetProcessID = 0 then
                  begin
                    if Assigned(OnTerminated) then
                      OnTerminated(True, FProcessExefile);
                  end
                  else
                  begin
                    if Assigned(OnTerminated) then
                      OnTerminated(False, FProcessExefile);
                    Exit;
                  end;
                  if FTimeOut > 0 then
                  begin
                    raise Exception.Create('Timeout');
                  end;
                end;
              WAIT_FAILED:
                begin
                  RaiseLastOSError;
                end;
              WAIT_OBJECT_0:
                begin
                  if Assigned(OnTerminated) then
                    OnTerminated(True, FProcessExefile);
                end;
            end;
          end
          else
          begin
            RaiseLastOSError;
          end;
          CloseHandle(ProcessHandle);
        end
        else
        begin
          RaiseLastOSError;
        end;
      end
      else
      begin
        raise Exception.Create(SysErrorMessage(GetLastError));
      end;
      FProcessID := GetProcessID;
    until FProcessID = 0;
  end;
end;

procedure TKillProcess.SetProcessExefile(const Value: string);
begin
  FProcessExefile := Value;
end;

procedure TKillProcess.SetPID(Value: Cardinal);
begin
  FProcessID := Value;
end;

end.
Demo:
Delphi-Quellcode:
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  MpuKillProcessCls in 'MpuKillProcessCls.pas';

type
  TMain = class(TObject)
  public
    procedure OnTerminated(HasTerminated: Boolean; ProcessExefile: string);
    procedure OnProcessNotFound(ProcessExefile: string);
  end;

procedure TMain.OnProcessNotFound(ProcessExefile: string);
begin
  Writeln('Fehler. Process ' + ProcessExeFile + ' nicht gefunden.');
end;

procedure TMain.OnTerminated(HasTerminated: Boolean; ProcessExefile: string);
begin
  if HasTerminated then
    Writeln('Prozess ' + ProcessExefile + ' beendet')
  else
    Writeln('Fehler beim Beenden des Prozesses ' + ProcessExefile);
end;

var
  Main: TMain;
  KillProcess: TKillProcess;
begin
  Main := TMain.Create;
  try
    KillProcess := TKillProcess.Create;
    try
      try
        KillProcess.OnTerminated := Main.OnTerminated;
        KillProcess.OnProcessNotFound := Main.OnProcessNotFound;
        KillProcess.TimeOutMSecs := 5000;
        KillProcess.ProcessExefile := 'photofiltre.exe';
        //KillProcess.ProcessExefile := 'svchost.exe';
        //KillProcess.PID := 2696;
        KillProcess.Kill;
      except
        on E: Exception do
          Writeln(E.Message);
      end;
    finally
      KillProcess.Free;
    end;
  finally
    Main.Free;
  end;
  Readln;
end.

HeikoAdams 22. Nov 2010 12:56

AW: Klasse zum Beenden eines Prozesses
 
Ich habe den kompletten Thread jetzt nicht durchgelesen, aber was mir noch fehlen würde, wäre die Möglichkeit, Prozesse anhand des Fenstertitels zu beenden.

Luckie 22. Nov 2010 13:59

AW: Klasse zum Beenden eines Prozesses
 
Immer langsam mit den jungen Pferden. Man muss sich ja noch für Version 2 was aufheben. ;)

Delphi-Laie 29. Nov 2010 16:28

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062473)
Zitat:

Zitat von himitsu (Beitrag 1062465)
Zitat:

Delphi-Quellcode:
if ... = true then

Das hätt ich jetzt nicht von dir erwartet.

Autsch, das passiert, wenn man alten Code per Copy and paste übernimmt.

Luckie, das wäre nicht nötig gewesen. Darob unterhielten wir uns schon wiederholt, erst vor Jahren (als ich zum ersten Mal darüber stolperte und sogar eine Diskussion dazu eröffnete, die recht lebhaft wurde), und dann vor einigen Monaten noch einmal im (gleichnamigen) Delphiforum.

Auch Dein

Zitat:

Zitat von Luckie (Beitrag 1062459)
Delphi-Quellcode:
if Process32First(ProcessSnapShot, pe32) = true then
begin
  while Process32Next(ProcessSnapShot, pe32) = true do
  begin
    if pos(LowerCase(FProcessFile), LowerCase(pe32.szExeFile)) <> 0 then
      FProcessID := pe32.th32ProcessID;
   end;
end

ist ein Quentchen eleganter möglich (auch das schrieb ich Dir schon):

Delphi-Quellcode:
if Process32First(ProcessSnapShot, pe32) then
  repeat
  if pos(LowerCase(FProcessFile), LowerCase(pe32.szExeFile)) <> 0 then
      FProcessID := pe32.th32ProcessID
  until not Process32Next(ProcessSnapShot, pe32)
Das nur so als Ergänzung zu dem ansonsten guten und bewährten (und deshalb auch von mir ursprünglich so übernommenen Code).

Gruß Delph-Laie

Delphi-Laie 29. Nov 2010 16:33

AW: Klasse zum Beenden eines Prozesses
 
Zitat:

Zitat von Luckie (Beitrag 1062528)
@himitsu: Wenn ich mich recht erinnere steht pe32.szExeFile nur der Prozessname, also kein Pfad.

Unter Win9x der gesamte Pfad, unter NTx nur der Dateiname des Prozesse.

Für den Pfad benötigt man deshalb unter NTx beim ersten Eintrag des Modulschnappschusses den Eintrag "szExePath".

Luckie 29. Nov 2010 17:33

AW: Klasse zum Beenden eines Prozesses
 
@Delphi-Laie: Du beziehst dich da gerade auf veralteten Code.


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:02 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz