![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Не правильное освобождение Menu [Delphi, Windows]
vampir_infernal © (2006-12-10 20:24:00)Использую в своем проекте Menu: PMenu. Создаю, рабтаю с ним, пытаюсь уничтожить. Если уничтожать так:
destructor TfmMain.Destroy;
Free_And_Nil(Menu);
Free_And_Nil(Form);
inherited;
end;
то вылетает AV на неправильную работу с указателями. То же самое, если добавлять меню в AutoFree апплета или формы. Теперь меняем местами освобождение меню и формы в деструкторе. Прога закрывается нормально, но MemProof и сторонняя программа контроля памяти ругаются на неосвобожденные 56 байт (сторонняя программа) или 64 байта (MemProof). Бился я с этой проблемой примерно 2 часа. Потом написал следующее:
destructor TfmMain.Destroy;
Free_And_Nil(Form);
Menu.Free;
Menu := nil;
inherited;
end;
и все заработало как надо. Если это какая-то неточнось в системе уничтожения меню в KOL, то ее необходимо исправить. Если же это моя ошибка, то подскажите, что же я сделал не правильно и почему меню не хочет уничтожаться как все остальные контролы?
Vladimir Kladov (2006-12-14 19:43:00) [1]Про косяк в асм-версии TObj.Final я уже писал. В pas_version не должно быть той утечки 44 байта. В выходной будет обновление.
Я про то, что лажаются при подсчете ресурсов-дескрипторов меню, иконок и подобного. Или действительно винда каким-то образом успевает дескриптор меню освободить до его отсоединения, но это противоречит MSDN.
vampir_infernal (2006-12-14 18:43:00) [2]я все таки считаю, что такой простой код проверки освобождения памяти ошибки давать не может:
unit checkMem;
{$WARN SYMBOL_PLATFORM OFF}
interface
implementation
uses sysUtils, kol;
var
HPs: THeapStatus;
HPe: THeapStatus;
lost: integer;
initialization
HPs := getHeapStatus;
finalization
HPe := getHeapStatus;
Lost := HPe.TotalAllocated - HPs.TotalAllocated;
if lost > 0 then
begin
beep;
ShowMessage(format('lostMem: %d', [lost]));
end;
end.
Vladimir Kladov (2006-12-13 20:01:00) [3]проблема в том, что мы не знаем, почему вызван деструктор. Заканчивается приложение или просто в коде кто-то объект формы убивает. Делать проверку AppletTerminated смысла нет, потому что это кода только добавит, но никак не убавит.
AndreyRus (2006-12-13 19:50:00) [4]
> GetClassInfo должен спрашиваться, чтобы не пытаться регистрировать
> по новой такой же класс окна.
А откуда он возьмется, если приложение только стартует?
> Все эти ресурсосчиталки, и мемпруф в том числе, просто лажаются.
Равно как и компиляторы, я уж не говорю о людях :)
Лично я никогда не трачу код на освобождение русурсов при закрытии приложения, т.к. это замечательно делает сборщик мусора - OS.
Vladimir Kladov (2006-12-13 16:38:00) [5]GetClassInfo должен спрашиваться, чтобы не пытаться регистрировать по новой такой же класс окна. В VCL вроде так же делается. Меню у меня уничтожается правильно. Сначало оно отсоединяется от окна, потом DestroyMenu. Все эти ресурсосчиталки, и мемпруф в том числе, просто лажаются. Я допускаю, что может быть система успела убить меню до его отсоединения еще до прихода WM_DESTROY, но нигде в документации не говорится, что это так. По крайней мере вызов DestroyXXXX для уже убитого дескриптора никаких неприятностей не даст, и утечек тоже.
Я как-то отправлял исправления по GRushControls недавно (от homm'а), думаю MTsvDN обновил у себя на сайте. Поди, проблемы с ресурсами в неисправленной версии, там не было вызова inherited в деструкторе.
SergeR (2006-12-13 12:14:00) [6]Еще по теме:
Если в на форму "кинуть" какой нибудь компонент из grushcontrols, то память так же не вся освобождается при дестрое, но только тогда, когда у компонента Caption не пустой.
Т.е. если ,например, панельке присвоить caption='something', то есть утечки.
AndreyRus (2006-12-12 20:15:00) [7]Испробовал я на досуге TracePlus/Win32 ( http://www.sstinc.com) для профилировки программы. IMHO? очень неплохая программа. Многое узнал о своей программе :) В программе все исправил, но остались недоразумения в KOL:
TRUE GetVersionExA( 0x004156C4 )
0x00010011 LoadCursorA( 0x00000000,0x00007F00 )
[*] GetClassInfoA( 0x00400000,0x00414334,0x0012FEF8 )
[*] GetClassInfoA( 0x00400000,0x0012FEB8,0x0012FE60 )
0xC1AD RegisterClassA( 0x0012FEF8 )
0x002A0510 (obj_Form) CreateWindowExA(0x10000,"obj_Form","MainForm",0x6CF0000,347,303,CW_USEDEFAULT,CW _USEDEFAULT,0x0,0x0,0x00400000)
* CreateMenu()
{...}
[*] DestroyMenu( 0x00370935 )
* RemoveMenu(*)
TRUE DestroyMenu( 0x02270853 )
[*] - Error!!!
Странно как-то создаются оконные элементы. В начале ошибочный запрос - GetClassInfoA, а затем создание и неладное что-то и с уничтожением меню.
Vladimir Kladov (2006-12-12 18:53:00) [8]Меню не наследуется от контрола, оно к нему привязвается. И неважно, к чему вы привязываете меню, раз вы его все равно привязываете, оно убьется автоматически. Всегда есть возможность проверить, остановиться в TMenu.Destroy.
Vladimir Kladov (2006-12-12 18:49:00) [9]Нет, не то. Кинулся искать баг, и нашел: в асм-версии действительно проблема в TObj.Final. Но меню ни при чем совершенно. Если просто закомментировать строчку:
//Result.MainMenu1 := NewMenu( Result.Form, 0, [ 'Test'], nil );
то в отчете MemProof совершенно ничего не меняется. А вот в случае PAS_VERSION меняется: остается только отчет об иконках.
vampir_infernal (2006-12-12 18:16:00) [10]Это меню, которое появляется над иконкой в трее. Наследуется оно от панельки размером 1 на 1, потому что если наследовать от формы, то меню автоматически вешается под загаловок и становится главным. Вот весь код всего модуля. В таком виде все работает и мемпруф молчит.
unit unMain;
interface
uses windows, messages, kol;
const Capt = 'Рассчет червячной передачи';
type
PfmMain = ^ TfmMain;
TfmMain = object( TObj )
private
Panel: PControl;
Menu: PMenu;
procedure ChToolTip(const Value: KOLstring);
procedure DoTrayMouse(Sender: PObj; Message: Word);
procedure DoWorkMenu(Sender: PMenu; Item: Integer);
procedure FormOnMinimize(Sender: Pobj);
procedure FormOnClose(Sender: PObj; var Accept: Boolean);
public
form: PControl;
Tray: PTrayIcon;
AcceptClose: boolean;
constructor Create;
destructor Destroy; virtual;
procedure ShowAbout;
end;
{$EXTERNALSYM ShellAbout}
function ShellAbout(Wnd: HWND; szApp, szOtherStuff: PChar; Icon: HICON): Integer; stdcall;
function NewFmMain : PfmMain;
implementation
function ShellAbout; external 'shell32.dll' name 'ShellAboutA';
{ TfmMain }
procedure TfmMain.ChToolTip(const Value: KOLstring);
var buf: KOLstring;
begin
buf := value;
if Length(buf) > 62 then
buf := Copy(buf, 1, 62)
else if Value <> '' then
Tray.Tooltip := buf;
buf := '';
end;
constructor TfmMain.Create;
begin
form := NewForm(applet, Capt).SetPosition(200, 200).SetSize(600, 400);
AcceptClose := False;
form.Style := form.Style and not WS_MAXIMIZEBOX;
form.SizeGrip := true;
form.Visible := true;
form.OnMinimize := FormOnMinimize;
form.OnClose := FormOnClose;
form.IconLoad(hInstance, 'MAINICON');
Panel := NewPanel(applet, esNone).SetSize(1, 1);
menu := NewMenu(panel, 0, ['Очистить поля', 'О программе', '-', 'Выход', ''], DoWorkMenu);
RemoveFromAutoFree(menu);
Tray := NewTrayIcon(panel, applet.Icon);
Tray.OnMouse := DoTrayMouse;
Tray.Active := true;
Tray.AutoRecreate := true;
ChToolTip('Рассчет червячной передачи');
end;
destructor TfmMain.Destroy;
begin
Free_And_Nil(Tray);
Free_and_Nil(panel);
Free_And_Nil(form);
Menu.Free;
Menu := nil;
inherited;
end;
function NewFmMain: PfmMain;
begin
New(Result, Create);
end;
procedure TfmMain.DoTrayMouse(Sender: PObj; Message: Word);
var
P: TPoint;
begin
case Message of
WM_RBUTTONUP:
begin
SetForegroundWindow(form.Handle);
GetCursorPos(P);
Menu.PopUpEx(P.X, P.Y);
end;
WM_LBUTTONUP:
begin
form.WindowState := wsNormal;
ShowWindow(FindWindow(nil, Capt), SW_SHOW);
SetForegroundWindow(form.Handle);
end;
end;
p := MakePoint(0,0);
end;
procedure TfmMain.DoWorkMenu(Sender: PMenu; Item: Integer);
begin
case Item of
// 0: CleanBoxes;
1: ShowAbout;
3: begin
AcceptClose := true;
PostMessage(Applet.Handle, WM_CLOSE, 0, 0);
end;
end;
end;
procedure TfmMain.FormOnClose(Sender: PObj; var Accept: Boolean);
begin
if not AcceptClose then
PControl(Sender).Visible := false;
Accept := AcceptClose;
end;
procedure TfmMain.FormOnMinimize(Sender: Pobj);
begin
PControl(Sender).WindowState := wsMinimized;
end;
procedure TfmMain.ShowAbout;
begin
ShellAbout(applet.handle, PChar(Capt), PChar('bla'), applet.Icon);
end;
end.
SergeR (2006-12-12 16:45:00) [11]
program Demo;
uses
// FastMM4,
Windows, kol;
type
PForm1 = ^TForm1;
TForm1 = object(TObj)
Form: PControl;
MainMenu1: PMenu;
public
end;
procedure NewForm1( var Result: PForm1; AParent: PControl );
begin
New( Result, Create );
Result.Form := NewForm( AParent, 'Demo' ).SetClientSize( 400, 400 );
Result.Form.Add2AutoFree( Result );
Result.MainMenu1 := NewMenu( Result.Form, 0, [ 'Test'], nil );
end;
var
Form1: PForm1;
begin
Applet := NewApplet( 'Test' );
NewForm1( Form1, Pointer( Applet ) );
Run( Applet );
end.
Вот на этот код FastMM4 и MemProof у меня ругаются
Vladimir Kladov (2006-12-12 15:19:00) [12]Тогда уточните: меню главное? Если главное, то при присваивании форме точно присоединяется. Вообще, кода приведено мало.
vampir_infernal (2006-12-11 19:11:00) [13]Это чистый KOL. Я не добавлял меню в AutoFree. Еслу оно само туда каким-то образом добавляется, то я это учту. Спасибо за помощь.
Vladimir Kladov (2006-12-11 16:57:00) [14]Начнем с того, что я не понял, что это такое:
destructor TfmMain.Destroy;
Free_And_Nil(Menu);
Free_And_Nil(Form);
inherited;
end;
Чистый KOL? Menu будет уничтожаться еще раз вместе с формой. Надо его отцепить RemoveFromAutoFree. В MCK первичен объект формы TControl, а не TObj, ее держащий. Соответственно, там надо уничтожать TControl, а он убьет держателя. А вы все делаете по-своему, не смотрите, что при этом делается, и хотите, чтобы заработало правильно.
SergeR (2006-12-11 10:53:00) [15]По теме:
Юзаю FastMM4 и MemProop. Они тоже ругаются на утечки памяти в проекте.
Для примера:
Создал новый KOL проект с пустой формой. Сохранил, запустил.
Оба менеджера ругаются на 40+16=56 байт не освобожденной памяти.
При добавлении на форму любых KOL компонентов объем "потерянной"
памяти увеличивается.
vampir_infernal (2006-12-10 20:30:00) [16]Открылись новые факты: все работает только с директивой PAS_VERSION. Без нее так же ругается не освобожденныу память.