Discussion:
CreateProcess, warten auf Ende und zwischendurch abbrechen
(zu alt für eine Antwort)
Julian Meurer
2007-05-09 18:22:19 UTC
Permalink
Hallo zusammen,
ich verwende aus Simons FAQ die Funktion execandwait, funktioniert
tadellos.
Am liebsten würde ich die zu startenden Anwendung minimiert oder
versteckt starten, mein Programm darf erst fortsetzen, wenn sie
beendet ist, denn es arbeitet mit dem Ergebnis weiter. Der Anwender
soll aber die Chance haben zwischendurch abzubrechen, wenn es ihm zu
lange dauert.
Auch dazu habe ich in der FAQ was gefunden, die unit closeapp.
Diese verlangt aber ein das Handle des gestarteten Prozesses. Leider
langt mein API-Wissen nicht, um das 'wie' zu verstehen.
- Kann ich das Handle des Prozesses beim Start mit CreateProcess
irgendwie in einer Variable speichern ?
- Kann ich dieses dann nutzen, um den Process aus meiner weiter
wartenden Anwendung, die auch den Fokus behalten sollte, zu beenden?
(wie?)
- Funktioniert das auch mit einem minimierten oder versteckt laufenden
Prozess?
Ich bin dankbar für jeden Tipp bzw. Beispielcode.
Julian
Andreas
2007-05-09 23:17:29 UTC
Permalink
Post by Julian Meurer
Am liebsten würde ich die zu startenden Anwendung minimiert oder
versteckt starten
StartInfo: TStartupInfo;

StartInfo.dwFlags := STARTF_USESHOWWINDOW;
StartInfo.wShowWindow := SW_MINIMIZE; //SW_HIDE;
Post by Julian Meurer
mein Programm darf erst fortsetzen, wenn sie
beendet ist
Der Anwender soll aber die Chance haben zwischendurch
abzubrechen, wenn es ihm zu lange dauert.
while WaitForSingleObject(ProcInfo.hProcess,100) = WAIT_TIMEOUT do
begin
application.Handlemessage;
end;


und dann z.B. bei einem Button-Click das Programm beenden.
Alternativ kann mit
if GetExitCodeProcess(ProcInfo.hProcess, retCode) and (retCode=STILL_ACTIVE)
then
überprüft werden ob das Programm noch läuft.
Post by Julian Meurer
Auch dazu habe ich in der FAQ was gefunden, die unit closeapp.
Diese verlangt aber ein das Handle des gestarteten Prozesses.
Das Handle des Hauptfensters wird dort benötigt, nicht das Prozess-Handle.
Zumindest sofern es die gleiche Funktion ist, die ich meine:
[...]
if GetWindowLong(Handle, GWL_HINSTANCE) = LongInt(Info) then begin
PostMessage(Handle, WM_CLOSE, 0, 0);
[...]

Prozess und Main-Thread Handle und deren IDs gibts in TProcessInformation
(letzter Parameter von CreateProcess)




So irgendwie könnte das dann aussehen:

var ProcInfo: TProcessInformation;

procedure TForm1.Button1Click(Sender: TObject);
var
StartInfo: TStartupInfo;
result:boolean;

begin

FillChar(StartInfo, SizeOf(TStartupInfo), #0);
StartInfo.dwFlags := STARTF_USESHOWWINDOW;
StartInfo.wShowWindow := SW_MINIMIZE; //SW_HIDE;
StartInfo.cb := SizeOf(TStartupInfo);

Result := CreateProcess(nil, 'notepad.exe', nil, nil, False,
CREATE_NEW_PROCESS_GROUP or CREATE_NEW_CONSOLE,
nil, nil, StartInfo, ProcInfo);

if Result then
begin
while WaitForSingleObject(ProcInfo.hProcess,100) = WAIT_TIMEOUT do
begin
application.Handlemessage;
end;
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
end;
end;



type TWindowsProcParam = record threadID, winHandele:cardinal end;
PWindowsProcParam =^TWindowsProcParam;

function EnumWindowsProc(Handle: HWND; param: PWindowsProcParam): boolean;
stdcall;
var pid: HWND;
begin
result:= TRUE;

if (param^.threadID <> 0) and (GetWindowThreadProcessId( Handle, @pid )
= param^.threadID) then
begin
param^.winHandele := Handle;
result:= FALSE;
end;
end;

procedure CloseProcess(threadID: cardinal; timeout: integer;
kill_after_timeout:boolean);
var result, retCode: cardinal;
param: TWindowsProcParam;
begin
if GetExitCodeProcess(ProcInfo.hProcess, retCode) and
(retCode=STILL_ACTIVE) then
begin // Prozess lebt noch
param.threadID := threadID;
EnumWindows(@EnumWindowsProc, integer(@param));
if (param.winHandele = 0) or not PostMessage(param.winHandele,
WM_CLOSE, 0, 0) or (GetLastError() = ERROR_INVALID_THREAD_ID) then
begin
// Anwendung kann nicht auf WM_CLOSE reagieren -> mit
TerminateProcess abschießen
TerminateProcess (ProcInfo.hProcess, 1);
end;

// Prozess wurde aufgefordert sich zu beenden - nun mal warten ob er es
auch wirklich macht
if (kill_after_timeout) then
begin
while (timeout>0) and (WaitForSingleObject(ProcInfo.hProcess,100) =
WAIT_TIMEOUT ) do
begin
timeout:=max(0,timeout-100);
application.ProcessMessages;
end;

// so was.. läuft ja immer noch.. dann eben wieder auf die
unfreundliche Art schließen
if GetExitCodeProcess(ProcInfo.hProcess, retCode) and
(retCode=STILL_ACTIVE) then
begin
// Benutzer gegebenenfalls erst fragen ob Prozess abgeschossen
werden soll
// if ... then
TerminateProcess (ProcInfo.hProcess, 1);
end;
end;
end;

end;



procedure TForm1.Button2Click(Sender: TObject);
var h: cardinal;
begin
CloseProcess(ProcInfo.dwThreadId, 2000, true);
// TerminateProcess(ProcInfo.hProcess,1);
end;


Gruß,
Andreas.
Borsdorf, Thomas
2007-05-10 08:06:49 UTC
Permalink
Hallo Andreas!
Nur Interesse halber:
Ist es irgendwie möglich festzustellen das der User "Speichern unter..."
angewählt hat?
Ist es vielleicht auch möglich den Zielpfad herauszubekommen?
Und das Ganze am besten noch bei WORD?
Post by Andreas
Gruß,
Andreas.
MfG Thomas.
Andreas
2007-05-10 14:27:21 UTC
Permalink
Post by Borsdorf, Thomas
Ist es irgendwie möglich festzustellen das der User "Speichern unter..."
angewählt hat?
Ist es vielleicht auch möglich den Zielpfad herauszubekommen?
Und das Ganze am besten noch bei WORD?
"irgendwie möglich" wohl schon...
Bei WORD könnte ich mir vorstellen, dass man so was abfragen kann..
Vielleicht über TWordApplication.. gibt vermutlich eine Dokumenteigenschaft
saved mit der man abfragen kann, ob das Dokument nach einer Änderung
gespeichert wurde.. den Dateinamen kann man vielleicht auch abfragen.. ob
es konkret was für "Speichern unter" gibt weiß ich aber nicht.
Vielleicht gibt es auch noch andere Schnittstellen zu WORD.. oder über ein
eigenes Makro / Plugin für Word.. habe mich damit aber noch nie
beschäftigt.

Eine Win-API Funktion zur Abfrage ob eine beliebige Anwendung gerade eine
geöffnete Daten gespeichert hat (insbesondere über ein "Speichern unter..")
gibt es nicht. - Würde ich jetzt zumindest mal behaupten.
Wenn man weiß, dass eine Anwendung immer an einer bestimmten Stelle was
speichert oder den Dateinamen kennt könnte man sich über Veränderung der
Datei benachrichtigen lassn (Vorraussetzung ist aber bei Speichern unter..
wohl kaum gegeben.. außer man überwacht alle Dateien auf die das
Zielprogramm schreibend zugreift bzw. die das Programm erstellt... kann dir
aber auch nicht sagen wie)
Beim Anklicken eines Menüpunktes wird normalerweise eine Message lokal in
der Anwendung verschickt.. wenn man deren ID kennt könnte man das Programm
belauschen.. Aber meist gibt es ja auch noch andere Möglichkeiten das
Speichern einzuleiten.. Tastenkombination, Makros, ... die zu
berücksichtigen sind. Den Dateinamen hat man dann aber immer noch nicht. Je
nach Anwendung könnte er in der Caption des Fensters stehen,...
Man könnte den API-Aufruf des Datei-Dialoges abfangen, sofern es einer der
Standarddialoge ist - oder, was wohl einfacher wäre, das System überwachen
ob ein Dialog mit dem entsprechenden Titel und gegebenenfalls weiteren
Merkmalen die man zuvor herausfinden muss geöffnet wird, setzt dann einen
MessageHook und holt sich beim Schließen des Dialogs (irgendwie) den
Dateinamen... oder...
Naja.. alles nur mal so ein paar allgemeine Überlegungen.

Andreas.
Julian Meurer
2007-05-11 12:09:16 UTC
Permalink
Hallo Andreas,
vielen Dank, das funzt wunderbar, auch wenn ich nur die Hälfte davon
verstehe :-))
Jul

Loading...