Форма входа
Категории раздела
Delphi [24]
Статьи по программированию на Delphi.
html [42]
Статьи и помощь по html
I Love Bashorg
Главная » Статьи » Программирование » Delphi

Как вызвать 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? мне кажется, что не очень.


Весь код, приведенный выше, вы можете получить, скачав демонстрационный проект. Вы можете использовать его на свой страх и риск в своих приложениях. Но основное что я хотел вам показать - используя нестандартные методы програмирования можно добиться интересных результатов, даже вызвать private метод класса находящегося в другом модуле.

Все описанное выше возникло при работе над моим shareware проектом - Storage library. Там этот прием используется для сохранения/чтения всех свойств обьектов наследников TPersistent например TEdit.Font или TForm.Icon или TImage.Picture. Вы можете подробнее ознакомиться со Storage library на www сервере компании DeepSoftware

DownloadРазмерОписание
CallPrivate.zip4.5kbДемонстрационный проект. Delphi3/4/5 CBuilder 3/4/5 версия.



Автор статьи:  Андрей Руфин




Источник: http://www.delphimaster.ru/articles/callprivate/index.html
Категория: Delphi | Добавил: Bombers (11.10.2009)
Просмотров: 651 | Рейтинг: 0.0/0
Всего комментариев: 0
Пятница, 19.04.2024, 19:49
Приветствую Вас Гость
Статистика
  • Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
    Admin icq status
    587643917
    Друзья сайта