Entwickler-Ecke
Windows API - Programm anhand Speicherpfad finden und beenden
Xabitire - Mi 03.06.09 16:53
Titel: Programm anhand Speicherpfad finden und beenden
Hi Leute,
ich versuche grad ein laufendes Programm anhand seines Speicherpfades zu finden und dann zu Beenden. Meine bisherige Vorgehensweise war, dass ich zunächst alle Prozesse durchgehe und den zugehörigen Speicherpfad des Prozesses mit dem gegebenen zu vergleichen und somit erst einmal die entsprechende ProzessID herauszufinden. Das klappt so weit auch ganz gut. Jetzt steh ich allerdings vor dem Problem, dass ich von der Id zum Fenster kommen muss, damit ich diesem eine WM_Close-Nachricht schicken kann. Das will aber irgendwie nicht so, wie ich will.
Hier erst mal der relevante Code:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153:
| function IsWinXP: Boolean; begin Result := (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion = 5) and (Win32MinorVersion = 1); end;
function IsWin2k: Boolean; begin Result := (Win32MajorVersion >= 5) and (Win32Platform = VER_PLATFORM_WIN32_NT); end;
function IsWinNT4: Boolean; begin Result := Win32Platform = VER_PLATFORM_WIN32_NT; Result := Result and (Win32MajorVersion = 4); end;
function IsWin3X: Boolean; begin Result := Win32Platform = VER_PLATFORM_WIN32_NT; Result := Result and (Win32MajorVersion = 3) and ((Win32MinorVersion = 1) or (Win32MinorVersion = 5) or (Win32MinorVersion = 51)); end;
function GetRunningProcessesIDList(List: TStrings): Boolean; var SnapProcHandle: THandle; ProcEntry: TProcessEntry32; NextProc: Boolean; PIDs: array [0..1024] of DWORD; Needed: DWORD; I: Integer; begin if IsWin3X or IsWinNT4 then begin SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); Result := (SnapProcHandle <> INVALID_HANDLE_VALUE);
if Result then try ProcEntry.dwSize := SizeOf(ProcEntry); NextProc := Process32First(SnapProcHandle, ProcEntry); while NextProc do begin List.Add(IntToSTR(ProcEntry.th32ProcessID)); NextProc := Process32Next(SnapProcHandle, ProcEntry); end; finally CloseHandle(SnapProcHandle); end; end else begin Result := EnumProcesses(@PIDs, SizeOf(PIDs), Needed);
if Result then for I := 0 to (Needed div SizeOf(DWORD)) - 1 do List.Add(IntToStR(PIDs[i])); end; end;
function ProcessFileName(PID: DWORD): string; var Handle: THandle; begin Result := ''; Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID);
if Handle <> 0 then try SetLength(Result, MAX_PATH);
if GetModuleFileNameEx(Handle, 0, PChar(Result), MAX_PATH) > 0 then SetLength(Result, StrLen(PChar(Result))) else Result := ''; finally CloseHandle(Handle); end; end;
function IsFilePath(ProcessID: DWORD; Path: String): Boolean; var FileName: String; begin case ProcessID of 0: FileName := RsSystemIdleProcess; 2: if IsWinNT4 then FileName := RsSystemProcess else FileName := ProcessFileName(ProcessID); 8: if IsWin2k or IsWinXP then FileName := RsSystemProcess else FileName := ProcessFileName(ProcessID); else FileName := ProcessFileName(ProcessID); end;
Result := UpperCase(FileName) = UpperCase(Path); end;
function GetWindowHandle(ProcessID: DWORD; var Handle: THandle): Boolean;
function EnumerateThreadWindows(Wnd: HWND; Data: lParam): BOOL; begin Handle := Wnd; Result := False; end;
begin Result := EnumThreadWindows(ProcessID, @EnumerateThreadWindows, 0); end;
var LST: TStringList; PID: DWORD; i: Integer; Path: String; WindowHandle: THandle;
LST := TStringList.Create;
if GetRunningProcessesIDList(LST) then begin PID := INVALID_HANDLE_VALUE;
for i := 0 to LST.Count - 1 do if IsFilePath(StrToInt(LST[i]), Path]) then PID := StrToInt(LST[i]);
if PID <> INVALID_HANDLE_VALUE then if GetWindowHandle(PID, WindowHandle) then SendMessage(WindowHandle, WM_CLOSE, 0, 0) end; |
der momentane Code für GetWindowHandle liefert überhaupt keine Ergebnisse, der weggeklammerte hat zwar immer ein paar Fenster zurückgegeben, aber das wahren nie die richtigen! Hab zuerst gedacht, es wären Child-Fenster und dann versucht, mit GetParent an das HauptFenster zu kommen, hat aber auch nicht funktioniert. Wenn man allerdings das ganze 4-5 mal hintereinander ausführt, wird das richtige Fenster gefunden und es klappt so wie es soll, nur das bringt mir an sich nicht sonderlich viel! Weis solangsam nicht mehr was ich machen soll?
Habe es mit mehreren Programmen getestet, scheinbar immer das selbe Problem!
Weis jemand vielleicht, was ich falsch mache, oder geh ich das ganze insgesamt komplett falsch an?
Mit freundlichen Grüßen
Blackbird 8690
Regan - Mi 03.06.09 22:28
Xabitire hat folgendes geschrieben : |
Weis jemand vielleicht, was ich falsch mache, oder geh ich das ganze insgesamt komplett falsch an? |
Ob du was falsch machst, kann ich dir nicht beantworten. Ich würde den Weg anders gehen: Erst alle Fenster auflisten (
EnumWinProc), dann kansnt du den Pfad ermitteln, mittels des Wnd-Parameters. Danach noch über
GetWindowsLong das Parent bekommen und fertig. Das sollte dir schon erstmal weiterhelfen. Im Endeffekt musst du dir ja immer vor Augen halten, was du willst. Da bringen dir die Prozesse eigentlich gar nichts.
Xabitire - Do 04.06.09 10:29
Wie genau kann ich denn mit Hilfe des Wnd-Parameters den Pfad ermitteln? Diesbezüglich hab ich nämlich nichts gefunden und deshalb auch meinen Ansatz über die Prozesse gestartet!
GetWindowModuleFileName hab ich schon ausprobiert, aber das gibt mir bei jedem Fenster immer nur den Speicherpfad des eigenen Programms zurück!?
delphi10 - Do 04.06.09 13:52
Nimm doch einfach folgenden Code
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| Function TFormx.KillTask(ExeFileName: string): Integer; const PROCESS_TERMINATE = $0001; var ContinueLoop: BOOL; FSnapshotHandle: THandle; FProcessEntry32: TProcessEntry32; begin Result := 0; FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); FProcessEntry32.dwSize := SizeOf(FProcessEntry32); ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while Integer(ContinueLoop) <> 0 do begin if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) = UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) = UpperCase(ExeFileName))) then Result := Integer(TerminateProcess(OpenProcess(PROCESS_TERMINATE,BOOL(0),FProcessEntry32.th32ProcessID), 0)); ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32); end; CloseHandle(FSnapshotHandle); end; |
Du musst nur den Titel des Programmfensters übergeben.
jaenicke - Do 04.06.09 14:02
delphi10 hat folgendes geschrieben : |
Du musst nur den Titel des Programmfensters übergeben. |
Nein, da muss man den Namen der Exe übergeben. ;-)
Wozu im Code ganz oben die Prozess-ID ausgelesen wird, verstehe ich sowieso nicht so ganz. Dieser Quelltext ist da besser geeignet, macht aber im Grunde das selbe. Nur dass er nicht den Umweg über die Prozess-ID geht.
Regan - Do 04.06.09 15:05
Xabitire hat folgendes geschrieben : |
Wie genau kann ich denn mit Hilfe des Wnd-Parameters den Pfad ermitteln? Diesbezüglich hab ich nämlich nichts gefunden und deshalb auch meinen Ansatz über die Prozesse gestartet! |
Naja, du musst dann halt von dem Fenster den Prozess ermitteln. Und dann kannst du wieder den Pfad herausbekommen. Ungefähr dann so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| function GetProcessNameFromWnd(Wnd: HWND): string; var List: TStringList; PID: DWORD; I: Integer; begin Result := ''; if IsWindow(Wnd) then begin PID := INVALID_HANDLE_VALUE; GetWindowThreadProcessId(Wnd, @PID); List := TStringList.Create; try if RunningProcessesList(List, True) then begin I := List.IndexOfObject(Pointer(PID)); if I > -1 then Result := List[I]; end; finally List.Free; end; end; end; |
Xabitire - Do 04.06.09 22:10
So, ich habs jetzt!
Der Fehler war, dass bei der Funktion
EnumerateThreadWindows noch zusätzlich ein
Delphi-Quelltext
1:
| export; {$ifdef Win32} stdcall; {$endif} |
dahinter muss! Ansonsten funktioniert das anscheinend nicht!
@jaenicke und delphi10: Ich wollte das Programm eigentlich sauber beenden und nicht "killen"!
@Regan: Das ist ja an sich der selbe Weg wie meiner, nur von der anderen Seiter her angefangen! Allerdings gestaltet sich so die Abfrage ein wenig einfacher, deswegen werde ich es dann sehr wahrscheinlich auch so machen! :wink:
Mit freundlichen Grüßen
Felix Klein
delphi10 - Do 04.06.09 22:21
Xabitire hat folgendes geschrieben : |
So, ich habs jetzt!
@jaenicke und delphi10: Ich wollte das Programm eigentlich sauber beenden und nicht "killen"!
|
Ich hatte mal eine Procedure GetHase geschrieben, ich konnte machen was ich wollte, es kam kein Hase raus;-)
Windows macht nichts anderes, wenn man den Schließmuskel oben rechts anklickt.
jaenicke - Do 04.06.09 22:25
delphi10 hat folgendes geschrieben : |
Windows macht nichts anderes, wenn man den Schließmuskel oben rechts anklickt. |
Nein, das stimmt nicht, er hat schon Recht... :roll:
TerminateProcess bricht den Prozess ab. Wenn man auf schließen klickt, wird das Programm zuerst aufgefordert sich zu beenden. Erst wenn das nicht klappt, wird der Benutzer gefragt, ob es gekillt werden soll. ;-)
delphi10 - Do 04.06.09 23:17
Die DH meint zu TerminateProcess, dass
The state of the process object becomes signaled, satisfying any threads that had been waiting for the process to terminate. The process object is deleted when the last handle to the process is closed. Ich hoffe, dass ich nicht zuweit daneben liege.
Das hat auch immer so geklappt, ich sehe darin kein "unsauberes abschiessen oder killen" - genau das macht Windows nach der Abfrage selbst so, alle threads und kind's schließen und danach dann den Mainthread. Verwendet der Prozess aber DLL's, stimmt das so nicht mehr, aber davon war ja auch keine Rede. Hab aber trotzdem eben noch mal versucht eine Situation zu simulieren, in der sich der Process nicht mit dem Close-Button schließen lassen würde, habe ich aber auf die schnelle eben nicht hinbekommen.
jaenicke ich weiss aber, wer mir einen Tipp geben könnte :angel:
jaenicke - Do 04.06.09 23:26
delphi10 hat folgendes geschrieben : |
Hab aber trotzdem eben noch mal versucht eine Situation zu simulieren, in der sich der Process nicht mit dem Close-Button schließen lassen würde, habe ich aber auf die schnelle eben nicht hinbekommen. jaenicke ich weiss aber, wer mir einen Tipp geben könnte :angel: |
Setze einfach CanClose in OnCloseQuery auf False. ;-)
Und zu TerminateProcess...
TerminateProcess in der MSDN Dokumentation [http://msdn.microsoft.com/en-us/library/ms686714.aspx] hat folgendes geschrieben: |
The TerminateProcess function is used to unconditionally cause a process to exit. The state of global data maintained by dynamic-link libraries (DLLs) may be compromised if TerminateProcess is used rather than ExitProcess.
TerminateProcess initiates termination and returns immediately. This stops execution of all threads within the process and requests cancellation of all pending I/O. The terminated process cannot exit until all pending I/O has been completed or canceled.
A process cannot prevent itself from being terminated. |
Delete - Fr 05.06.09 00:18
delphi10 hat folgendes geschrieben : |
Du musst nur den Titel des Programmfensters übergeben. |
Deswegen heißt der Parameter auch
ExeFileName, weil man den Fenstertitel angibt und im Code wird keine einzige Funktion aufgerufen, die irgendwie bezug auf ein Fenster nehmen. :roll:
Und bezüglich deiner anderen Aussagen zu
TerminateProcess zum Beispiel, lässt mich vermuten, dass du dich entweder nicht richtig inmformiert hast oder nicht weiß, wo von du redest.
delphi10 - Sa 06.06.09 00:21
Luckie hat folgendes geschrieben : |
delphi10 hat folgendes geschrieben : | Du musst nur den Titel des Programmfensters übergeben. |
Deswegen heißt der Parameter auch ExeFileName, weil man den Fenstertitel angibt und im Code wird keine einzige Funktion aufgerufen, die irgendwie bezug auf ein Fenster nehmen. :roll:
Und bezüglich deiner anderen Aussagen zu TerminateProcess zum Beispiel, lässt mich vermuten, dass du dich entweder nicht richtig inmformiert hast oder nicht weiß, wo von du redest. |
Das mit dem Übergabenamen hatte ich schon weiter oben aufgeklärt, bzw. erklärt, dass ich mich da vertan hatte. Das man mal danebenliegt, kommt selbstverständlich nur bei mir vor. Jemanden zu unterstellen, er wisse nicht wo von er redet, empfinde ich auch in einem mehr oder weniger anonymen Forum als abwertend.
Wem hast Du mit Deinem kindischen Nachtreten jetzt weitergeholfen? Bin ich Dir irgendwie zu nahe getreten, oder warum pi... Du mich hier öffentlich an?
(Das ist nur eine rhetorische Frage auf die ich nicht wirklich eine Antwort erwarte.)Was hast Du außer diesem zitierten posting zur Lösung der Anfangsfrage beigetragen?
Zusätzlich bin ich einigermassen irritiert, da Du mir vor einigen Wochen mit einem Tipp zu HardLinks sehr weitergeholfen hast - in einem durch aus polemikfreien Kontext.
Edit: Das war
Garf, nicht-wie heißt er gleich nochmal-ach ja, lucki
Im Übrigen bin ich nach wie vor der Ansicht, dass TerminateProcess eine "offizielle" dokumentierte Funktion ist, die nach ihrer Anwendung im Normalfall keinen Programm- oder systemkritischen Zustand hinterlässt - wenn man Ausnahmen (DLL etc.) berücksichtigt, hatte ich aber auch schon oben erwähnt. Was daran ist also "bad", wenn man schnell und unkompliziert ein Problem lösen will? Letztlich ist es eine Frage von Aufwand und Nutzen ob Methoden eingesetzt werden, die alle Spitzfindigkeiten und Eventualitäten abdecken, durch die aber u.U. nach einem halben Jahr keiner mehr durchblickt.
Aber nun isses gut, ich würde es begrüßen, wenn dieser Thread geschlossen würde, da die Lösung ja erabeitet wurde.
jaenicke - Sa 06.06.09 00:43
delphi10 hat folgendes geschrieben : |
Im Übrigen bin ich nach wie vor der Ansicht, dass TerminateProcess eine "offizielle" dokumentierte Funktion ist, die nach ihrer Anwendung im Normalfall keinen Programm- oder systemkritischen Zustand hinterlässt - wenn man Ausnahmen (DLL etc.) berücksichtigt, hatte ich aber auch schon oben erwähnt. |
Das stimmt nicht, und wenn du in die Dokumentation schaust, wird dort auch davon abgeraten auf diese Weise einen Prozess zu beenden, und das zeigt ja auch schon der obige Text zu TerminateProcess... :roll:
Siehe z.B. hier für genauere Informationen:
Dokumentation zum Beenden von Prozessen... [http://msdn.microsoft.com/en-us/library/ms686722.aspx] hat folgendes geschrieben: |
[...]
If a process is terminated by TerminateProcess, all threads of the process are terminated immediately with no chance to run additional code. This means that the thread does not execute code in termination handler blocks. In addition, no attached DLLs are notified that the process is detaching. If you need to have one process terminate another process, the following steps provide a better solution:
[...] |
delphi10 - Sa 06.06.09 01:29
OK, sehe ich jetzt ein. Es gibt doch immer wieder Konstruktionen von denen man felsenfest überzeugt ist. Dabei ist oft noch nicht einmal das absolute Gegenteil richtig. Na gut, hamwa was gelernt, ist doch auch nicht schlecht.
cu del der jetzt dringend schlafen muss phi10
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!