Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Speicherzugriffsfehler


Heiko - So 05.02.06 17:11
Titel: Speicherzugriffsfehler
Hi @all,

ich habe irgendwie ein Speicherzugriffsproblem in meinem Code. Er bekommt das nicht hin mit dem erstelltem Array zu arbeiten bzw. die Größe des Array zu ermitteln. Wenn ich die markierten Zeilen in eine extra Procedur packe (extra Button) funktioniert es auch ordentlich, aber nicht zusammen hängend (leider) :(.


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:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  TFensterInfo=record
    Handle: HWND;
    Caption: String;
  end;

var
  Form1: TForm1;
  FensterInfo: array of TFensterInfo;

implementation

{$R *.dfm}

Function EnumWindowsProc(Wnd: HWND): BOOL; stdcall;
var
  Capt: Array [0..128of Char;
begin
  Result:=true;
  if IsWindowVisible(Wnd) and
     ((GetWindowLong(Wnd, GWL_HWNDPARENT)=0or
      (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT))=GetDesktopWindow)) and
     ((GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW)=0)
  then
  begin
    SendMessage(Wnd, WM_GETTEXT, Sizeof(Capt), integer(@Capt));
    SetLength(FensterInfo, length(FensterInfo)+1);
    FensterInfo[High(FensterInfo)].Caption:=String(Capt);
    FensterInfo[High(FensterInfo)].Handle:=Wnd;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
begin
  SetLength(FensterInfo, 0);
  EnumWindows(@EnumWindowsProc, 0);
  Form1.ListBox1.Items.Clear;
  for i:=0 to High(FensterInfo) do
  begin
    Form1.ListBox1.Items.Add(FensterInfo[i].Caption)
  end;

end;

end.


So hier dagegen funktioniert es:


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:
unit Unit1;
 
interface 
  
uses 
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, StdCtrls, ExtCtrls;
 
type
  TForm1 = class(TForm) 
    Button2: TButton;
    ListBox1: TListBox;
    procedure Button2Click(Sender: TObject); 
  private 
    { Private-Deklarationen } 
  public 
    { Public-Deklarationen } 
  end
 
type 
  TFensterInfo=record 
    Handle: HWND; 
    Caption: String
  end
 
var 
  Form1: TForm1; 
  FensterInfo: array of TFensterInfo;
  lengthvar: integer = 0;
 
implementation 
 
{$R *.dfm}
 
Function EnumWindowsProc(Wnd: HWND): BOOL; stdcall
var 
  Capt: Array [0..128of Char; 
begin 
  Result:=true; 
  if IsWindowVisible(Wnd) and 
     ((GetWindowLong(Wnd, GWL_HWNDPARENT)=0or 
      (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT))=GetDesktopWindow)) and
     ((GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW)=0
  then 
  begin
    SendMessage(Wnd, WM_GETTEXT, Sizeof(Capt), integer(@Capt));
    SetLength(FensterInfo, length(FensterInfo)+1);
    inc(lengthvar);
    FensterInfo[High(FensterInfo)].Caption:=Capt;
    FensterInfo[High(FensterInfo)].Handle:=Wnd;
  end;
end
 
procedure TForm1.Button2Click(Sender: TObject);
var 
  i: integer;
begin
  lengthvar := 0;
  SetLength(FensterInfo, 0);
  EnumWindows(@EnumWindowsProc, 0);
  Form1.ListBox1.Items.Clear;
  for i := 0 to lengthvar do
  begin
    Form1.ListBox1.Items.Add(FensterInfo[i].Caption);
  end
end;
 
end.

Bei dieser 2. Variante ist nur noch eine globale Variable hinzugefügt, damit der Befehl High wegfällt ;).

Wisst ihr woran das Problem liegt? Und kann man irgendwie auch machen, dass der Fenstertitel auch über eine Länge von 128 Bytes ausgelsen wird (also mit einer unbegrenzten Länge)?


Horst_H - Mo 06.02.06 10:54

Hallo,

ich habe es mal im CPU-Debugger angesehen.
In der ersten Version wird die Adresse des dynamischen Array in dem Register EDI gespeichert ist.
Aber EDI ist nach dem Aufruf von der EnumWindows(@EnumWindowsProc, 0) = 0.
Ich meine nach isvisible war EDI 0.
Damit wird bei dem Aufruf von High das variable Arra auf $0 gesucht wo es nicht ist und nie war.

Da hat Der compiler falsch gedacht, dass EDI nicht in der anderen procedure benutzt wird.
Die >zweite< Frage solltest Du nochmals stellen ;-)

Gruss Horst


Tastaro - Mo 06.02.06 11:23

Die Funktion high liefert den größten Wert im Bereich des Indextypes des Array. So steht es in der Hilfe.
D.h. dass in deinem Fall die High immer 128 liefert.

Mit


Delphi-Quelltext
1:
2:
3:
    SetLength(FensterInfo, length(FensterInfo)+1);  
    FensterInfo[length(FensterInfo) - 1].Caption:=String(Capt);  
    FensterInfo[length(FensterInfo) - 1].Handle:=Wnd;


sollte das besser klappen.

Beste Grüße
Tastaro


Heiko - Mo 06.02.06 20:47

So nochmal zur Frage die ich vlt. nicht gut genug ausgedrückt habe ;). Und zwar will ich die Begrenzung aufheben die hier besteht (ich markiere es mal):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
function EnumWindowsProc(Wnd: HWND): BOOL; stdcall;
var
  Capt: Array [0..128of Char;
begin
  Result:=true;
  if IsWindowVisible(Wnd) and
     ((GetWindowLong(Wnd, GWL_HWNDPARENT)=0or
      (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT))=GetDesktopWindow)) and
     ((GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW)=0)
  then
  begin
    SendMessage(Wnd, WM_GETTEXT, Sizeof(Capt), integer(@Capt));
    SetLength(FensterInfo, length(FensterInfo)+1);
    FensterInfo[High(FensterInfo)].Caption:=String(Capt);
    FensterInfo[High(FensterInfo)].Handle:=Wnd;
  end;
end;


Wie man sieht wird an der 2. markierten Stelle nur max. 129 Bytes zurückgegeben, da das Array nicht größer ist. Kann dort die Besczhränkung auf die 129 Bytes aufheben?

@Horst_H: Das mag sein (in der Art hatte ich es schon vermutet), aber ich kann mir nicht erklären warum dann der folgende Quelltext Problemlos funktioniert, wenn ich nach ein ander die Buttons drücke:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
procedure TForm1.Button2Click(Sender: TObject);
begin
  SetLength(FensterInfo, 0);
  EnumWindows(@EnumWindowsProc, 0);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  i: integer;
begin
  Form1.ListBox1.Items.Clear;
  for i:=0 to High(FensterInfo) do
  begin
    Form1.ListBox1.Items.Add(FensterInfo[i].Caption)
  end;
end;


@Tastaro: An dem Einspeichern liegt das nicht (dein Quelltext ergibt nach Def. genau das gleiche, denn Low(...) ist bei dynamischen Arrays immer 0). Und wenn ich mir die Ergebnisse in der Zeile der for-Schleife auswerten lasse (Arrayname markieren + Strg+F7) gibt er ja genau das richtige aus, er hat also irgendein Problem, welches der Debugger scheinbar nicht hat :(.


NeoInDerMATRIX - Di 07.02.06 23:09

Hi,


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:
Function EnumWindowsProc(Wnd: HWND): BOOL; StdCall;   
Var   
  Capt: Array [0..128of Char;   
Begin   
  Result:=true;   
  If IsWindowVisible(Wnd) and   
     ((GetWindowLong(Wnd, GWL_HWNDPARENT)=0or   
      (HWND(GetWindowLong(Wnd, GWL_HWNDPARENT))=GetDesktopWindow)) and  
     ((GetWindowLong(Wnd, GWL_EXSTYLE) and WS_EX_TOOLWINDOW)=0)   
  then   
  begin  
    SendMessage(Wnd, WM_GETTEXT, Sizeof(Capt), integer(@Capt));  
    SetLength(FensterInfo, length(FensterInfo)+1);  
    inc(lengthvar);  
    FensterInfo[High(FensterInfo)].Caption:=Capt;  
    FensterInfo[High(FensterInfo)].Handle:=Wnd;  
  end;  
end;   
   
procedure TForm1.Button2Click(Sender: TObject);  
Begin
 BuildArray();
End;

Procedure tForm1.BuildArray();
Var
 I: Integer;
Begin
 EnumWindows(@EnumWindowsProc, 0);
 With Form1.ListBox1.Items do
  Begin
   Clear;
   For I:=0 to Length(FensterInfo) - 1 do
    Begin
     Add(FensterInfo[I].Caption);
    End;
  End;
End;


So sollte das eigentlich gehen. Du solltest die anzahl der Items mit Length - 1 abfragen. Nicht über HIGH etc.
Ist eindeutiger.

Cu
Neo