Недавно добавленные исходники

•  DeLiKaTeS Tetris (Тетрис)  3 669

•  TDictionary Custom Sort  5 800

•  Fast Watermark Sources  5 603

•  3D Designer  8 215

•  Sik Screen Capture  5 913

•  Patch Maker  6 388

•  Айболит (remote control)  6 378

•  ListBox Drag & Drop  5 237

•  Доска для игры Реверси  94 551

•  Графические эффекты  6 570

•  Рисование по маске  5 643

•  Перетаскивание изображений  4 759

•  Canvas Drawing  5 135

•  Рисование Луны  4 863

•  Поворот изображения  4 411

•  Рисование стержней  3 116

•  Paint on Shape  2 360

•  Генератор кроссвордов  3 228

•  Головоломка Paletto  2 551

•  Теорема Монжа об окружностях  3 328

•  Пазл Numbrix  2 200

•  Заборы и коммивояжеры  2 849

•  Игра HIP  1 820

•  Игра Go (Го)  1 740

•  Симулятор лифта  2 072

•  Программа укладки плитки  1 798

•  Генератор лабиринта  2 240

•  Проверка числового ввода  1 925

•  HEX View  2 225

•  Физический маятник  1 911

 
скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Delphi Sources

Как вызвать 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.

Download Размер Описание
CallPrivate.zip (не рабочая ссылка) 4.5kb Демонстрационный проект. Delphi3/4/5 CBuilder 3/4/5 версия.
Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.





Похожие по теме исходники

Сортировка методом Хоара

Метод Рунге-Кутта решения дифур

Метод Симпсона




Copyright © 2004-2025 "Delphi Sources" by BrokenByte Software. Delphi World FAQ

Группа ВКонтакте