| Autor |
Beitrag |
soong101
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: Do 17.04.08 18:02
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
      
Beiträge: 184
WinXP
D5 Prof
|
Verfasst: 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... 
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: 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 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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
      
Beiträge: 184
WinXP
D5 Prof
|
Verfasst: Fr 18.04.08 13:22
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 18.04.08 13:53
|
|
soong101 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: Fr 18.04.08 15:03
Oh doch, Luckie, ich hab mir die Programmierhilfe angeschaut. Zugeben war ich nicht auf msdn.com, aber ich hab in der Delphi-Hilfe nachgeschaut, dort steht derselbe Artikel wie hier: msdn2.microsoft.com/...ms633497(VS.85).aspx
Ich steh vielleicht auf dem Schlauch, aber so hat man es auch hier gemacht. Vielleicht versteh ich aber auch nicht, was ein Pointer ist. Ich kenn halt nur Java-Referenzen.
Na komm, ich hab ja schon einige Zeit gebraucht um rauszufinden, dass die Funktion "IsWindowVisible" heißt statt "IsVisible" 
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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.
Zuletzt bearbeitet von jaenicke am Fr 18.04.08 15:33, insgesamt 2-mal bearbeitet
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: 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.
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 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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...
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: 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 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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:
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.
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: 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 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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.
_________________ As Wichita Falls So Falls Wichita Falls
|
|
Motzi
      
Beiträge: 2931
XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
|
Verfasst: 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.
_________________ gringo pussy cats - eef i see you i will pull your tail out by eets roots!
|
|
soong101 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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)
_________________ As Wichita Falls So Falls Wichita Falls
|
|
soong101 
      
Beiträge: 31
WIN XP
Delphi 2005 Personal
|
Verfasst: 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:
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?
_________________ As Wichita Falls So Falls Wichita Falls
|
|
|