![]() |
Prozess-Handle vs. Fenster-Handle
Nabend. Ich hab folgendes Problem:
Ich habe die PID eines Prozess und will nun das Handle des Hauptfensters, sodass ich dieses z. B. in ShowWindow verwenden kann. Wie kann ich das machen? Ich kann mir ja mit OpenProcess ein Handle auf den Prozess holen, aber mit diesem Handle kann ich das Hauptfenster ja nicht verändern. Wäre super, wenn ihr mir helfen könntet. |
Wenn du dir die ThreadID vom Prozess besorgen kannst, dann kannst du mit EnumThreadWindows alle Hauptfenster des Prozesses ermitteln.
|
Hmm, leider krieg ich das nicht hin. Ich habs jetzt mal so gemacht, dass ich die PID, die ich über einen Snapshot und dann die Process32First bzw -Next-Funktionen bekommen mit th32OwnerProcessID von einem TThread32Entry vergleiche, solange, bis es zu einer Übereinstimmung kommt. Das tut es aber leider nicht. :( Wie muss ich sowas richtig machen?
Edit: Das tuts doch. Meine Funktion, die das erledigt, war wohl falsch. Jedenfalls habe ich jetzt die ThreadID (ich glaub zumindest, dass es die Richtige ist). Ich wag mich jetzt mal an EnumThreadWindow ran. |
Ne, ich versteh das bei EnumThreadWindows nicht. Variablen-Pointer sind mir inzwischen zwar geläufig, aber Funktions-Pointer sind für mich leider noch ne andere Welt. Kann mir da einer helfen?
|
Mal ein Beispiel:
Code:
function EnumThreadWndProc(wnd: HWND; Items: TStrings): Boolean; stdcall;
var Buf: array[0..1024] of Char; WndCaption: string; begin SetString(WndCaption, Buf, GetWindowText(wnd, Buf)); Items.Add(WndCaption); Result := True; // nächstes Fenster; end; begin EnumThreadWindows(ThreadId, @EnumThreadWndProc, Integer(ListBox.Items)); end; |
Tut mir leid, dass ich noch weiter frage, aber was bedeutet
Code:
?
Integer(ListBox.Items)
Ich versteh das mit dem Zitat:
|
"application-defined value" bedeutet, dass du damit machen kannst was du willst, sollange es 4 Bytes nicht überschreitet (32Bit Integer). Mit Integer(ListBox.Item) übergebe ich die ListBox.Items: TStrings als Integer. Da ListBox.Items ein Zeiger auf eine TStringList-Instanz ist, und Zeiger 4 Bytes groß sind, konvertiere (typecast) ich diesen Zeiger in einen 32Bit Integer, den ich als "application-defined value" übergebe. Dieser Wert wird dann an die Callback-Funktion (EnumThreadWndProc) als zweiter Parameter übergeben.
|
Hmm, ich glaube, ich hab oben was Falsches gesagt. Ich glaub, ich habe die ThreadID doch nicht. Ich schreib mal auf, wie ich denk, wie man die ThreadID über den Namen und Pfad der ausführbaren Datei kommt.
1. Erstellung eines System-Snaphshots mit CreateToolhelp32Snapshot 2. Ermittlung der ProcessID aller Prozesse über Process32First und Process32Next mit dem angegebenen Dateinamen. 3. Ausführen von Module32First und Module32Next. Vergleich jeder ProcessID mit der Liste der ProcessIDs von 2. Wenn ID übereinstimmt, heißt das, der Name des ausgeführten Programms ist gleich. Dann mit Hilfe von szExePath die endgültig gesuchte ProcessID herausfinden. 4. Ausführen von Thread32First und Thread32Next. Sichern aller ThreadIDs, die als th32OwnerProcessID die ProcessID von 3 haben. Somit hat man die ThreadIDs aller Threads des gewünschten Prozesses. 5. Mit EnumThreadWindows die Fenster aller Threads durchgehen, solange, bis das Hauptformular gefunden wurde. Problem: Wie kann man dieses erkennen? 6. Nun hat man das Fensterhandle und kann mit diesem machen, was man will :D Ist nur ein reichlich komplizierter (zumindest für nen WinAPI-Anfänger) Weg. Gibts einen einfacheren Weg? Eine Verkürzung wäre, wenn man den Hauptthread herausfinden könnte. Dann müsste man bei 5 nur einmal das EnumThreadWindow-Gedingsel durchgehen. Dazu wichtig die Frage: In einem Prozess mit mehreren Threads, ist das Hauptfenster überhaupt immer in dem Hauptthread? Bzw. gibt es eigentlich einen Hauptthread und davon abgeleitete Threads oder kann es mehrere "Hauptthreads" geben? Fragen über Fragen, wäre schön, wenn mir jemand darauf Korrekturen, Tips oder was auch immer geben könnte. Nur mal so nebenbei: Die theoretische Vorgehensweise hab ich mit Hilfe des Windows SDK und den Antworten hier von jbg entwickelt und das ist die beste Lernmethode. Das in Code umzusetzen ist zweitrangig. |
Es gibt noch einen anderen Weg. Nämlich andersherum vorgehen.
Du ermittelst mit EnumWindows() alle Hauptfenster und kannst dann mit Hilfe der API GetWindowModuleFileName (erst ab Win98) den Dateinamen der Anwendung sowie mit der API GetWindowThreadProcessId das Process Handle herausfinden. |
OK, werd ich mal versuchen. Aber würde der von mir beschriebene Weg funktionieren?
|
Dein Weg kann theoretisch auch funktionieren. Man weiß ja nie wie Windows reagiert. :D
|
Na ja, wenigstens etwas. Aber heut abend nicht mehr, ich hab zu viel getrunken, um das noch hinzukriegen und zu wenig, um es problemlos zu programmieren :mrgreen:
|
Ich glaub, ich bin zu blöd, um EnumWindows zu benutzen. Folgenden Code benutze ich zum Testen:
Code:
und
function EnumWindowsProc(Hnd: HWND; Lines: TStrings): Boolean; stdcall;
var FileName: Array[0..255] of Char; begin GetWindowModuleFileName(Hnd, @FileName, 255); Lines.Add(InttoStr(Hnd) + ': ' + FileName); if FileName = AppName then begin Result := False; end else begin Result := True; end; end;
Code:
Doch komischerweise kommen in dieser Liste immer die gleichen Handles vor. Wenn ich die in einem ShowWindow-Aufruf verwende, veränder ich nicht das Fenster, das ich gewollt hab (also das meines Programmes), sondern irgendein anderes, meistens eins von meinem AntiVirus-Programm. Was heißt eigentlich genau "top-level-window"? Weil das, was ich gedacht habb, dass es heißt, kann es wohl nicht heißen, denn sonst hätt ich wohl kaum 30 dieser Fenster mit dem gleichen Pfad und Dateinamen gefunden.
AppName := ParamStr(0);
EnumWindows(@EnumWindowsProc, Integer(Form1.Memo1.Lines)); |
top-level-window = Hauptfenster der Anwendung.
mit diesem Code kannst du herausfinden welches deiner Fenster du gefunden hast:
Code:
var
Buf: array[0..1024] of Char; ClassName: string; begin SetString(ClassName, Buf, GetClassName(Hnd, Buf, 1024)); if ClassName = 'TApplication' then ... else if ClassName = 'TForm1' then ... else if ClassName = 'TForm2' ... end; |
So, nachdem ich schon aufgegeben hatte, hab ich jetzt doch mal weiter gemacht. Auch wenn ich es sehr seltsam finde, dass ich in meiner Anwendung auch Handles, die laut ClassName zu WinAmp gehören, finde, scheint eine Sache zu funktionieren: Wenn ich das Handle von 'Form1' als Parameter in ShowWindow einsetze, funktioniert es. Das Problem ist nur, dass es jeweils zwei Handles zu TForm1, TApplication etc. gibt.
Falls es jemanden interessiert, hier soweit der Code der Callback-Funnktion:
Code:
Auf der Form befindet sich z. B. eine Listbox. Folgendermaßen wird die Callback-Funktion aufgerufen:
function EnumWindowsProc(Hnd: HWND; Lines: TStrings): Boolean; stdcall;
var FileName: Array[0..255] of Char; ClassName: String; Buf: Array[0..1023] of Char; begin GetWindowModuleFileName(Hnd, @FileName, 255); if FileName = AppName then begin SetString(ClassName, Buf, GetClassName(Hnd, Buf, 1024)); if Classname = 'TForm1' then begin Lines.Add(Classname + ':' + InttoStr(Hnd)); Result := False; ShowWindow(hnd,SW_MAXIMIZE); end; end else begin Result := True; end; end;
Code:
Jetzt versuch ich das gleiche Mal für Anwendungen, die nicht in Delphi geschrieben sind.
AppName := ParamStr(0);
EnumWindows(@EnumWindowsProc, Integer(Listbox1.Items)); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:03 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