Entwickler-Ecke
Windows API - Zu Anwendung wechseln, Screenshot, wieder zurück
soong101 - Do 17.04.08 18:02
Titel: Zu Anwendung wechseln, Screenshot, wieder zurück
Hallo,
bisher hab ich nur einfache Sachen mit Delphi gemacht, für mein aktuelles Projekt muss es aber etwas tiefer gehen. Hab schon einige Threads hier gelesen und weiß nun ein paar Bruchstücke.
Ich will also ein bestimmtes Programm suchen, das im Hintergrund geöffnet ist. Von dem kenn ich die .exe-Datei, z.B. someapp.exe. (Ich weiß zwar auch den Titel des Fenster, aber der setzt sich zusammen aus "<GEÖFFNETE DATEI> - <APPNAME>" zusammen - und außerdem find ich die .exe-Variante schöner. Sollte das mit FindWindow() realisiert werden?
Ist die Anwendung nicht geöffnet, gibts nen Fehler (find ich selbst raus).
Wenn ich die Anwendung identifiziert habe, will ich sie in den Vordergrund holen und einen Screenshot machen. Das geht mit
Delphi-Quelltext
1:
| keybd_event(VK_SNAPSHOT,1,0,0); |
, oder?
Natürlich will ich dann wieder in mein Programm zurück, den Screenshot aus der Zwischenablage holen und bearbeiten, aber das ist ein anderes Problem.
Mir geht allerdings das Verständnis dafür ab, wie ich wieder zu meinem Programm zurück wechseln kann - denn wenn die andere Anwendung den Fokus hat, wie kann mein Programm überhaupt irgendwelche Befehle/Tastenkommandos ausführen? Ich hoffe ihr habt ein paar rücksichtsvolle Erklärungen für mich ;)
chrisdrury - Fr 18.04.08 08:04
Bin da jetzt nicht so der Experte, aber wenn ich Dich richtig verstanden habe, dann such mal hier im Forum nach Keyboard-Hook, global oder systemweit... :?
Ich meine, da mal einen oder mehrere passende Threads gesehen zu haben... :wink:
Delete - Fr 18.04.08 10:02
chrisdrury hat folgendes geschrieben: |
| Bin da jetzt nicht so der Experte, aber wenn ich Dich richtig verstanden habe, dann such mal hier im Forum nach Keyboard-Hook, global oder systemweit... :? |
Was soll er denn da mit einem Keyboardhook? :?
Du suchst dir das Fenster und nicht den Prozess. Da der Fenstertitel variable ist, kannst du nicht mit
FindWindow arbeiten. Stattdessen musst du dir mit
EnumWindows alle Fenster Handle holen, dann kannst du schon mal die nicht sichtbaren (
IsVisisble) ignorieren und holst dir nur von den Sichtbaren mit
WM_GETTEXT den Fenstertitel. Mit
pos guckst du, ob der Programmname im Fenstertitel auftaucht. Tut er das, hast du dein Fenster gefunden. Mit
BringWindowToTop holst du das Fenster nach vorne. Dann holst du dir mit
GetDC einen DC auf das Fenster und kopierst den Inhalt des DCs mit
BitBlt in ein Bitmap im Speicher. Dieses kannst du dann abspeichern. Fertig.
soong101 - Fr 18.04.08 11:25
Danke für die Wegweiser, bin nach einer Stunde auch schon etwas vorangekommen. Das ist meine EnumWindowsProc-Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function EnumWindowsProc(handle: HWND; lparam: LPARAM): Boolean; var wnd_title: String; begin result := true; if IsWindowVisible(handle) then begin SendMessage(handle,WM_GETTEXT,0,Integer(wnd_title)); result := pos('Editor',wnd_title)=0; end; end; |
So wende ich es an
Delphi-Quelltext
1:
| EnumWindows(@EnumWindowsProc, 0 ); |
Beim Compilieren bekomme ich dann diesen Fehler, der sich auf eben diese Zeile bezieht. Der Cursor steht hinter dem ",".
Quelltext
1:
| E2036 Variable erforderlich |
Was mache ich falsch?
chrisdrury - Fr 18.04.08 13:22
Luckie hat folgendes geschrieben: |
Was soll er denn da mit einem Keyboardhook? :?
|
Hhhmm, manchmal hilft gründliches Lesen doch weiter... :shock:
Hatte mich halt irgenwie auf's Keyboard eingeschossen... :oops:
Delete - Fr 18.04.08 13:53
soong101 hat folgendes geschrieben: |
So wende ich es an
Delphi-Quelltext 1:
| EnumWindows(@EnumWindowsProc, 0 ); |
Beim Compilieren bekomme ich dann diesen Fehler, der sich auf eben diese Zeile bezieht. Der Cursor steht hinter dem ",".
Quelltext 1:
| E2036 Variable erforderlich |
Was mache ich falsch? |
Du guckst nicht in die Hilfe:
http://msdn2.microsoft.com/en-gb/library/default.aspx bzw. du guckst nicht hin, wenn die Programmierhilfe im Editor aufgeht.
jaenicke - Fr 18.04.08 15:26
Also ich würde sagen, der von dir gepostete Quelltext ist vollkommen korrekt, der Fehler liegt woanders.
Poste doch mal die gesamte Unit, vielleicht ist dann zu sehen was da falsch läuft.
// EDIT:
Folgender Quelltext lässt sich bei mir problemlos kompilieren, der Fehler beim Kompilieren erstmal ist nicht da drin:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| function EnumWindowsProc(handle: HWND; lparam: LPARAM): Boolean; var wnd_title: String; begin result := true; if IsWindowVisible(handle) then begin SendMessage(handle,WM_GETTEXT,0,Integer(wnd_title)); result := pos('Editor',wnd_title)=0; end; end;
procedure TForm75.Button1Click(Sender: TObject); begin EnumWindows(@EnumWindowsProc, 0); end; |
Was das Result dort macht, weiß ich jetzt nicht, aber kompilieren lässts sich erstmal.
// EDIT2:
Ich hab grad mal gelesen was bei MSDN steht, also das lässt sich nur kompilieren, wird aber nicht das machen was du willst denke ich...
Oder willst du nur das erste Fenster mit Editor im Namen finden?
Zweitens machst du dann gar nix, merkst also gar nix davon, dass was gefunden wird.
Delete - Fr 18.04.08 15:32
Richtig, der zweite Parameter ist ein Zeiger. Du hast dort aber einfach eine Zahl angegeben und das ist definitiv kein Zeiger. Also entweder übergibst du einen Zeiger auf eine Datenstruktur oder alternativ eben
nil.
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:
| type PMyEnumParam = ^TMyEnumParam; TMyEnumParam = record lb: TListbox; end;
function GetWindows(const hWnd: Longword; Param: PMyEnumParam): LongBool; stdcall; var Len: Longint; S: string; begin Result := True; if not (IsWindow(hWnd) and IsWindowVisible(hWnd)) then Exit; Len := SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); if Len > 0 then begin SetLength(S, Len); SendMessage(hWnd, WM_GETTEXT, Len + 1, Longint(Pchar(S))); Param.lb.Items.Add(s); end; end;
procedure TForm1.Button1Click(Sender: TObject); var Param: TMyEnumParam; begin Param.lb := Listbox1; EnumWindows(@GetWindows, LPARAM(@Param)); end; |
soong101 hat folgendes geschrieben: |
| Na komm, ich hab ja schon einige Zeit gebraucht um rauszufinden, dass die Funktion "IsWindowVisible" heißt statt "IsVisible" ;) |
Sorry, mein Fehler.
soong101 - Fr 18.04.08 16:11
In der Popuphilfe stand Integer als Typ des zweiten Parameters - wusste nicht, dass das ein Pointer ist. Seis drum, ich habe dort tatsächlich nil geschrieben, um mögliche Fehler zu vermeiden. Es kommt immer noch dieser Fehler, an derselben Stelle.
Ich poste mal noch den Prozedurkopf, aber ich denke nicht, dass die gesamte Unit hilfreich wäre - da steht jede Menge anderer Code der nichts mit diesem Problem zu tun hat.
Die Anwendung (an der Funktion hat sich nichts geändert):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TFmain.Button1Click(Sender: TObject); var wnd_this, wnd_other: hwnd; wnd_dc: HDC; test: boolean;
begin
EnumWindows(@EnumWindowsProc, nil);
... |
Zu der Funktion noch: Ich weiß, was sie bei Result = TRUE/FALSE macht. Es ist so: Ist pos(substr,String) = 0, also der gesuchte String nicht im Titel des Fensters, such weiter. So soll das doch sein? Mir ist nur noch nicht klar, wie dann zum Schluss das Handle auf das gesuchte Fenster bekomme...
Delete - Fr 18.04.08 16:33
Mist schon wieder falsch. Integer ist natürlich richtig. Und wenn du einfach meinen Code mal kopierst und entsprechend anpasst?
soong101 - Fr 18.04.08 17:05
Dein Code funktioniert einwandfrei. Ich hab auch meinen dahingehend abgewandelt, dass er ein Record benutzt, aber der Fehler bleibt.
Ich hab mal deinen Code geändert und vereinfacht, jetzt gehts. Wurmt mich aber doch, dass ich den Fehler nicht finde...
Egal, jetzt kann ich mich mal an DC usw. machen. Das war bestimmt nicht die letzte Frage ;)
soong101 - Fr 18.04.08 19:24
Der angepasste Code kompiliert zwar, tut aber nicht das, was er soll - Bei Klick auf Button1 wird das Fenster 'Unbenannt - Editor' nicht nach vorne geholt. Also auf ein Neues:
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:
| function GetWindows(handle: Longword; Param: LParam): LongBool; var wnd_title: string; begin Result := True; if IsWindowVisible(handle) then begin SendMessage(handle,WM_GETTEXT,0,LPARAM(@wnd_title)); if pos('Editor',wnd_title)<>0 then begin result := false; wnd_other := handle; end; end; end;
procedure TFmain.Button1Click(Sender: TObject); var wnd_this: hwnd;
begin
wnd_this := Application.Handle; EnumWindows(@GetWindows, 0); BringWindowToTop(wnd_other);
... end; |
wnd_other ist global deklariert. Die meiner Meinung nach kritische Stelle hab ich mal unterlegt.
Delete - Fr 18.04.08 21:52
Dann vergleich noch mal die zeilen von meinem Code in denen ich den Fenstertitel hole mit deinem Code. Ich hole mir nicht zum Spass zu erst danzahl der Zeichen des Fenstertitels. ;)
soong101 - Sa 19.04.08 11:29
Luckie hat folgendes geschrieben: |
| Dann vergleich noch mal die zeilen von meinem Code in denen ich den Fenstertitel hole mit deinem Code. Ich hole mir nicht zum Spass zu erst danzahl der Zeichen des Fenstertitels. ;) |
Aber warum dann? Angenommen, der Titel eines Fensters ist leer, also müsste WM_GETTEXT einen leeren String zurückliefern. Dann wäre pos(substr,string)=0 und das Fenster kommt nicht in Frage.
Wann gilt für ein Fenster eigentlich isWindowVisible? Wenn es nicht ganz verdeckt ist? Oder schon wenn es nicht minimiert ist. Und bitte kein Link zu MSDN als Antwort, denn das hab ich mir schon durchgelesen.
Motzi - Sa 19.04.08 12:40
Kleiner Hinweis nebenbei: deine GetWindows Funktion ist eine Callback-Funktion die von der API-Funktion aus aufgerufen wird, daher muss sie auch die korrekte Aufrufkonvention stdcall verwenden. In Luckies Code ist das richtig drinnen, bei deinem vermisse ich das allerdings.
IsWindowVisible liefert false zurück wenn das Fenster versteckt ist - das hat nichts damit zu tun ob es minimiert oder von anderen Fenstern verdeckt ist.
soong101 - Sa 19.04.08 14:01
Motzi hat folgendes geschrieben: |
Kleiner Hinweis nebenbei: deine GetWindows Funktion ist eine Callback-Funktion die von der API-Funktion aus aufgerufen wird, daher muss sie auch die korrekte Aufrufkonvention stdcall verwenden. In Luckies Code ist das richtig drinnen, bei deinem vermisse ich das allerdings.
IsWindowVisible liefert false zurück wenn das Fenster versteckt ist - das hat nichts damit zu tun ob es minimiert oder von anderen Fenstern verdeckt ist. |
Dankeschön! Und ich möchte es nochmal verdeutlichen: Ich hab in diesem Bereich keine Erfahrung, deswegen ist dieser kleine Hinweis ein Riesenhinweis. Seid ruhig weiter so direkt!
:( stdcall hilft auch nichts. Liegt es vielleicht am übergebenen handle?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| function GetWindows(handle: Longword; Param: LParam): LongBool; stdcall; var wnd_title: string; begin Result := True; if IsWindowVisible(handle) then begin if SendMessage(handle,WM_GETTEXTLENGTH,0,0) > 0 then begin SendMessage(handle,WM_GETTEXT,0,LPARAM(@wnd_title)); if pos('Editor',wnd_title)<>0 then begin result := false; wnd_other := handle; end; end; end; end; |
(aktuelle Version)
soong101 - Do 24.04.08 19:49
So, ein paar Tage sind verstrichen und ich habs selber hinbekommen. Ausgehend von Luckies Version finde ich jetzt genau das Fenster, das ich haben will.
Das nächste Problem ist nun, das Childwindow zu bekommen, denn das Fenster ist aufgeteilt in eine Frame links (Werkzeugleiste) und eine große Fläche rechts (Arbeitsbereich). Von letzterer möchte ich nun einen DC. Mit Luckies WindowInfo2 hab ich den Namen des Fenster rausbekommen, und hab mir eine Callbackfunktion geschrieben, die mir dann das Handle auf eben diesen Arbeitsbereich liefern soll. Die Rückgabe ist aber immer 0.
Die neue GetWindows-Funktion:
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:
| function GetWindows(const hWnd: Longword; Param: LParam): LongBool; stdcall; var Len: Longint; S: string; begin Result := TRUE; if not (IsWindow(hWnd) and IsWindowVisible(hWnd)) then Exit; Len := SendMessage(hWnd, WM_GETTEXTLENGTH, 0, 0); if Len > 0 then begin SetLength(S, Len); SendMessage(hWnd, WM_GETTEXT, Len + 1, Longint(Pchar(S))); if pos('EAW Terrain Editor',S)<>0 then begin wnd_other := hWnd; eaw_mode := TRUE; filename_map := Copy(S,0,Len-21); Result := FALSE; end else if pos('FOC Terrain Editor',S)<>0 then begin wnd_other := hWnd; eaw_mode := FALSE; filename_map := Copy(S,0,Len-21); Result := FALSE; end; end; end; |
Die GetChildWindows-Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| function GetChildWindows(const hWnd: Longword; Param: LParam): LongBool; stdcall; var S: string; begin Result := TRUE; GetClassName(hWnd,Pchar(S),100); if S='AfxFrameOrView70s' then begin wnd_child := hWnd; Result := FALSE; end;
end; |
Leider hab ich für den Classnamen nichts entsprechendes zu WM_GETTEXTLENGTH gefunden.
Die Anwendung:
Delphi-Quelltext
1: 2: 3: 4:
| EnumWindows(@GetWindows, 0); ShowWindow(wnd_other,SW_MAXIMIZE); EnumChildWindows(wnd_other,@GetChildWindows,0); ShowWindow(wnd_child,SW_MAXIMIZE); |
Die Idee ist doch richtig oder? Sieht jemand einen Fehler?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 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!