Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - 64-Bit-Code und EnumWindows - wie?
galagher - Mo 20.08.18 23:16
Titel: 64-Bit-Code und EnumWindows - wie?
Hallo!
Ich verwende folgenden Code von
Frühlingsrolle, um Fenster-Captions zu ermitteln, deren genauer Text unbekannt ist. Leider funktioniert das als 64-Bit-Code nicht, da gibt's eine Zugriffsverletzung. Was muss ich ändern? Es liegt definitiv an EnumWindows!
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:
| procedure GetSpecificWindowCaption(const AList: TStrings; const SubWindowCaption: string); function GetAllWindows(hWnd: HWND; const AList: TStrings): Boolean; stdcall; var captionLength: Word; wndCaption: string; begin captionLength := GetWindowTextLength(hWnd); SetLength(wndCaption, captionLength); GetWindowText(hWnd, PChar(wndCaption), captionLength + 1); Alist.Add(wndCaption); result := true; end; var tmp: TStringList; i: Integer; begin if (AList <> nil) and (SubWindowCaption <> '') then begin tmp := TStringList.Create; try EnumWindows(@GetAllWindows, Integer(tmp)); tmp.BeginUpdate; for i := 0 to tmp.Count - 1 do if Pos(UpperCase(SubWindowCaption), UpperCase(tmp[i])) <> 0 then AList.Add(tmp[i]); tmp.EndUpdate; finally tmp.Free; end; end; end; |
Siehe auch:
https://www.entwickler-ecke.de/viewtopic.php?t=116545
jaenicke - Di 21.08.18 07:10
galagher hat folgendes geschrieben : |
Delphi-Quelltext 1:
| EnumWindows(@GetAllWindows, Integer(tmp)); | |
Wie viele Bytes groß ist denn tmp unter 64-Bit? Richtig, 8 Byte oder 64 Bit. Wenn du das nun auf die Hälfte abschneidest (Integer = 32 Bit bzw. 4 Byte), ist der Pointer auf die Liste kaputt und dementsprechend knallt es beim Zugriff.
Der zweite Parameter von EnumWindows ist nicht umsonst vom Typ NativeInt. Dann geht es vermutlich auch:
Delphi-Quelltext
1:
| EnumWindows(@GetAllWindows, NativeInt(tmp)); |
Ich würde allerdings auch noch prüfen, ob das Fenster überhaupt Text enthält, sonst kann es beim GetWindowText knallen:
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:
| procedure GetSpecificWindowCaption(const AList: TStrings; const SubWindowCaption: string); function GetAllWindows(hWnd: HWND; const AList: TStrings): Boolean; stdcall; var captionLength: Integer; wndCaption: string; begin captionLength := GetWindowTextLength(hWnd); if captionLength > 0 then begin SetLength(wndCaption, captionLength); GetWindowText(hWnd, PChar(wndCaption), captionLength + 1); Alist.Add(wndCaption); end; result := true; end; var tmp: TStringList; i: Integer; begin if (AList <> nil) and (SubWindowCaption <> '') then begin tmp := TStringList.Create; try EnumWindows(@GetAllWindows, NativeInt(tmp)); tmp.BeginUpdate; for i := 0 to tmp.Count - 1 do if Pos(UpperCase(SubWindowCaption), UpperCase(tmp[i])) <> 0 then AList.Add(tmp[i]); tmp.EndUpdate; finally tmp.Free; end; end; end; |
galagher - Di 21.08.18 17:54
jaenicke hat folgendes geschrieben : |
Wie viele Bytes groß ist denn tmp unter 64-Bit? Richtig, 8 Byte oder 64 Bit. Wenn du das nun auf die Hälfte abschneidest (Integer = 32 Bit bzw. 4 Byte), ist der Pointer auf die Liste kaputt und dementsprechend knallt es beim Zugriff. |
Das habe ich überhaupt nicht bedacht, habe es aber trotzdem bereits mit
NativeInt versucht, zunächst aber erfolglos, denn:
jaenicke hat folgendes geschrieben : |
Ich würde allerdings auch noch prüfen, ob das Fenster überhaupt Text enthält, sonst kann es beim GetWindowText knallen: |
Hm, mit 32-Bit ist das aber scheinbar kein Problem...
Jedenfalls klappt es jetzt!
//Edit: Naja, es klappt im dem Sinn, dass es sich kompilieren lässt. Aber es findet keine entsprechenden Fenster, zumindest die Caption der MainForm müsste es auflisten, tut es aber nicht! :nixweiss:
galagher - Di 21.08.18 21:38
Delphi-Laie hat folgendes geschrieben : |
Es enumeriert alles mögliche. Fenster sind als Enumerationsobjekte auch darin vorhanden. |
Dein
WindowsEnumeration verursacht genauso einen Zugriffsfehler, und wenn ich zusätzlich Code einfüge, um die Länge der Caption zu prüfen, bleibt die Liste leer.
Ich bin ratos!
Delphi-Laie - Di 21.08.18 22:02
galagher hat folgendes geschrieben : |
Delphi-Laie hat folgendes geschrieben : | Es enumeriert alles mögliche. Fenster sind als Enumerationsobjekte auch darin vorhanden. | Dein WindowsEnumeration verursacht genauso einen Zugriffsfehler |
Aber doch wohl nicht in meinen Compilaten, sondern in Deinem?!
"Meine" Enumwindows-Funktionen sind ja auch nur in Delphiforen zusammengeklaubt worden.
Wenn es bei mir funktioniert, bei Dir aber nicht, gibt es einen sicheren, wenngleich meistens recht mühsamen, langwierigen Weg, den Fehler sicher aufzuspüren: Den funktionirenden Code "schälen", entblättern, herunterbrechen und anschließend mit dem neuen Code zu umhüllen. Alles schön schrittweise. Es gibt kaum Fehler, die nicht aufzuspüren wären, entsprechende Hartnäckigkeit vorausgesetzt.
galagher - Di 21.08.18 22:16
Delphi-Laie hat folgendes geschrieben : |
Aber doch wohl nicht in meinen Compilaten, sondern in Deinem?! |
In meinem, und nur mit 64-Bit!
Delphi-Laie hat folgendes geschrieben : |
"Meine" Enumwindows-Funktionen sind ja auch nur in Delphiforen zusammengeklaubt worden. |
Ja, so was kenne ich!
Delphi-Laie hat folgendes geschrieben : |
Wenn es bei mir funktioniert, bei Dir aber nicht, gibt es einen sicheren, wenngleich meistens recht mühsamen, langwierigen Weg, den Fehler sicher aufzuspüren: Den funktionirenden Code "schälen", entblättern, herunterbrechen und anschließend mit dem neuen Code zu umhüllen. |
Es liegt definitiv an
EnumWindows, das findet mit 64-Bit-Code keine Fenster mit Caption, in der der angegebene Text vorkommt. Mit 32-Bit funktioniert es aber. Das Problem ist also bereits eingegrenzt!
jaenicke - Mi 22.08.18 05:36
Wo genau knallt es denn? Stacktrace, lokale Variablen, Assemblercode an der Fehlerstelle, Registerinhalte, ...?
Du kannst ja Screenshots machen, wenn du möchtest. In die Assembleransicht kommst du mit Strg +Alt + C oder über das Menü.
galagher - Mi 22.08.18 07:39
jaenicke hat folgendes geschrieben : |
Wo genau knallt es denn? Stacktrace, lokale Variablen, Assemblercode an der Fehlerstelle, Registerinhalte, ...?
Du kannst ja Screenshots machen, wenn du möchtest. In die Assembleransicht kommst du mit Strg +Alt + C oder über das Menü. |
Assemblercode nicht, das andere muss ich mir noch ansehen. Kann dann ja Screenshots machen, es ist jedenfalls eine Zugriffsverletzung!
jaenicke hat folgendes geschrieben : |
Der zweite Parameter von EnumWindows ist nicht umsonst vom Typ NativeInt. |
Diese Stelle verursacht die Zugriffsverletzung nicht, sondern, wenn ich die Prüfung
if captionLength > 0 nicht durchführe.
Wenn ich aber den Rat von
jaenicke befolge:
jaenicke hat folgendes geschrieben : |
Ich würde allerdings auch noch prüfen, ob das Fenster überhaupt Text enthält, sonst kann es beim GetWindowText knallen: |
... die Abfrage
if captionLength > 0 also mache, dann läuft das Programm zwar ohne Zugrifssverletzung, findet aber keine Fenster mit entsprechender Caption, obwohl die da sind. Wohlgemerkt: Das alles nur bei 64-Bit!
jaenicke - Mi 22.08.18 08:02
Ach so, dann schreib doch einfach mal alle Überschriften in die Liste rein unabhängig von der Bedingung bzw. schau dir die Liste im Debugger an. Werden da Fenster gefunden?
galagher - Mi 22.08.18 08:34
jaenicke hat folgendes geschrieben : |
Ach so, dann schreib doch einfach mal alle Überschriften in die Liste rein unabhängig von der Bedingung |
Kann ich machen, dann wird's wohl auch funktionieren. Die Fenstercaptions sollen aber gefunden, nicht vorgegeben werden!
jaenicke hat folgendes geschrieben : |
schau dir die Liste im Debugger an. Werden da Fenster gefunden? |
Mit 64-Bit-Code trennt sich jedesmal der Debugger vom Programm. Aber das ist jetzt nicht das Problem! Ist mir im Moment egal.
Vielleicht klappt ja
captionLength := GetWindowTextLength(hWnd); nicht. Habe es stattdessen auch schon mit
SendMessage versucht (und dabei auf die richtigen Parameter für 64-Bit-Code geachtet) - erfolglos.
Es klappt nicht, eagl, ob ich
PWideChar statt
PChar verwende, egal, ob mit NativeInt oder Integer. Sogar, wenn ich den Suchtext hardcode - das Fenster wird nicht gefunden. Also entweder funktioniert
EnumWindows oder
GetWindowTextLength oder beides nicht...
jaenicke - Mi 22.08.18 10:28
galagher hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | Ach so, dann schreib doch einfach mal alle Überschriften in die Liste rein unabhängig von der Bedingung | Kann ich machen, dann wird's wohl auch funktionieren. Die Fenstercaptions sollen aber gefunden, nicht vorgegeben werden! |
Nein, ich meinte das zum Debuggen.
Denn die Frage ist ja, ob die Überschriften nicht richtig ausgelesen werden oder der Vergleich schief geht.
galagher - Mi 22.08.18 11:11
jaenicke hat folgendes geschrieben : |
Nein, ich meinte das zum Debuggen.
Denn die Frage ist ja, ob die Überschriften nicht richtig ausgelesen werden oder der Vergleich schief geht. |
Ich bin jetzt nicht am Computer mit dem Projekt, aber ich werde die Liste dann noch testweise manuell befüllen, um zu sehen, ob es am Vergleich liegt.
Glaube ich aber nicht, denn die Überschriften werden nicht in die Liste geschrieben, die Liste ist leer: Wenn ich sie mit SaveToFile speichere, hat die Datei 0 Bytes.
Mein Verdacht ist, dass entweder
EnumWindows oder
GetWindowTextLength mit 64-Bit-Code nicht kann.
jaenicke - Mi 22.08.18 11:19
galagher hat folgendes geschrieben : |
Glaube ich aber nicht, denn die Überschriften werden nicht in die Liste geschrieben, die Liste ist leer: Wenn ich sie mit SaveToFile speichere, hat die Datei 0 Bytes. |
Die temporäre Liste?
galagher - Mi 22.08.18 17:54
jaenicke hat folgendes geschrieben : |
galagher hat folgendes geschrieben : | Glaube ich aber nicht, denn die Überschriften werden nicht in die Liste geschrieben, die Liste ist leer: Wenn ich sie mit SaveToFile speichere, hat die Datei 0 Bytes. | Die temporäre Liste? |
Seltsam, es kracht schon hier bei
Alist.SaveToFile:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| procedure GetSpecificWindowCaption(const AList: TStrings; const SubWindowCaption: string); function GetAllWindows(hWnd: HWND; const AList: TStrings): Boolean; stdcall; var captionLength: Integer; wndCaption: string; begin captionLength := GetWindowTextLength(hWnd); if captionLength > 0 then begin SetLength(wndCaption, captionLength); GetWindowText(hWnd, PWideChar(wndCaption), captionLength + 1); Alist.Add(wndCaption); end; Alist.SaveToFile('1.txt'); result := true; end; |
Dann erhalte ich wieder:
Zitat: |
Zugriffsverletzung bei Adresse 000000000080665D in Modul 'SynEdit.exe'. Lesen von Adresse FFFFFFFFFFFFFFFF |
Aber AList existiert doch:
Delphi-Quelltext
1: 2: 3: 4: 5:
| tmp := TStringList.Create; try EnumWindows(@GetAllWindows, NativeInt(tmp)); |
//Edit:
Das Projekt heisst zwar SynEdit, ich verwende aber kein TSynEdit! Daran kann es nicht liegen!
jaenicke - Mi 22.08.18 20:00
Da wirst du wohl debuggen müssen welche Werte in den Variablen stecken (tmp, ...) und was dann in der Callbackfunktion ankommt usw.
galagher - Mi 22.08.18 20:13
Es werden die Captions nicht mal an eine einfache String-Variable angehängt, weil enumWindows definitiv mit 64-Bit nicht funktioniert. Die Konstante AList gibt es in Funktion GetAllWindows nicht, weil EnumWindows sie nicht (richtig) mit übergibt. Wenn man mit
if Assigned(AList) prüft, wird der Codeteil übersprungen. Ansonsten schlägt das Einfügen in AList fehl, weil das dort schlicht nicht existiert.
jaenicke hat folgendes geschrieben : |
Da wirst du wohl debuggen müssen welche Werte in den Variablen stecken (tmp, ...) und was dann in der Callbackfunktion ankommt usw. |
Erspare ich mir! Ich habe hier ->
https://www.swissdelphicenter.ch/en/showcode.php?id=327 was gefunden und angepasst, das ohne EnumWindows auskommt: :dance2:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure GetSpecificWindowCaption(const AList: TStrings; const SubWindowCaption: string); var NextHandle: Hwnd; wndCaption: array[0..260] of char; begin NextHandle := GetWindow(Application.Handle, GW_HWNDFIRST); while NextHandle > 0 do begin GetWindowText(NextHandle, wndCaption, 255);
if Pos(AnsiUpperCase(SubWindowCaption), AnsiUpperCase(wndCaption)) > 0 then AList.Add(wndCaption);
NextHandle := GetWindow(NextHandle, GW_HWNDNEXT); end; end; |
jaenicke - Mi 22.08.18 21:13
galagher hat folgendes geschrieben : |
Es werden die Captions nicht mal an eine einfache String-Variable angehängt, weil enumWindows definitiv mit 64-Bit nicht funktioniert. Die Konstante AList gibt es in Funktion GetAllWindows nicht, weil EnumWindows sie nicht (richtig) mit übergibt. |
Bei mir funktioniert die Funktion, die ich oben gepostet habe, problemlos unter 64-Bit.
Aber wenn du es nicht brauchst, ist das natürlich noch einfacher. Kannst du da nicht einfach Screen.Forms benutzen, wenn es um deine eigene Anwendung geht? Außerdem frage ich mich wie es passieren kann, dass du selbst in deiner Anwendung nicht weißt was da für Fenster existieren. :gruebel:
galagher - Mi 22.08.18 21:29
jaenicke hat folgendes geschrieben : |
Bei mir funktioniert die Funktion, die ich oben gepostet habe, problemlos unter 64-Bit. |
Werde sie noch in einem separaten Programm testen. Könnte es eventuell daran liegen, dass es ursprünglich eine 32-Bit-Anwendung war, die ich jetzt zu 64-Bit kompiliere?
jaenicke hat folgendes geschrieben : |
Kannst du da nicht einfach Screen.Forms benutzen, wenn es um deine eigene Anwendung geht? Außerdem frage ich mich wie es passieren kann, dass du selbst in deiner Anwendung nicht weißt was da für Fenster existieren. :gruebel: |
Ich möchte nicht die Captions
meiner Anwendung ermitteln, sondern die Form1.Caption's
anderer Instanzen meiner Anwendung!
Dann kann ich in jeder Form1.Caption aller meiner Instanzen angeben, was die aktuelle Instanz ist und wieviele Instanzen laufen, zB. steht dann bei der als 1. aufgerufenen Instanz "SynEdit (Instanz 1/1)", bei der 2. Instanz steht dann "SynEdit (Instanz 2/2)" usw. Wenn eine Instanz gestartet oder beendet wird, passe ich die Captions an.
Eine nette Spielerei, nicht mehr. Aber sie funktoniert jetzt endlich auch mit 64-Bit!
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 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!