Autor Beitrag
worm
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 135


D6 Prof
BeitragVerfasst: Sa 16.08.03 02:29 
Hallo!

Ich habe eine ListView mit OwnerData = True. Das Problem ist, wenn ich im OnData-Event den SubItems Werte zuweise, werden diese Strings scheinbar nie mehr freigegeben. Scrollt der User in der Liste hin und her, wird laufend neuer Speicher reserviert.

Zum Nachvollziehen des folgenden Codes nur eine ListView aufs Formular packen und OwnerData auf true setzen (ViewStyle=vsReport ist nichtmal nötig). Und eben die Ereignisse verknüpfen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
var s: String;

procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
  with Item do
  begin
    Caption := s;
    SubItems.Add(s);  //<-- hierbei wird Speicher reserviert, aber nie wieder freigegeben
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListView1.Items.Count := 2048;
  Randomize;
  s := IntToStr(Random(MAXINT)); //um keinen konstanten String zu haben
end;

Ich hoffe, irgendwer hat eine Lösung für das Problem; entweder geschickter adden (gerne auch per API) oder Stringlisten wieder freigeben.

Danke, worm

_________________
In the beginning, the universe was created. This has made a lot of people very angry, and is generally considered to have been a bad move.
worm Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 135


D6 Prof
BeitragVerfasst: Mi 27.08.03 22:52 
@Mods: Bitte verschieben :arrow: API!

So, ich glaube, ich habe das Problem jetzt geortet... in der VCL wird bei der Behandlung der LVN_GETDISPINFO Notification (Message CN_NOTIFY, in TCustomListView.CNNotify) folgender Code verwendet:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
with PLVDispInfo(NMHdr)^.item do
begin
  if (mask and LVIF_TEXT) <> 0 then
    if iSubItem = 0 then
      StrPLCopy(pszText, Item.Caption, cchTextMax - 1)
    else
      with Item.SubItems do
        if iSubItem <= Count then
          StrPLCopy(pszText, Strings[iSubItem - 1], cchTextMax - 1)
        else pszText[0] := #0;
 ...
Der String wird durch StrPLCopy also in den Puffer pszText kopiert.
Jetzt Fragen dazu:
  • Ist für diesen Puffer von Windows bereits Speicher reserviert oder könnte ich auch pszText auf meine bereits existierenden Strings zeigen lassen (die ich sowieso im Speicher habe und auch dort behalte)?
  • Falls ich das könnte, kann ich irgendwie eine eigene Klasse von TCustomListView ableiten und dabei nur die Behandlung der Notification LVN_GETDISPINFO ändern?
  • Hat noch jemand eine andere Idee?
Thx für alle Antworten, die da kommen mögen!

Cya, worm

_________________
In the beginning, the universe was created. This has made a lot of people very angry, and is generally considered to have been a bad move.
worm Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 135


D6 Prof
BeitragVerfasst: Sa 20.09.03 00:43 
Hab mich nochmal damit beschäftigt und hab's jetzt selbst gelöst. War gar nicht soo kompliziert. Heißen Dank an Luckie's Win32API-Tutorials (hab die ListView-Demo als Basis zum Experimentieren genommen) :D

Also gelöst habe ich es jetzt, indem ich die WM_NOTIFY-Messages abfange und die LVN_GETDISPINFO für meine ListView selbst behandle. Dadurch kommt die VCL gar nicht erst dazu, Speicher zu verschwenden:
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:
34:
35:
36:
37:
38:
39:
...

uses ..., CommCtrl;

type
  TForm1 = class(TForm)
    ListView1: TListView;
  private
    procedure WMNOTIFY(var M: TWMNotify); message WM_NOTIFY;
  end;

const
  entry_name = 'Testname';
  entry_size = 1234;

...

procedure GETDISPINFO(item: PLVITEM);
//Daten für die ListView bereitstellen
begin
  with item^ do
    if (mask and LVIF_TEXT) <> 0 then
    case iSubItem of
      0: pszText := PChar(entry_name);
      1: wvsprintf(pszText, '%u', PChar(@entry_size));
    end;
end;

procedure TForm1.WMNOTIFY(var M: TWMNotify);
//LVN_GETDISPINFO für ListView1 abfangen
begin
  with M.NMHdr^ do
    if hwndFrom = ListView1.Handle then
      case code of
        LVN_GETDISPINFO: GETDISPINFO(@PLVDispInfo(M.NMHdr)^.item);
        else Inherited;
      end
    else Inherited;
end;


Falls das jemand mal selbst ausprobieren will, hier der Source für ein kleines Testprogramm... per Define lässt sich das Abfangen der Notification ein- und ausschalten und unten wird der Speicherverbrauch angezeigt. Bei der VCL-Version kostet es sogar Speicher, mit der Maus über die Einträge zu fahren. :roll:

Cya, worm

_________________
In the beginning, the universe was created. This has made a lot of people very angry, and is generally considered to have been a bad move.