Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Не правильное освобождение 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. Без нее так же ругается не освобожденныу память.


Наверх

Титульная страница Поиск, карта сайта Написать письмо