|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Принудительное удаление объекта изнутри
Собственно есть код:
Код:
unit Unit1; interface uses SysUtils, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private FB:TButton; procedure FBClick(Sender: TObject); { Private declarations } public { Public declarations } end; TMButton = class(TButton) procedure Click; override; end; var Form1: TForm1; MB:TMButton; implementation {$R *.dfm} procedure CreateTMButton(); begin MB:=TMButton.Create(Form1); MB.Width:=Form1.ClientWidth; MB.Height:=Form1.ClientHeight-20; MB.Caption:='Class'; MB.Parent:=Form1; end; procedure TMButton.Click; begin inherited; //showmessage(inttostr(Integer(MB))); FreeAndNil(MB); //CreateTMButton(); //showmessage(inttostr(Integer(MB))); //Showmessage('MButtonClick'); end; procedure TForm1.FBClick(Sender: TObject); begin FreeAndNil(MB); //CreateTMButton(); //Showmessage('FBClick'); end; procedure TForm1.FormCreate(Sender: TObject); begin CreateTMButton(); FB:=TButton.Create(Form1); FB.Top:=Form1.ClientHeight-20; FB.Height:=20; FB.Width:=Form1.ClientWidth; FB.Caption:='Form'; FB.OnClick:=FBClick; FB.Parent:=Form1; end; end. В коде создается две кнопки: FB (для внешнего удаления), MB (для внутреннего удаления) При клике на FB все работает удовлетворительно. А при клике на MB происходит AV ошибка при выходе из процедуры из-за попытки передачи управления на уже несуществующие адреса. Вопрос в том как адекватно спилить под собой ветку и не налюбнутся с дерева? Тость, как можно адекватно(приближенно к правильному) удалять объект MB из той же TForm1.FBClick? Есть ли какие-то стандартные или нестандартные махинации со стеком или прыжками в иное место кода чтоб все адекватно удалялось и работало? Буду признателен даже за извращенные варианты (хоть эта задачка в принципе извращенная вроде как)... Так сказать если не для дела, то для общего развития и более глубоко понимания сгодятся |
#2
|
||||
|
||||
Уберите везде строчку с freeandnil и вставьте её первой в createtmbutton ввиде if MB <> nil then FreeAndNil(MB);
Я не понял Вашего вопроса, но всё же Вам на него отвечу! |
#3
|
|||
|
|||
Цитата:
Это ничего не изменит и будет примерно также если бы просто расскоментировать строчку CreateTMButton(); Работает (без AV) только из-за того что объект наново создается и в том же адресном пространстве... Но это чисто мое предположение. Я еще не вникал по случайности оно так получается или по какому-то задуманому механизму. Суть в том что надо вызывать FreeAndNil(MB) или его аналог в любом месте кода. Как во внутрях самого объекта MB так и за их предалами... И не важно будет ли объект пересоздан или нет Все это не извращенными людьми делается внешними вызовами (в отношении к MB) по типу TForm1.FBClick и разными вариациями на тему... PS: На коментарии в коде можно не смотреть. |
#4
|
||||
|
||||
Тогда проще из-самого-себя не удалять, а управлять видимостью
Я не понял Вашего вопроса, но всё же Вам на него отвечу! |
#5
|
|||
|
|||
Можно еще добавить вот так чтоб была более наглядно проилюстрированна суть извращения:
Код:
procedure TMButton.MyFree; begin FreeAndNil(MB); MB.I:=0; end; procedure TMButton.Click; begin inherited; MyFree(); end; Тойсть ошибка происходить из-за того что идет обращение к объекту или его частям после того как он был удален... Вопрос в том как излюбнуть систему чтоб управление после FreeAndNil(MB); переходило в какую-то часть программы и дальше все работало в штатном режиме как будто объекта MB и не бывало? Для людей не насилующих свой мозг: Удаление MB.I:=0; не решит проблему.. Так как "end" грубо говоря превращается в ассемблерный "ret" который вытаскивает адрес (удаленного объекта) из стека процесора и пытается с ним взаимодействовать Решение которое мне приходит на ум то это какие-то махинации со стеком или "jmp" в какой-то участок кода, или вообще все вместе... Но это пока еще вопрос как и не нарушу ли я этим какую-то священную логику самого самого Delphi |
#6
|
|||
|
|||
Цитата:
Что проще, знаю) Интересно как такое можно из самого себя провернуть) |
#7
|
||||
|
||||
Получилось "харакири" через PostMessage
Код:
... protected procedure WndProc(var Msg: TMessage); override; ... procedure TForm1.WndProc(var Msg: TMessage); begin case Msg.Msg of CM_RELEASE: FreeAndNil(MB); else inherited; end; end; ... procedure TMButton.Click; begin inherited; PostMessage(Form1.Handle, CM_RELEASE,0,0); end; Я не понял Вашего вопроса, но всё же Вам на него отвечу! |
#8
|
|||
|
|||
Ну еще можно COM попробовать (то бишь интерфейсы). Там вообще удалять не нужно - само умрет, когда счетчик ссылок обнулится.
|
#9
|
|||
|
|||
Цитата:
Пожалуй с сообщениями окна весьма неплох вариант если развить и немного облагородить небольшой оберткой) Да и в целом идея в объекте ставить его в очередь на удаление, а удалять уже у в другом объекте наверно лучшее что можно с этой задачей придумать Но как-то не покидают голову совсем трешовые идеи с наглыми прыжками по коду. К примеру удалить объект и прыгнуть в тот же обработчик сообщений окна... В общем, надо повкуривать отладчик и понять что эта идея если не жутко баговая выйдет, так вообще провальная) Последний раз редактировалось MProg, 12.04.2017 в 23:04. |
#10
|
|||
|
|||
Цитата:
Не вникал в COM, но идея в том чтоб именно самому "ручками" удалять. Запилить некое подобие сборщика мусора который теми или иными способами следит за объектам, ставит их в очередь, потом каким-то механизмом удаляет вроде не проблема без добавления 10 технологий |
#11
|
||||
|
||||
Если переделывать Ваш пример, то получается:
Код:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) FB: TButton; procedure FBClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; TMButton = class(TButton) procedure Click; override; procedure CMRelease(var Message: TMessage); message CM_RELEASE; end; var Form1: TForm1; MB: TMButton; implementation {$R *.dfm} procedure CreateTMButton(); begin MB := TMButton.Create(Form1); MB.Width := Form1.ClientWidth; MB.Height := Form1.ClientHeight - 20; MB.Caption := 'Class'; MB.Parent := Form1; end; procedure TMButton.Click; begin inherited; //PostMessage(Self.Handle, CM_RELEASE, 0, 0); PostMessage(MB.Handle, CM_RELEASE, 0, 0); //showmessage(inttostr(Integer(MB))); //FreeAndNil(MB); //CreateTMButton(); //showmessage(inttostr(Integer(MB))); //Showmessage('MButtonClick'); end; procedure TMButton.CMRelease(var Message: TMessage); begin FreeAndNil(Self); end; procedure TForm1.FBClick(Sender: TObject); begin FreeAndNil(MB); //CreateTMButton(); //Showmessage('FBClick'); end; procedure TForm1.FormCreate(Sender: TObject); begin CreateTMButton(); FB := TButton.Create(Form1); FB.Top := Form1.ClientHeight - 20; FB.Height := 20; FB.Width := Form1.ClientWidth; FB.Caption := 'Form'; FB.OnClick := FBClick; FB.Parent := Form1; end; end. Но можно и без ввода дополнительных классов, а "шаманским методом Гео" (ищи на DK) изменить стандартный класс. В таком случае уже все стандартные компоненты "модифицированного" класса, установленные ранее на форме, будут иметь "модифицированные" свойства и методы. Ну и и их можно не создавать динамически. Код:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TButton= class(StdCtrls.TButton) public procedure Click; override; procedure Release; protected procedure CMRelease(var Message: TMessage); message CM_RELEASE; end; TForm1 = class(TForm) FB: TButton; MB: TButton; procedure FBClick(Sender: TObject); procedure MBClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} { TButton } procedure TButton.Click; begin inherited; Release; end; procedure TButton.CMRelease(var Message: TMessage); begin FreeAndNil(Self); end; procedure TButton.Release; begin PostMessage(Self.Handle, CM_RELEASE, 0, 0); end; { TForm1 } procedure TForm1.FBClick(Sender: TObject); begin FreeAndNil(MB); end; procedure TForm1.MBClick(Sender: TObject); begin MB.Release; end; end. Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. |
#12
|
|||
|
|||
Цитата:
Пардонте редкому обитателю форумов, а что такое "DK"? По поводу жесткого посягательства на код программы и не менее жесткой наркомании(пример Delphi 7): Код:
program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); asm mov eax,esp sub eax,$8 mov [Unit1.pRunStack],eax end; pRunLoop:=Addr(TApplication.Run); pRunLoop:=Pointer(Integer(pRunLoop)+133); Application.Run; end. Код:
unit Unit1; interface uses SysUtils, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private FB:TButton; procedure FBClick(Sender: TObject); public end; TMButton = class(TButton) procedure Click; override; end; var Form1: TForm1; MB:TMButton; pRunLoop:Pointer; pRunStack:Pointer; implementation {$R *.dfm} procedure CreateTMButton(); begin MB:=TMButton.Create(nil); MB.Width:=Form1.ClientWidth; MB.Height:=Form1.ClientHeight-20; MB.Caption:='Class'; MB.Parent:=Form1; end; procedure TMButton.Click; var r:real; begin inherited; FreeAndNil(MB); asm mov ebp,[pRunStack] jmp [pRunLoop] end; end; procedure TForm1.FBClick(Sender: TObject); begin //FreeAndNil(MB); if(MB=nil)then CreateTMButton(); end; procedure TForm1.FormCreate(Sender: TObject); begin CreateTMButton(); FB:=TButton.Create(Form1); FB.Top:=Form1.ClientHeight-20; FB.Height:=20; FB.Width:=Form1.ClientWidth; FB.Caption:='Form'; FB.OnClick:=FBClick; FB.Parent:=Form1; end; end. На удивление и первый взгляд это безобразие даже как-то сносно работает) Вот только явно не учтено еще как минимум парочку каких-то моментов.. После срабатывания TMButton.Click и закрытия окна, уже радостно встречает другое AV... так, чисто легонько намекая что если уж посягаешься, то посягайся глубже Последний раз редактировалось MProg, 13.04.2017 в 12:03. |
#13
|
||||
|
||||
Цитата:
Грамотно поставленный вопрос содержит не менее 50% ответа. Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть. |
#14
|
||||
|
||||
Оффтоп:
Цитата:
Я не понял Вашего вопроса, но всё же Вам на него отвечу! |
Этот пользователь сказал Спасибо Alegun за это полезное сообщение: | ||
lmikle (14.04.2017)
|
#15
|
|||
|
|||
Цитата:
Да и куда занятней пусть даже вокруг дома погасать на "Лесодрели" нежели просто лежать и мечтать о том что кто-то вскоре что-то подобное или даже лучне придумает. В моей конечной цели (примитивный API для работы с фреймами и управления ими на формах) некая обертка очень даже кстати.. Чтоб в результате были функции инициализации, деинициализации, парочку рабочих. Удалять фрейм изнутри вроде как удобненько, а какой-то громоздкий механизм управления фреймами городить не хотелось. Вот и стало интересно что опытные люди используют для таких задач из стандартного или не совсем стандартного. В итоге заюзаю сообщения окна для большей предсказуемости и адекватности) Еще как вариант можно очередь на удаления сделать, в удаляемом объекте ставить его в очередь на удаление, а в рабочих функциях при их запуске из других объектов проверять очередь и удалять. Это вариант который приходит в голову чтоб не использовать дополнительные потоки, таймеры и тому подобное. Ассемблерное извращение это чисто любопытство на тему и желания познать внутренности работы. В нем минусов куда больше, чем плюсов и решение пожалуй для очень специфических задач. В общем тема вроде как исчерпана и в целом алгоритмы действий понятны) Всем спасибо А если у кого-то имеется внушительный опыт и не менее внушительные познания внутренностей, буду рад почитать о плохих вещах (по типу моей идеи с ассемблерными вставками) |