Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   ShellExecuteEx + TerminateProcess (https://www.delphipraxis.net/207684-shellexecuteex-terminateprocess.html)

shebang 22. Apr 2021 11:32

ShellExecuteEx + TerminateProcess
 
Hallo,

ich verwende ShellExecuteEx um eine Konsole zu starten, die einen Befehl ausführt und die Ausgabe in eine Textdatei umleitet soll. Da der Befehl kontinuierlich Daten erzeugt, möchte ich ihn irgendwann abbrechen. Dafür sollte doch TerminateProcess geeignet sein. Das Problem ist, das Beenden funktioniert nicht. Für das Bespiel hier habe ich einfach ping verwendet und die Umleitung weggelassen, da sie für das eigentliche Problem nicht wichtig ist. Ich habe also eine Form mit zwei Buttons und den folgenden Funktionen erstellt:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  ShExecInfo.cbSize      := SizeOf(ShellExecuteInfo);
  ShExecInfo.fMask       := SEE_MASK_NOCLOSEPROCESS;
  ShExecInfo.lpVerb      := 'open';
  ShExecInfo.lpFile      := 'cmd.exe';
  ShExecInfo.lpParameters := '/c ping -t delphipraxis.net';
  ShExecInfo.nShow       := SW_SHOW;

  if not ShellExecuteEx(@ShExecInfo) then raise Exception.Create('ShellExecuteEx error = ' + IntToStr(GetLastError));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if not TerminateProcess(ShExecInfo.hProcess, 0) then raise Exception.Create('TerminateProcess error = ' + IntToStr(GetLastError));
end;
Das Flag
Delphi-Quellcode:
SEE_MASK_NOCLOSEPROCESS
führt dazu, dass das Process handle in der Variablen
Delphi-Quellcode:
ShExecInfo.hProcess
gespeichert wird. Es gibt keinerlei Fehlermeldung, aber der Prozess wird nicht beendet. Was mache ich falsch? Gibt es eine andere Möglichkeit die Konsole zu schließen?

PS: CreateProcess verwende ich nicht, da dort die Umleiten der Ausgabe in eine Datei nicht funktioniert.

Der schöne Günther 22. Apr 2021 12:43

AW: ShellExecuteEx + TerminateProcess
 
Der Doku "How Processes are Terminated" nach:
Zitat:

(...)
For console processes, the default console control handler calls ExitProcess when the console receives a CTRL+C or CTRL+BREAK signal.
(...)
Da du den Prozess ja über seine eigene, neue Konsole laufen lässt ist wahrscheinlich der einfachste Weg, die Anwendung in einem Job laufen zu lassen und diesen dann notfalls zu beenden.


Wenn ich das allerdings anmerken darf, das ganze erscheint trotzdem etwas Quick & Dirty. Bist du sicher, dass beispielsweise der Umweg über Dateien auf der Festplatte notwendig ist? Das tolle an Konsolenanwendungen ist eigentlich dass du die direkt aus deiner Delphi-Anwendung über Pipes (StdIn, StdOut, StdErr) auslesen und füttern kannst wie der Mensch der das schwarze Konsolenfenster mit der Tastatur bedient.

PS: Ja, dass es mit Jobs zu funktioniert bestätigt hier jemand. Trotzdem hätte ich gesagt dass der jetzige Ansatz wirklich nicht optimal ist.

Dalai 22. Apr 2021 13:28

AW: ShellExecuteEx + TerminateProcess
 
Bist du sicher, dass der Prozess nicht beendet wird? Du killst cmd.exe, aber die Ausgabe kommt von ping.exe. Führt man eine CMD von Hand aus (mit ping auf irgendeinen Host) und killt per Task-Manager die cmd.exe, bleibt das Konsolenfenster dennoch offen und wird erst bei Abbruch der ping.exe (z.B. per Strg+C) geschlossen.

Grüße
Dalai

KodeZwerg 22. Apr 2021 13:31

AW: ShellExecuteEx + TerminateProcess
 
Delphi-Referenz durchsuchenAttachConsole bzw Delphi-Referenz durchsuchenAllocConsole sollte doch funktioniert und im Cancel-Event dann Delphi-Referenz durchsuchenFreeConsole oder?

Aber ja, wenn es ein anderes Programm ausführt bringt das nicht viel....

shebang 22. Apr 2021 14:31

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1487593)
Bist du sicher, dass beispielsweise der Umweg über Dateien auf der Festplatte notwendig ist? Das tolle an Konsolenanwendungen ist eigentlich dass du die direkt aus deiner Delphi-Anwendung über Pipes (StdIn, StdOut, StdErr) auslesen und füttern kannst wie der Mensch der das schwarze Konsolenfenster mit der Tastatur bedient.

Die Daten, die in die Datei geschrieben werden sollen, benötige ich innerhalb meiner Delphi-Anwendung gar nicht. Die sind für eine spätere externe Auswertung vorgesehen. Ich möchte im Prinzip nur ein Konsolenprogramm starten und zu einem späteren Zeitpunkt wieder stoppen können. Die Konsole wird später gar nicht erst angezeigt, sondern soll unsichtbar im Hintergrund laufen.

Ich habe auch schon versucht ein Ctrl+C an die Konsole zu schicken aber das hat auch nicht den gewünschten Effekt gebracht:
Delphi-Quellcode:
PostMessage(ShExecInfo.hProcess, WM_KEYDOWN, VK_CONTROL,    0);
PostMessage(ShExecInfo.hProcess, WM_KEYDOWN, VkKeyScan('c'), 0);
PostMessage(ShExecInfo.hProcess, WM_KEYUP,  VkKeyScan('c'), 0);
PostMessage(ShExecInfo.hProcess, WM_KEYUP,  VK_CONTROL,    0);

shebang 22. Apr 2021 14:40

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Dalai (Beitrag 1487602)
Bist du sicher, dass der Prozess nicht beendet wird? Du killst cmd.exe, aber die Ausgabe kommt von ping.exe. Führt man eine CMD von Hand aus (mit ping auf irgendeinen Host) und killt per Task-Manager die cmd.exe, bleibt das Konsolenfenster dennoch offen und wird erst bei Abbruch der ping.exe (z.B. per Strg+C) geschlossen.

Komme ich denn irgendwie an das Handle von ping.exe, damit ich dem ein Strg+C schicken kann?

Delphi.Narium 22. Apr 2021 15:41

AW: ShellExecuteEx + TerminateProcess
 
Was passiert denn bei
Delphi-Quellcode:
 ShExecInfo.cbSize := SizeOf(ShellExecuteInfo);
  ShExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
  ShExecInfo.lpVerb := 'open';
  ShExecInfo.lpFile := 'ping.exe';
  ShExecInfo.lpParameters := '-t delphipraxis.net';
  ShExecInfo.nShow := SW_SHOW;
Brauchst Du denn überhaupt eine Konsole als "Zwischenwirt"?

Der schöne Günther 22. Apr 2021 16:00

AW: ShellExecuteEx + TerminateProcess
 
Das kann hinten und vorne nicht klappen.
Erstens machst du PostMessage mit einem Handle auf den Prozess. Man schickt aber Messages an ein Fensterhandle (HWND).

Zweitens interessieren sich Konsolenanwendungen herzlich wenig für Messages.

Ich bleibe dabei, ich sehe zwei Möglichkeiten:
1. Man verbindet sich mit der Konsole des gestarteten Prozesses und schickt ihm ein Ctrl+C mittels GenerateConsoleCtrlEvent

oder

2. Man packt das Ding in einem Job und wenn man es wirklich hart abschießenb will schließt man einfach den Job.


Ich nutze unter Windows die Jobs gerne, sie funktionieren zuverlässig und sind wirklich das Mittel der Wahl wenn man selbst gestartete Prozesse ordentlich und sicher verwalten will.

shebang 22. Apr 2021 16:48

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1487630)
Was passiert denn bei
Delphi-Quellcode:
 ShExecInfo.cbSize := SizeOf(ShellExecuteInfo);
  ShExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
  ShExecInfo.lpVerb := 'open';
  ShExecInfo.lpFile := 'ping.exe';
  ShExecInfo.lpParameters := '-t delphipraxis.net';
  ShExecInfo.nShow := SW_SHOW;
Brauchst Du denn überhaupt eine Konsole als "Zwischenwirt"?

Dein Vorschlag funktioniert prinzipiell, TerminateProcess beendet den Prozess wie gewünscht. Allerdings ist dann die Umleitung der Ausgabe in eine Datei nicht möglich.

Delphi.Narium 22. Apr 2021 17:06

AW: ShellExecuteEx + TerminateProcess
 
Wieso nicht?
Delphi-Quellcode:
ShExecInfo.cbSize := SizeOf(ShellExecuteInfo);
ShExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.lpVerb := 'open';
ShExecInfo.lpFile := 'ping.exe';
ShExecInfo.lpParameters := '-t delphipraxis.net > c:\temp\datei_in_die_umgeleitet_werden.soll';
ShExecInfo.nShow := SW_SHOW;

shebang 22. Apr 2021 17:33

AW: ShellExecuteEx + TerminateProcess
 
Das hatte ich auch schon probiert, aber scheinbar weiß die ping.exe nicht, was sie mit dem ">" anfangen soll und beendet sich sofort beim Aufruf. So wie ich es verstanden habe ist die Umleitung mittels ">" eine Funktionalität der Konsole.

shebang 22. Apr 2021 17:37

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1487632)
Man packt das Ding in einem Job und wenn man es wirklich hart abschießenb will schließt man einfach den Job.

Hast du zufällig einen Link zu einem Bespiel, wie man das in Delphi löst?

KodeZwerg 22. Apr 2021 18:36

AW: ShellExecuteEx + TerminateProcess
 
nur mal eine frage so am rande, geht es wirklich um "ping.exe" oder andere CLI anwendungen?

ping kann man ja per indy und konsorten auch ohne CLI haben, unsichtbar im hintergrund, auswerten und schreiben in eine datei etc....

Dalai 23. Apr 2021 02:50

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1487641)
Wieso nicht?

Weil die CMD (oder PowerShell oder Terminal oder was auch immer) sich um die Umleitungen kümmert, nicht jedes Konsolenprogramm selbst. Letzteres weiß gar nichts von Umleitungen sondern nur von StdIn, StdOut und StdErr.

@shebang:
Werden denn selbstgeschriebene Konsolenprogramme ausgeführt? Wenn ja, dann würde ich das Konzept überdenken und die Programme entsprechend der Anforderungen anpassen. Prozesse töten sollte immer einer der letzten Wege sein.

Grüße
Dalai

TiGü 23. Apr 2021 09:03

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von KodeZwerg (Beitrag 1487653)
nur mal eine frage so am rande, geht es wirklich um "ping.exe"

Ne, das geht meiner Ansicht nach aber aus den ersten Sätzen des Eröffnungsposts hervor.

Delphi.Narium 23. Apr 2021 09:51

AW: ShellExecuteEx + TerminateProcess
 
Frag' bitte mal die Suchmaschine Deiner Wahl nach createprocess with pipe site:delphipraxis.net. Da sollten ein paar brauchbare Ansätze zu finden sein. Momentan kann ich aber nicht entscheiden, welcher der für Dich Richtige ist.

Der beste Ansatz ist vermutlich hier zu finden: https://www.delphipraxis.net/169697-...t-ausgabe.html, allerdings kann ich dort nicht erkennne, wie dort der gestartete Prozess beendet werden kann.

KodeZwerg 23. Apr 2021 10:10

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von TiGü (Beitrag 1487670)
Zitat:

Zitat von KodeZwerg (Beitrag 1487653)
nur mal eine frage so am rande, geht es wirklich um "ping.exe"

Ne, das geht meiner Ansicht nach aber aus den ersten Sätzen des Eröffnungsposts hervor.

Dann könnte man (weil ich keine ahnung von jobs habe) als workaround eine prozess liste (Hier im Forum suchenTlHelp32) anfertigen bevor man das ganze startet.
Nachdem es gestartet wurde noch eine liste anfertigen.
Prozessliste nach "CLIname.exe" (ping.exe) bei den neu dazugekommenen durchforsten und PID merken.
Bei abbruch dann zuerst die PID abschießen anschließend die CLI von der du ja bereits das handle kennst.
(In manchen situationen ändert sich das handle allerdings, das sollte man im hinterkopf haben)

Wenn es wiederum doch nur ums pingen geht würde ich komplett auf CLI verzichten und per MSDN-Library durchsuchenIcmpSendEcho oder Hier im Forum suchenTIdStackWindows abfragen/auswerten/speichern solange wie man es halt benötigt.

Der schöne Günther 23. Apr 2021 14:03

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von shebang (Beitrag 1487648)
Hast du zufällig einen Link zu einem Bespiel, wie man das in Delphi löst?

Ja, Sourcecode (2 Dateien) sind hier. Die Header hatte ich hiervon übernommen.

Benutzung ist folgendermaßen:
Delphi-Quellcode:
var
   windowsJob: TJobObject;
   shellExecuteInfo: TShellExecuteInfo;   
begin
   (...)
   windowsJob := TJobObject.Create();
   ShellExecuteEx(shellExecuteInfo);
   windowsJob.moveProcessTo(shellExecuteInfo.Handle);
end;

shebang 23. Apr 2021 15:53

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1487672)
Der beste Ansatz ist vermutlich hier zu finden: https://www.delphipraxis.net/169697-...t-ausgabe.html, allerdings kann ich dort nicht erkennne, wie dort der gestartete Prozess beendet werden kann.

Vielen Dank, das hat mich zu meiner Lösung gebracht. Mir war nicht bewusst, dass man über die Pipes von CreateProcess auch eine Datei angeben kann. Es funktioniert jetzt genau so wie ich es mir vorgestellt habe.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  StartupInfo : TStartupInfo;
  SecAttr    : TSecurityAttributes;
  command    : string;
  filename   : string;
begin
  command := 'ping.exe -t delphipraxis.net';
  filename := 'D:\output.txt';

  ZeroMemory(@SecAttr, SizeOf(TSecurityAttributes));
  SecAttr.nLength       := SizeOf(SecAttr);
  SecAttr.bInheritHandle := True;
  fileHandle            := CreateFile(PChar(filename), GENERIC_WRITE, FILE_SHARE_WRITE, @SecAttr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

  ZeroMemory(@StartupInfo, SizeOf(TStartupInfo));
  StartupInfo.cb         := SizeOf(TStartupInfo);
  StartupInfo.hStdOutput := fileHandle;
  StartupInfo.dwFlags    := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := SW_HIDE;

  CreateProcess(nil, PChar(command), nil, nil, TRUE, 0, nil, nil, StartupInfo, ProcessInfo);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TerminateProcess(ProcessInfo.hProcess, 0);
  CloseHandle(fileHandle);
  CloseHandle(ProcessInfo.hThread);
  CloseHandle(ProcessInfo.hProcess);
end;

KodeZwerg 26. Apr 2021 17:12

AW: ShellExecuteEx + TerminateProcess
 
Schade das shebang Kommunikation mit mir vermeidet, sonst hätte ich meine timer-basierte CLI freie Lösung mal zeigen können, da es sich bis zum Ende ja doch nur um Ping gehandelt hat oder es war alles nur exemplarisch und ich bin schwer von begriff :-]

TiGü 27. Apr 2021 08:00

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von KodeZwerg (Beitrag 1487893)
...oder es war alles nur exemplarisch und ich bin schwer von begriff :-]

Ja!

shebang 27. Apr 2021 11:17

AW: ShellExecuteEx + TerminateProcess
 
Zitat:

Zitat von KodeZwerg (Beitrag 1487893)
Schade das shebang Kommunikation mit mir vermeidet, sonst hätte ich meine timer-basierte CLI freie Lösung mal zeigen können, da es sich bis zum Ende ja doch nur um Ping gehandelt hat oder es war alles nur exemplarisch und ich bin schwer von begriff :-]

Da ist bei dir nur ein falscher Eindruck entstanden. Ich dachte, dass die Antwort von TiGü auf deine Frage ausreicht. Ich habe die ping.exe als Beispiel genommen, da sie kontinuierlich Text ausgibt und bei jedem potenziellen Tester meiner Codes vorhanden sein sollte. Bei der realen Anwendung handelt es sich um die tshark.exe von Wireshark.

Das Gute an der gefundenen Lösung ist ja, dass sie (zumindest aus meiner Sicht) allgemeingültig ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:38 Uhr.

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