Entwickler-Ecke

Windows API - Virtual ListView (OwnerData) -> Speicherleck bei SubItems


worm - Sa 16.08.03 02:29
Titel: Virtual ListView (OwnerData) -> Speicherleck bei SubItems
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.

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


worm - 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 [http://msdn.microsoft.com/library/en-us/shellcc/platform/commctls/listview/notifications/lvn_getdispinfo.asp] (Message CN_NOTIFY, in TCustomListView.CNNotify) folgender Code verwendet:

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:Thx für alle Antworten, die da kommen mögen!

Cya, worm


worm - 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 [http://www.luckie-online.de/tutorials/win32apituts/] (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:

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 [http://home.arcor.de/f0k/lv-ownerdata.zip]... 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