Как вызвать private метод класса, находящегося в другом модуле
Иногда возникает необходимость вызвать private метод другого класса, расположенного в другом модуле. Это противоречит принципам ООП, заложенным в Delphi, но все-таки попробуем это сделать. Для примера рассмотрим случай, когда требуется сохранить/прочитать все свойства обьекта наследника TPersistent, например обьекта класса TFont.
В Delphi есть стандартные классы TReader,TWriter разработанные для сохранения/чтения свойств обьекта. В этих классах нам интересны методы TWriter.WriteProperties(Instance: TPersistent) и TReader.ReadProperty(AInstance: TPersistent). Метод WriteProperties позволяет сохранить в поток все свойства обьекта наследника TPersistent. Вызов в цикле метода ReadProperty позволяет прочитать из потока все сохраненные ранее свойства.
Рассмотрим сохранение свойств.
В Delphi5 все просто. Обьявление метода WriteProperties находится в Protected секции класса TWriter. Вызвать его особых проблем не составит:
type THackWriter = class(TWriter); .... THackWriter(Writer).WriteProperties(Instance); //вызов метода ....В Delphi4 все несколько сложнее. Метод WriteProperties находится в private секции класса TWriter. Стандартно вызвать этот метод можно только в рамках модуля где находится класс TWriter т.е. модуля classes.pas. Все, скажете вы, ситуация безнадежна, ведь добавить свой код в стандартный модуль classes.pas нельзя, вызвать метод WriteProperties из другого модуля тоже нельзя. Но я хочу показать, что выход из этой ситуации есть. Для начала заметим что:
WriteProperties статический метод. Тоесть адрес метода определяется на этапе компиляции проекта.
метод WriteProperties вызывается в public методе TWriter.WriteCollection.
Для вызова метода WriteProperties нам необходимо узнать его адрес. Попробуем его узнать через public метод WriteCollection. Сделаем простенький проект, в котором будет вызов метода WriteCollection. Поставим точку останова на вызов метода WriteCollection. Запустим проект и дойдем до точки останова. Откроем CPU window и войдем в метод WriteCollection, нажимая F7 (Trace Info). А теперь самое интересное: в методе WriteCollection найдем вызов метода WriteProperties и вычислим смещение (в байтах) команды call TWriter.WriteProperties относительно начала метода WriteCollection. В нашем случае оно равно $36+1 байт. И так, код для определения адреса метода WriteProperties будет выглядеть так:
var p: pointer; .... p := @TWriter.WriteCollection; Inc(PByte(p), $37); Inc(PByte(p), PInteger(p)^+4);добавим несколько дополнительных проверок для повышения надежности этого кода
var p: pointer; .... p := @TWriter.WriteCollection; Inc(PByte(p), $37); if PByte(PChar(p)-1)^<>$E8 then begin exit; end; Inc(PByte(p), PInteger(p)^+4); if PByte(p)^<>$55 then begin exit; end;адрес метода WriteProperties у нас уже есть, осталось только его вызвать
asm push eax push edx mov eax, Writer mov edx, Instance call p pop edx pop eax end;Аналогично можно вычислить адрес TReader.ReadProperty. Для Delphi3, CBuilder3,4 придется провести все вышеперечисленные операции еще раз.
В результате мы получили код, который можно использовать для сохранения в поток/чтения из потока всех свойства любого обьекта наследника TPersistent.
Где его можно использовать? Например, можно запомнить TEdit.Font или TForm.Icon или TImage.Picture.
В чем преимущества этого метода? Мы создали универсальные методы для сохранения/чтения всех свойств любого обьекта наследника TPersistent, получили небольшой по размеру код. И в конечном итоге научились вызывать private методы другого класса.
В чем его недостатки? "Плохой стиль" программирования, в обход принципов ООП. Теперь наш код неявным образом зависит от модуля classes.pas. Любое изменение в модуле classes.pas, в обьектах TWriter, TReader, в методах TWriter.WriteCollection, TReader.ReadCollection может превести к сбоям в работе разработанного нами кода. Причем мы не сможем увидеть это на этапе компиляции приложения, только в момент его работы. Но часто ли вы изменяли и перекомпилировали модуль classes.pas? мне кажется, что не очень. Вот и всё, Удачи!
Добавил(а): AtomicEdition | Дата: 2016-06-13 | |
|