Entwickler-Ecke

Windows API - Fenstersuche


FinalFantasy - Di 20.09.05 15:46
Titel: Fenstersuche
Hi,

Ich bin auf der Suche nach allen Fenstern auf meinem Desktop. FindWindow liefert mir aber Tausende von Handles die zu irgendwelchen Sachen gehören... Jetzt bin ich schonmal auf die Idee gekommen, alle Fenster die Parentfenster haben oder bei denen length(caption) = 0 zutrifft, auszufiltern. Das hat meine Liste schonmal deutlich minimiert, aber ich finde immernoch sehr viele Handles, die offensichtlich nicht zu einem richtigem Fenster gehören.
So, wie kann ich jetzt die Fenster rausfiltern, die wirklich ein Fenster sind (also eigentlich alles, was auch bei Alt+Tab auftaucht, nicht mehr und nicht weniger), und zusätzlich müsste ich noch rausfinden, ob die Fenster auf Always on Top gesetzt sind....
Hat jemand eine Idee, wie man das machen kann/könnte?

Achja, und das Fenster meiner Applikation sollte unsichtbar sein (also auch nicht in der Taskleiste auftauchen), die Visible-Property des Forms scheint Delphi aber nicht sonderlich zu interessieren, und im OnCreate kann ich ja Hide() noch nicht aufrufen, weil das Fenster ja noch gar nicht existiert....


matze - Di 20.09.05 15:57

Wenn du 2 Fragen hast, dann stell bitte jede Frage in einem seperaten Topic.


FinalFantasy - Di 20.09.05 16:20

Ja, sorry.

EnumWindows scheint genau das zu sein, was ich Suche.
Allerdings krieg ich bei dem Aufruf der Funktion den Fehler "Variable erforderlich":

Delphi-Quelltext
1:
  EnumWindows(@EnumWinProc, 0);                    


EnumWinProc sieht dabei so aus:

Delphi-Quelltext
1:
function TfrmOnTopControl.EnumWinProc(Wnd: THandle; LParam: LongInt): Boolean; stdcall;                    


Ist also eine Memberfunktion der Klasse.
Bekomme aber den selben Fehler, wenn ich es als Funktion deklariere.

Das ganze stammt von hier: http://www.delphi-forum.de/viewtopic.php?t=39080&highlight=enumwindows


retnyg - Di 20.09.05 19:20

nimm Suche im MSDN ENUMDESKTOPWINDOWS, damit kriegst du alle top-level windows.
die enumwindowsproc darf übrigens kein mitglied einer klasse sein.


Motzi - Di 20.09.05 19:26

EnumWindows liestet auch nur Top-Level-Fenster auf, aber nicht jedes Top-Level-Fenster ist auch automatisch in der Taskleiste bzw im Alt-Tab-Fenster.
EnumDesktopWindows ist was ganz andres - hier werden die Fenster eines bestimmten Desktops aufgelistet, wobei ein "Desktop" hier ein absolut andere Bedeutung als im üblichen Sprachgebrauch hat, hier ist ein Desktop nämlich ein Security-Objekt - ein Teil einer WindowStation.

Gruß, Motzi


retnyg - Di 20.09.05 19:31

user profile iconMotzi hat folgendes geschrieben:

EnumDesktopWindows ist was ganz andres...

ok, und was würdest du vorschlagen ? ich habe das mal so gemacht:

Delphi-Quelltext
1:
2:
3:
4:
//style: integer
    style:=getwindowlong(wnd,GWL_STYLE);
    if (style and WS_VISIBLE) <> 0 then           //inttostr(style) + ',' +
    if (style and WS_SYSMENU) <> 0 then

das liefert ganz brauchbare ergebnise, aber es entspricht nicht genau dem resultat der taskbar.


Motzi - Di 20.09.05 20:08

Mit GetWindowLong() bist du schon am richtigen Weg, allerdings solltest du statt GWL_STYLE GWL_EXSTYLE nehmen und auf WS_EX_APPWINDOW testen...


retnyg - Di 20.09.05 21:21

das funktioniert nicht wirklich.
nimm z.b. den VLC mediaplayer (0.8.2) , der hat folgende Styles:

Quelltext
1:
2:
3:
4:
5:
WS_OVERLAPPEDWINDOW
WS_VISIBLE
WS_CLIPSIBLINGS
WS_CLIPCHILDREN
WS_OVERLAPPED


Extended:

Quelltext
1:
2:
3:
4:
WS_EX_LEFT
WS_EX_LTRREADING
WS_EX_RIGHTSCROLLBAR
WS_EX_WINDOWEDGE


von WS_EX_APPWINDOW keine spur, trotzdem ists in der taskleiste drin ^^


FinalFantasy - Mi 21.09.05 08:43

Habs jetzt EnumWindows zum laufen gebracht... allerdings findet EnumWindows auch viel zu viele Fenster, ähnlich wie FindWindow...

GetWindowsLong findet dagegen kein einziges Fenster mit der WS_VISIBLE-Eigenschaft, aber da sind definitiv welche da!!

Mit WS_EX_APPWINDOW wird mein Feierabendcountdown gefunden (der hat zwar ein Fenster, ist aber ins Systemtray minimiert), Outlook und die MSDN werden gefunden... Opera, Delphi, Delphi-Hilfe und sich selbst findet es damit jedoch auch nicht....

Was für ein gefummel...

[EDIT]:

Sorry, war mein Fehler... die Flags von GWL_STYLE sind nicht in GWL_EXSTYLE enthalten.... logisch, dass kein Fenster WS_VISIBLE hat, wenn ich GetWindowLong mit GWL_EXSTYLE aufrufe...
Ich hole mir jetzt beide Styles, weil ich gesehen hab, dass im Ex-Style die TopMost Eigenschaft drin steckt... (womit eine weitere Frage beantwortet wäre)
WS_VISIBLE liefert (wenn man es richtig macht :-)) doch ganz brauchbare Ergebnisse, da ich ja nur die Fenster haben will, die man auch sehen kann *gg*


Motzi - Do 22.09.05 14:17

Ok, du hast recht, nicht jedes Fenster das in der Taskbar ist hat WS_EX_APPWINDOW gesetzt, aber jedes Fenster das WS_EX_APPWINDOW gesetzt hat, ist auch in der Taskbar! ;)
Weiters - jedes Fenster, das WS_EX_TOOLWINDOW gesetzt hat ist nicht in der Taskbar.

Ich hab mal einen bisschen Code von mir zusammengesucht und mal das zusammengeklickt:

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:
function GetWindowText(wnd: Hwnd): String;
var
  dwResult: DWord;
  pBuffer: PChar;
begin
  SetLastError(0);
  if SendMessageTimeOut(wnd, WM_GETTEXTLENGTH, 00, SMTO_NORMAL,
    750, dwResult) = 0 then
  begin
    if GetLastError = ERROR_TIMEOUT then
      Result := '<SendMessage timed out>'
    else
      Result := '<SendMessage failed>';
    Exit;
  end;
  Inc(dwResult);

  pBuffer := GetMemory(dwResult);
  try
    if SendMessageTimeOut(wnd, WM_GETTEXT, dwResult, Integer(pBuffer),
      SMTO_NORMAL, 750, dwResult) = 0 then
    begin
      if GetLastError = ERROR_TIMEOUT then
        Result := '<SendMessage timed out>'
      else
        Result := '<SendMessage failed>';
    end
    else
      Result := String(pBuffer);
  finally
    FreeMemory(pBuffer);
  end;
end;


function GetClassName(wnd: Hwnd): String;
var
  szBuffer: array [0..255of Char;
  iCount: Integer;
begin
  ZeroMemory(@szBuffer, SizeOf(szBuffer));
  iCount := Windows.GetClassName(wnd, szBuffer, SizeOf(szBuffer));
  if wnd = GetDesktopWindow then
    StrPCopy(@szBuffer[iCount], 'Desktop');

  Result := String(szBuffer);
end;

function enumProc(wnd: Hwnd; Listbox: TListBox): Boolean; stdcall;
var
  dwStyle: DWord;
  bAdd: Boolean;
begin
  Result := true;
  if IsWindowVisible(wnd) then
  begin
    bAdd := True;
    dwStyle := GetWindowLong(wnd, GWL_STYLE);
    bAdd := bAdd and (dwStyle and WS_POPUP = WS_POPUP);

    dwStyle := GetWindowLong(wnd, GWL_EXSTYLE);
    bAdd := bAdd or (dwStyle and WS_EX_APPWINDOW = WS_EX_APPWINDOW);
    bAdd := bAdd and not (dwStyle and WS_EX_TOOLWINDOW = WS_EX_TOOLWINDOW);

    if bAdd then
      Listbox.Items.Add('0x' + IntToHex(wnd, 8) + ' - ' + GetClassName(wnd) +
      ' - ' + GetWindowText(wnd));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  EnumWindows(@enumProc, Integer(ListBox1));
end;


Das funktioniert schon ganz gut, man muss es aber noch ein bisschen verfeinern, bei mir findet er alle Fenster bis auf den Firefox.

Gruß, Motzi