Entwickler-Ecke

Windows API - Alle Kindfenster auflisten


DerNetteNachbar - Fr 12.12.08 14:58
Titel: Alle Kindfenster auflisten
Hallo an euch alle, wie der Titel schon etwas verrät suche ich eine API Funktion die mir alle Fenster bzw. Handles auflistet die vom Top Level Fenster bzw. TOP Prozess aufgerufen wurden.

MfG

Nachbar


Delete - Fr 12.12.08 15:02

Wenn ich den Threadtitel ins Englische übersetze, komme ich auf EnumChildWindows ;).


DerNetteNachbar - Fr 12.12.08 15:06

Da bekomme ich leider alle Fenster die vom System aufgerufen wurden. Ich brauche nur die die von meiner Anwendung aufgerufen wurden.


Delete - Fr 12.12.08 15:10

Versteh ich nicht. Du hast das jetzt aber nicht mit EnumWindows verwechselt?
Zitat:
The EnumChildWindows function does not enumerate top-level windows owned by the specified window, nor does it enumerate any other owned windows.


DerNetteNachbar - Fr 12.12.08 15:13

Könnt ihr mir vielleicht ein kleines Beispiel aus dem Ärmel schütteln. Irgentwie ist des alles etwas verwirrend.

MfG

Nachbar


Delete - Fr 12.12.08 15:18

Hab da etwas Interessantes bei den Schweizern [http://www.swissdelphicenter.ch/de/showcode.php?id=327] gefunden.


DerNetteNachbar - Fr 12.12.08 15:29

Irgentwie liefert mir diese Funktion EnumCHildWindows nichts als leere Strings zurück.


Delete - Fr 12.12.08 15:34

Wie verwendest Du sie denn? Meine Glaskugel ist gerade zum Polieren.


DerNetteNachbar - Fr 12.12.08 15:56

So verwenden ich diese Funktion:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
function EnumProc(wnd: HWND; Lines: TStrings): BOOL; stdcall;
var
  buf, Caption: array[0..255of char;
begin
  Result := True;
  GetClassName(wnd, buf, SizeOf(buf) - 1);
  SendMessage(wnd, WM_GETTEXT, 256, Integer(@Caption));
  Lines.Add(Format('ID: %d, ClassName: %s, Caption: %s',
           [GetDlgCtrlID(wnd), buf, Caption]));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 ListBox1.Clear; EnumChildWindows(Application.Handle, @EnumProc, Integer(ListBox2.Items));
end;


Delete - Fr 12.12.08 16:01

Zitat:

Delphi-Quelltext
1:
Lines.Add(...                    

Lines wovon?


DerNetteNachbar - Fr 12.12.08 16:08

Vom Typ TString, als linken Parameter kann man dann das Ergebnis als string darstellen lassen.


Delete - Fr 12.12.08 16:11

Ach, das war ein Übergabeparameter, sry. Ersetz doch mal Application.Handle durch self.Handle.


DerNetteNachbar - Fr 12.12.08 16:14

Ja dann liefert mir die Funktion alle Objekte die auf der Form sind. Keine andere Formulare.


Delete - Fr 12.12.08 16:18

Du willst die Fenster Deiner Anwendung auflisten? Dann wäre EnumThreadWindows wohl eher die richtige Funktion.


DerNetteNachbar - Fr 12.12.08 16:23

So das ist die Funktion die ich verwende: trotzdem werden mir nur leere Strings zurückgeliefert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function EnumerateThreadWindows(Wnd: HWND; Data: lParam): BOOL;
var
  WindowText: array[0..255of char; // holds the text of the window
  WindowText1: PChar;
begin
  { Get the text from the window }
  GetWindowText(Wnd, WindowText, 255);
  { Display it in the listbox}
  //ShowMessage(WindowText);
  Form1.ListBox1.Items.Add(WindowText);
  { Continue the enumeration }
  Result := True;
end;

procedure TRetForm.Button1Click(Sender: TObject);
begin
EnumThreadWindows(GetCurrentThreadID, @EnumerateThreadWindows, 0);
end;


Delete - Fr 12.12.08 16:38

Schau mal hier, da hatte jemand ein ähnliches Anliegen: http://www.delphipraxis.net/topic126465_fenstertext+per+processid+bekommen.html [http://www.delphipraxis.net/topic126465_fenstertext+per+processid+bekommen.html]


DerNetteNachbar - Fr 12.12.08 16:50

Hilft mir leider auch nicht weiter


Delete - Fr 12.12.08 16:51

Wie wäre es denn hiermit?

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 EnumerateThreadWindows(Wnd: HWND; Data: lParam): BOOL;stdcall;
var
  WindowText: string;
  pid: integer;
begin
  GetWindowThreadProcessID(Wnd,@pid);
  if IsWindowVisible(Wnd) and (pid = Data) then
    begin
      SetLength(WindowText,MAX_PATH);
      GetWindowText(Wnd, @WindowText[1], MAX_PATH - 1);
      SetLength(WindowText,StrLen(PChar(WindowText)));
      Form1.ListBox1.Items.Add(WindowText);
    end;
  Result := True;
end;


procedure TForm1.Button1Click(Sender: TObject);
var pid: integer;
begin
  GetWindowThreadProcessID(Handle,@pid);
  EnumWindows(@EnumerateThreadWindows, pid);
end;


DerNetteNachbar - Fr 12.12.08 17:00

Ich glaub die Funktion ist etwas unausgereift, denn mir meldet Delphi ne Menge Fehler.


Delete - Fr 12.12.08 17:01

Unter Delphi 5 läuft es. Welche Fehler meldet Delphi denn?


DerNetteNachbar - Fr 12.12.08 17:06


Delphi-Quelltext
1:
GetWindowThreadProcessID(Wnd,@pid);                    
Doppeldeutiger Überladener Aufruf von dieser Funktion


Delete - Fr 12.12.08 17:13

Dann musst Du mal die Codevervollständigung in Anspruch nehmen oder eine gute Stunde warten, ich kann hier mit Delphi 5 nicht nachsehen.


DerNetteNachbar - Fr 12.12.08 17:27

Wie geht denn eine Codevervollständigung


Delete - Fr 12.12.08 17:29

Gib mal GetWindowThreadProcessID( ein und warte ein wenig oder drücke CTRL - SPACE. Dann sollte ein Hintfenster mit möglichen Parametern erscheinen.


DerNetteNachbar - Fr 12.12.08 17:32

Ahso das meinst du, da hab ich vorhin schon getan, leider kommen da beim 2ten. Parameter zwei Möglichkeiten in Frage (Cardinal und Pointer) und keins der beiden scheint zu klappen.


Delete - Fr 12.12.08 17:36

Und wenn Du das @-Zeichen weglässt?


DerNetteNachbar - Fr 12.12.08 17:44

Schon alles erdenkliche versucht, nichts klappt......welche uses Klausel sollte eigentlich eingebunden werden.
Hab den Verdacht ,dass das ich nicht die richtige Klausel drin habe.


Delete - Fr 12.12.08 17:47

Nächster Versuch:

Delphi-Quelltext
1:
Windows.GetWindowThreadProcessID(Wnd,@pid);                    


Delete - Fr 12.12.08 17:57

Ich hab jetzt gleich Feierabend und versuch es zu Hause unter Delphi 2007 nochmal.


DerNetteNachbar - Fr 12.12.08 17:59

Gut das klappt ;-), man man das war vielleicht eine schwere Geburt.
Ich danke vorallem dem DeddyH das er soviel Gedult mit mir hatte ;-)......
Ich versuche mich zu bessern und nicht soviele Fragen zu stellen.

MfG Nachbar


Delete - Fr 12.12.08 18:43

Mir ist da noch ein anderer Ansatz eingefallen *g*

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure ListOwnForms(const sl: TStrings; OnlyVisible: Boolean = True);
var i: Integer;
begin
  sl.BeginUpdate;
  try
    for i := 0 to Screen.FormCount - 1 do
      if Screen.Forms[i].Showing or not OnlyVisible then
        sl.Add(Screen.Forms[i].Caption);
  finally
    sl.EndUpdate;
  end;
end;


DerNetteNachbar - Sa 13.12.08 14:26

Eigentlich sollte es ja möglichst mit API Funktionen gehen, da ich auch DLL Aufrufe aus meinen Programm erkennen muss. Und soviel ich weiss werden Forms aus DLL nicht als Child Fenster angezeigt? Oder gibt es da etwa doch einen Kniff?

MfG


Delete - Sa 13.12.08 14:31

Ach, es geht um DLLs, das muss ich überlesen haben. IIRC hat die DLL ein anderes Prozesshandle als die aufrufende Instanz, das stimmt schon.


DerNetteNachbar - Sa 13.12.08 14:46

Und was heisst das jetzt für mich im konkreten?


Delete - Sa 13.12.08 14:53

Du müsstest dann nicht das eigene Prozesshandle übergeben, sondern das der DLL.


DerNetteNachbar - So 14.12.08 22:54

Und wie stelle ich das an?


jaenicke - So 14.12.08 23:23

Ich nehme an du lädst die DLL mit LoadLibrary, und die Funktion gibt dir das Handle der DLL zurück.


DerNetteNachbar - Mo 15.12.08 10:21

Nein ich lade die DLL statisch, sprich ich binde diese einfach per external Klausel ein. Ich wüsste jetzt nicht partou wie ich das Handle der DLL rauskriegen würde.


Delete - Mo 15.12.08 11:25

Wenn es Deine eigene DLL ist, könntest Du sie eine entsprechende Funktion exportieren lassen.


DerNetteNachbar - Mo 15.12.08 12:31

Ich muss von einer DLL einer anderen das Handle der DLL mitteilen.


jaenicke - Mo 15.12.08 13:34

Wenn du LoadLibrary auf eine bereits in dein Programm geladene DLL aufrufst bekommst du einfach das Handle und ein Referenzzähler wird um 1 erhöht (der dann bei FreeLibrary wieder um 1 reduziert wird, damit die DLL entladen wird, wenn du nicht mehr darauf zugreifst). Ich gehe davon aus, dass das auch bei statisch eingebundenen DLLs so funktioniert.

Es gibt natürlich auch direkte Möglichkeiten die geladenen DLLs durchzugehen, aber das dürfte die einfachste Lösung sein.


DerNetteNachbar - Mo 15.12.08 13:38

Wie kann ich denn eine DLL identifizieren, z.B. anhand des Namens? Brauche wie ich schon sagte das Handle während der Laufzeit. Mit FindWindow geht es wohl schlecht da ich kein Fenster in der DLL nun mal habe.


jaenicke - Mo 15.12.08 13:55

Ja, wenn du es mir LoadLibrary nicht machen willst, dann sollte EnumProcessModules das richtige sein.


DerNetteNachbar - Mo 15.12.08 14:49

Kannst du vielleicht ein Beispiel posten damit ich einen Blick dafür habe?


jaenicke - Mo 15.12.08 14:56

Die Dokumentation:
http://msdn.microsoft.com/en-us/library/ms682631(VS.85).aspx
und ein Beispiel, zwar nicht in Delphi, aber es zeigt die Funktionsweise:
http://msdn.microsoft.com/en-us/library/ms682621(VS.85).aspx


DerNetteNachbar - Mo 15.12.08 16:29

Geht es vielleicht etwas leichter?


jaenicke - Mo 15.12.08 16:57

Ich habe LoadLibrary ja bereits vorgeschlagen, ich habe das nicht getestet, gehe aber wie gesagt davon aus, dass das funktioniert. Einfacher geht es nun wirklich nicht. Hast du das denn einmal ausprobiert?


DerNetteNachbar - Mi 17.12.08 10:56

Ja danke mit loadlibrary hat es geklappt das Handle der DLL zu kriegen. Nun sende ich mittels SendMessage Textnachrichten von der DLL an das Programm und vom Programm an die DLL. Der erste Weg klappt ganz hervorragend nur der zweite will irgentwie nicht. Ich habe das mit diesem Beispiel von den Schweizern realisiert:
http://www.swissdelphicenter.ch/de/showcode.php?id=163
Irgentwas mach ich falsch da ich mit der DLL nicht empfangen kriege.


MfG

Nachbar

Moderiert von user profile iconNarses: Link in URL-Tags gesetzt


jaenicke - Mi 17.12.08 12:43

Naja, in der DLL hast du vermutlich kein Fenster? Das kann auch ein leeres sein, um das zu erstellen gibts eine API-Funktion. Aber Nachrichten kann man nur mit Fensterhandles austauschen.

Oder wenn doch wie sieht dort denn der Code aus?

Aber wozu SendMessage? Hat das einen tieferen Sinn?


Delete - Mi 17.12.08 12:47

Nochmal zum Verständnis: ist das Deine eigene DLL oder eine fremde?


DerNetteNachbar - Mi 17.12.08 13:12

Das ist meine eigene DLL und ich habe auch Fenster drin die aufgerufen werden. Mit der oben erwähnten Funktion Receive empfange ich den String von der DLL. Nur leider klappt eben derselbe Mechanismus nicht wenn man zu dem Handle der DLL sendet. Übrigens in der SendMessage hab ich eben den String der gesendet wird, als eigene Message deklariert.


Delete - Mi 17.12.08 13:16

Dann frage ich mich aber, wieso Du die Kommunikation zwischen Anwendung und DLL über Messages abwickelst und nicht über exportierte DLL-Funktionen.


jaenicke - Mi 17.12.08 13:17

Ich habe sowas nie ausprobiert, aber du könntest ja direkt an die einzelnen Fenster schicken. Schließlich hast du die ja auflisten lassen.
Und das ist auch der Titel dieses Threads. Deine Fragen jetzt haben damit ja nicht mehr etwas zu tun, oder? Denn dieses Problem war ja gelöst.

Trotzdem frage ich mich warum du überhaupt SendMessage benutzt, das ist doch unnötig kompliziert. :gruebel:


DerNetteNachbar - Mi 17.12.08 13:28

Stimmt, sorry bin etwas vom Thema abgewischen. Theoretisch währe das Problem hiermit gelöst. Danke euch beiden für euere tatkräftige Unterstützung.

MfG

Nachbar