-
Есть форма, на ней - TKOLListView В момент создания формы в function TControl.CreateWindow: Boolean; вызывается fHandle := CreateWindowEx( Params.ExStyle, Params.WinClassName,..., который создает окно класса obj_SysListView32. При этом создаются 1 BRUSH и 3 FONT GDI-хэндла, которые даже в destructor TControl.Destroy; при вызове DestroyWindow( I ); не возвращаются. Соответственно, при постоянном создании-удалении окна у приложения растут GDI Handles. Вопрос - так и должно быть или я что-то упускаю?
-
Наверное, не хватает TKOLApplet? Demo смотрели с модальными формами? Оно gdi-ресурсы не теряет?
-
Наличие TKOLApplet значения не имеет. DemoModalForm ресурсы не потребляет, но в нем и TKOLListView нет. А проблема как раз в ListView.
Собственно, даже такой кусок кода создает утечку GDI-ресурсов и памяти:
lv : TKOLListView;
procedure TForm1.Button1Click(Sender: PObj); begin lv := NewListView(Form, lvsList, [], nil, nil, nil); lv.Show; end;
procedure TForm1.Button2Click(Sender: PObj); begin lv.Free; end; Каждое нажатие Button1 + Button2 приводит к захвату дополнительных 1 BRUSH и 2 FONT. Потребление памяти также каждый раз увеличивается на 2 Кбайта.
-
Обновление:
Если взять стандартный пример DemoModalForm и добавить: 1. в unitb.pas строку
lv : TKOLListView;
2. в UnitB_1.inc в конец Result.lv := NewListView(Result.Form, lvsList, [], nil, nil, nil);
то ресурсы опять-таки теряются при каждом показе модальной формы.
Иными словами, ресурсы теряются при каждом создании экземпляра TKOLListView.
-
А где утечки смотрите? Memproof показал что все в норме
-
-
Количество брашей и фонтов он как раз показывает. Позже проверю GDIView'ом тогда
-
Проверил через гдивьев, что-то не то он показывает явно. Попробуй код var
b: DWORD;
procedure TForm1.Button1Click(Sender: PObj);
var
t: TLogBrush;
begin
t.lbStyle := BS_SOLID;
t.lbColor := DIB_RGB_COLORS;
b := CreateBrushIndirect(t);
form.Caption := Int2Str(b);
end;
procedure TForm1.Button2Click(Sender: PObj);
begin
form.Caption := Int2Str(integer(DeleteObject(b)));
end;
-
GDIView показывает ровно то же самое, что и ProcesExplorer Русиновича и даже (!!!) стандартный Диспетчер задач в Windows (если включить в нем колонку "объекты GDI"). Последним двум я верю даже больше, чем себе :)
А именно: 1. Сразу после запуска за приложением числятся 14 GDI-ресурсов. 2. После создания кисти число GDI-ресурсов становится равным 15. 3. После удаления кисти число GDI-ресурсов остается равным 15. Это - да, непонятно. 4. Однако при повторном создании кисти число ресурсов опять не изменяется и остается равным 15. 5. к пункту 3.
При создании-удалении KOLListView ситуация иная: 1. Сразу после запуска за приложением числятся 14 GDI-ресурсов. 2. После создания KOLListView число GDI-ресурсов становится равным 19. 3. После удаления KOLListView число GDI-ресурсов не изменяется (!!!). 4. При повторном создании KOLListView число GDI-ресурсов увеличивается на 3 (!!!). 5. к пункту 3.
Если с BRUSH-ами все не так страшно, поскольку общее число ресурсов не растет, то с ListView все достаточно плохо, поскольку каждый объект (пусть даже потом и удаленный) увеличивает число захваченных ресурсов на 3. 3333 созданных за время работы программы списков - и финиш.
-
Забавно, что ни в VCL, ни в LCL (Lazarus) подобных фокусов с ListView не происходит - там GDI-ресурсы исправно возвращаются обратно.
-
I just tried something. It seems you do not understand MemProof! You have to compile first with stackframes ON and full debug info. Otherwise MemProof will give false, incorrect results. If you test with these options, NO brushes and pens are leaked at all.
This is just illusion. RTFM from MemProof BTW. <smile>
To be friendly: you are not the first one who forgot this.
-
To be fair:
You can only test kol for memory leaks with stackframes on and {$D+}. You can remove that for "release". The reason is that kol is too efficient :-) If Memproof says it doesn't leak with these options, it will not leak without these options either!
Plz let me know the results. I know that the code that you wrote here Does Not Leak!
-
2 thaddy:
Can you send me a working EXE (creating and destroying KOLListView example)?
-
And again:
I'm NOT using MemProof. I'm using GDIView, ProcessExplorer and standard Windows Task Manager. The all show the same - GDI resources leaking.
If you have a correctly working EXE (with create/destroy KOLListView), please send me.
-
Send me some code in private. (you have mail)
-
I guess this is closed?
-
hard to tell with such activity in this forum)
-
I spend some time to check the claim. My conclusion was right. I you debug the leaks with a good program (like AQ** or its ancestor memproof) and you follow the instructions, there are no leaks in GDI. There is also a directive to debug the GDI resources in KOL. That also doesn't give anything unusual. Debugging can ONLY work if you follow the rules, otherwise you get false positives for leaks. If G-host is still not convinced, email me with code.
-
@RusSun: It is a bit quiet here, you are right. Doesn't mean we don't read.
-
2 thaddy Sorry, I think you are wrong. Because this code just crash or hangs up the application: procedure TForm1.Button1Click(Sender: PObj);
var
i : Integer;
begin
for I := 1 to 4000 do begin
lv := NewListView(Form, lvsList, [], nil, nil, nil);
lv.Show;
lv.Free;
end;
end; GDI resources goes to 10000 and than application hangs. Why it's happens, if releasing of GDI is OK?
-
-
@thaddy: Reading without action for me like no reaction)
-
Action taken right now. I'll have a look... Was easier by email...
-
After one minute:
procedure TForm1.Button1Click(Sender: PObj);
var
i : Integer;
begin
for I := 1 to 4000 do
begin
lv := NewListView(Form, lvsList, [], nil, nil, nil);
try lv.Show;
finally
lv.Free;
end;
end;
end;
No leaks.... Slightly improved.....
form.BeginUpdate; try for I := 1 to 4000 do
begin
lv := NewListView(Form, lvsList, [], nil, nil, nil);
try lv.Show;
finally
lv.Free;
end;
finally
Form.EndUpdate;
end;
end;
No leaks at all.... And finally:
procedure TForm1.Button2Click(Sender: PObj);
begin
if Assigned(lv) then lv.Free;
end;
Case closed.
-
BTW the trick is of course:
SHOWMODAL!
-
The lesson is: your program doesn't have full control over OS resources when your application has no time to respond to messages. Although Autofree does a good job, it still needs to be notified. AND you are creating the listview in a procedure again and again. in that case NO framework can guarantee that the original object is already freed. By using a try finally block you can prevent this. Second mistake: you use show, where you really want to use either showmodal or use the virtual listview option (Vladimir has an excellent example on kolmck,net) Third mistake: too many screen updates... Lock the form, with begin update ensures that no paint messages are send until you are finished with your work (be sure to unlock it!) Fourth mistake: The way you have written the code can cause the conflict that there is no listview at all, so you have to check if it is not already destroyed.
If you test your code with my small improvements, you will find that it does not leak at all. But good programming is completely different. I can point you to at least a dozen other basic mistakes in the rest of the your program.
-
Easy to make mistakes:
form.BeginUpdate; try for I := 1 to 4000 do
begin
lv := NewListView(Form, lvsList, [], nil, nil, nil);
try lv.Show;
finally
lv.Free;
end;
end;
finally
Form.EndUpdate;
end;
end;
-
It doesn't leak, but it does not show either <smile> Programming in human memory isn't without flaws... But then again: the code is nonsense. Why would you do that? In the VCL application you solved things more normal. But with a totally different algorithm. Your KOL code has to many mistakes anyway.
If you really want to create a new listview object every 4000 times --- do the screen lock in the loop too..... It still doesn't leak and this time everything is tested with memproof (I can prove it!)
-
2 thaddy: Please send me a compiled EXE and project source of example, which create and destroy 4000 ListViews
-
В общем, ответа от thaddy нет, а проблема остается - создать (и уничтожить) 4000 ListView в программе на KOL не представляется возможным. Причем не важно, сразу создавать или размазывать код по времени.
-
Страдаете откровенной ерундой. вот код, утечек нет.
procedure TForm1.Button1Click(Sender: PObj);
var
i : Integer;
begin
for I := 1 to 4000 do begin
lv := NewListView(Form, lvsList, [], nil, nil, nil);
lv.SetPosition(0, 0).SetSize(20, 20);
Form.Invalidate;
lv.Free;
end;
end;
а так же читаем комменты в коле
procedure Show;
где тут написано что можно юзать метод с листвью?
-
Попробуйте UNICODE_CTRLS. В новых Delphi начиная с 2009, как я понимаю, иначе и не получается, там этот символ просто должен быть. (Хорошо бы еще понять, как сделать, чтобы утечки не было без него - для старых Delphi. Возможно, надо отлавливать какое-нибудь событие с W на конце имени, хотя контрол и не уникодовский. Первый раз подобное было замечено с treeview).
2Dufa: Однако, можно. Не помню как раньше (и было ли иначе), но сейчас это то же самое что Visible := true;
-
Все, разобрался. Действительно, UNICODE не при чем. Действительно, имеется утечка - именно для ListView. Надо было отнести код, удаляющий привязку контрола к окну, в WM_NCDESTROY. Есть некоторые сомнения, что абсолютно все будет работать корректно: - как минимум, пришлось вызвать обработчик WM_NCDESTROY по умолчанию - так же, в коде есть комментарий по поводу исправления для корректного уничтожения progress bar'а (версия 2.41-2.42). Я проверил - вроде бы все нормально после переноса, но может, я что-то не учел. - дополнительно, есть вероятность, что требуется проверка того, что сообщение WM_DESTROY / WM_NCDESTROY пришло от своего собственного окна. Заключение в кавычки {IFnDEF SMALLER_CODE}, видимо, достаточно - в общем случае проверка останется. - изменения могут привести к тому, что несколько больше обработчиков событий и оконных сообщений может срабатывать в момент разрушения контрола или формы. Но в принципе, особых проблем не наблюдается - на тестах.
Сейчас я приготовлю обновление. Если есть пожелания о внесении в версию каких-либо изменений, пишите сейчас.
-
хотелось бы для мост копабилити в TKOLStrList между анси и юникодом наблюдать property values
interface
type
PWStrList = ^TWstrList;
procedure OptimizeForRead;
protected
procedure SetValue(const AName, Value: KOLWideString);
function GetValue(const AName: KOLWideString): KOLWideString;
public
function IndexOfName(AName: KOLWideString): Integer;
property Values[const AName: KOLWideString]: KOLWideString read GetValue write SetValue;
end;
implementation
procedure TWStrList.OptimizeForRead;
begin
if fList <> nil then
fList.OptimizeForRead;
end;
function TWStrList.IndexOfName(AName: KOLWideString): Integer;
var i: Integer;
L: Integer;
fCount: integer;
begin
Result:=-1;
L := Length( AName );
if L > 0 then
begin
AName := WLowerCase( AName ) + fNameDelim;
Inc( L );
fCount := GetCount - 1;
for i := 0 to fCount do
begin
if _WStrLComp( PWideChar( WLowerCase( ItemPtrs[ i ] ) ), PWideChar( AName ), L ) = 0 then
begin
Result:=i; exit;
end;
end;
end;
end;
procedure TWStrList.SetValue(const AName, Value: KOLWideString);
var
I: Integer;
begin
I := IndexOfName(AName);
if i=-1
then Add( AName + fNameDelim + Value )
else Items[i] := AName + fNameDelim + Value;
end;
function TWStrList.GetValue(const AName: KOLWideString): KOLWideString;
var
i: Integer;
begin
I := IndexOfName(AName);
if I >= 0
then Result := Copy(Items[i], Length(AName) + 2, Length(Items[i])-Length(AName)-1)
else Result := '';
end;
-
так, еще?
|