Autor Beitrag
soong101
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 184

WinXP
D5 Prof
BeitragVerfasst: 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:
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 18.04.08 10:02 
user profile iconchrisdrury 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: Fr 18.04.08 11:25 
Danke für die Wegweiser, bin nach einer Stunde auch schon etwas vorangekommen. Das ist meine EnumWindowsProc-Funktion:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function EnumWindowsProc(handle: HWND; lparam: LPARAM): Boolean;
//überprüft übergebenes Fensterhandle, ob 'Editor' im Namen vorkommt
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
ausblenden 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 ",".
ausblenden Quelltext
1:
E2036 Variable erforderlich					


Was mache ich falsch?
chrisdrury
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 184

WinXP
D5 Prof
BeitragVerfasst: Fr 18.04.08 13:22 
user profile iconLuckie 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:
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 18.04.08 13:53 
user profile iconsoong101 hat folgendes geschrieben:

So wende ich es an
ausblenden 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 ",".
ausblenden Quelltext
1:
E2036 Variable erforderlich					


Was mache ich falsch?

Du guckst nicht in die Hilfe: msdn2.microsoft.com/...library/default.aspx bzw. du guckst nicht hin, wenn die Programmierhilfe im Editor aufgeht.
soong101 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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:
ausblenden 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;
//überprüft übergebenes Fensterhandle, ob 'Editor' im Namen vorkommt
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...
msdn2.microsoft.com/...ms633498(VS.85).aspx hat folgendes geschrieben:
Return Value

To continue enumeration, the callback function must return TRUE; to stop enumeration, it must return FALSE.
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



BeitragVerfasst: 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.

ausblenden volle Höhe 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;
  // mit Result = False kann die Callbackfunktion vorzeitig verlassen werden
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Param: TMyEnumParam;
begin
  Param.lb := Listbox1;
  EnumWindows(@GetWindows, LPARAM(@Param));
end;




user profile iconsoong101 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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):
ausblenden 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



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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:
ausblenden volle Höhe 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;
//überprüft übergebenes Fensterhandle, ob 'Editor' im Namen vorkommt
//Dank geht an Luckie von Delphi-Forum.de!
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;
//   wnd_dc: HDC;

begin

wnd_this := Application.Handle;
EnumWindows(@GetWindows, 0);
//wnd_dc := GetDC(wnd_this);
//SetForeGroundWindow(wnd_other);
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



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: Sa 19.04.08 11:29 
user profile iconLuckie 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: Sa 19.04.08 14:01 
user profile iconMotzi 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?

ausblenden 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;
//überprüft übergebenes Fensterhandle, ob 'Editor' im Namen vorkommt
//Dank geht an Luckie von Delphi-Forum.de!
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 31

WIN XP
Delphi 2005 Personal
BeitragVerfasst: 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:
ausblenden volle Höhe 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;
//überprüft übergebenes Fensterhandle, ob 'Editor' im Namen vorkommt
//Dank geht an Luckie von Delphi-Forum.de!
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); //kopiere ohne ' - EAW Map Editor'
         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); //kopiere ohne ' - FOC Map Editor'
         Result := FALSE;
         end;
  end;
end;


Die GetChildWindows-Funktion:

ausblenden 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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
EnumWindows(@GetWindows, 0); //suche nach Editorfenster
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