Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

СообЧА. Программирование на Delphi


Информационный Канал Subscribe.Ru - www.lycos.ru

Subscribe.Ru :СообЧа программирование на дельфи !
—Сообча : программирование на дельфи

Некому оформить сайт ?
Нужен красивый баннер ?
Нет времени на обновление сайта?

Качественное оформление, работа с самыми современными средствами, FLASH,CGI,JAVA
Тогда вам сюда !!!!
Менее чем за 50$ мы поможем


----- (перед просмотром рассылку лучше сохранить)

 

 В этом выпуске:

 

Фонд поддержки наших проектов и рассылки:
Получатель: ИНН 7707083893 Новгородское ОСБ № 8629
Счет получателя: 47422810343029900030
Банк получателя
:Новгородское ОСБ № 8629 г.Великий Новгород 30101810100000000698 Бик 044959698

Храпунову Кириллу Алексеевичу

! желающим помочь рассылке


Рассылки Subscribe.Ru это стильно удобно, и информативно!
СообЧа (СООБщество ЧАйников). Обмен опытом, вопросы, ответы.


подпишись и подпиши друга!!!!

 Contact (Связь с Нами):

Pixel@novgorod.net + Subject: (см ниже)

 

Vcl Haunting

"Золотой Чайник"

Вопрос по дельфи N (N номер версии)

Help!

Реклама

Полезный линк

 

Наш сайт : pixelsoft.narod.ru

Новости СЕТИ

К заголовку

Приношу извинения за ОГРОМНОЕ количество опечаток и недоделок , которые попали в прошлый выпуск. В тот день вообще жизнь не удалась ... (Виды сдохли, софт потерял любимый , да еще и с 5-ти утра на ногах был...)

 

Книги по Дельфи которые ВЫ ОБЯЗАНЫ ПРОЧИТАТЬ...

Не столько учебник, сколько справочное пособие по наиболее используемым алгоритмам и командам...

Пожалуй лучший учебник по дельфи 6 на сегодняшний день, вам даже не надо знать дельфи, чтобы начать ...

  • Дельфи 5: Руководство разработчика БД
  •  

    DirectX. Графика в проектах Delphi (+CD - ROM)
    Лучшее пособие для тех кто решил связаться с DirectX, по слухам на CD помимо примеров есть DirectxSDK7.0 от Microsoft...

    Среда программирования Delphi 5-6. Справочное пособие
    Книга полностью описывает среду программирования Delphi, которая включает в себя полный набор визуальных инструментов для быстрой и профессиональной разработки приложений для различных операционных систем, кроме того рассмотрены проблемы перехода между этими версиями дельфи.

    Программирование в Delphi 6 (+ floppy дискета ) Чайникам рекомендуется!!!

    Книга содержит методические и справочные материалы по новой версии системы визуального объектно-ориентированного программирования Delphi 6 и предшествующим версиям Delphi 5 и 4. Рассмотрены такие новые возможности Delphi, как кросс-платформенные приложения, технологии доступа к данным ADO, InterBase Express, dbExpress, компоненты — серверы СОМ, технологии распределенных приложений СОМ, CORBA, MIDAS, новая методика диспетчеризации действий...

 


Еще Немного новостей-2...

К заголовку

Статья от прошлого выпуска... часть 2-я

Вернёмся к структуре RTTI класса TForm1. По смещению 14h находится указатель на компоненты, которыми владеет данный класс. Это все элементы списка Components во время разработки. Эта структура имеет довольно простой вид:

Структура RTTI класса TForm1
Смещение
Тип
Описание
0
Word
число CompCount различных классов компонентов
2
Dword
указатель на массив указателей на структуры RTTI этих классов. Первым элементом этого массива является WORD - число его элементов, далее расположены указатели на структуры RTTI

Сразу вслед за ней идут CompCount структур, описывающих эти компоненты:

CompCount структур
Смещение
Тип
Описание
0
Word
смещение в классе, по которому находится указатель на компонент
1
Word
- unknown -
2
Word
индекс в массиве структур RTTI - по нему определяется класс компонента
n+2
Word
длина Pascal-строки
n+6
String
имя компонента (например, Edit1)

Самым важным здесь являются смещение на компонент во включающем классе и его тип. Запомним их для компонентов в форме TForm1:
Смещение на компонент во включающем классе и его тип
Имя компонента
Смещение в классе
Тип компонента
Edit1
02C4h
0 - TEdit
Edit2
02C8h
0 - TEdit
Button1
02CCh
1- TButton
Button2
02d0h
1- TButton
Button3
02d4h
1- TButton
BitBtn1
02D8h
2 - TBitBtn
BitBtn1
02DCh
2 - TBitBtn
BitBtn1
02E0h
2 - TBitBtn

По смещению 2Ch идёт таблица методов. Порядок следования методов в ней мне не до конца ясен, однако я уверен, что в ней должны содержаться конструктор и деструктор данного класса.

Настало время рассмотреть обнаруженные нами методы подробнее. Я рассмотрю их в том порядке, в каком их расположила Delphi в массиве обработчиков событий.

BitBtn1Click

BitBtn1Click proc near
push ebx
mov ebx, eax
push 0
loc_0_444149:
mov cx, ds:word_0_444168
mov dl, 3
mov eax, offset aBitbtn1click
call @MessageDlg
loc_0_44415C:
mov dword ptr [ebx+22Ch], 1
pop ebx
retn
BitBtn1Click endp
Простой и понятный код. Подспудно выясняется, что закрытие формы осуществляется записью DWORD'а (ModalResult) по смещению 022Ch в экземпляре классе. Обратите внимание на механизм передачи параметров - по умолчанию Delphi использует соглашение вызова register - параметры передаются слева-направо, используя регистры EAX, EDX и ECX, очистку стека производит вызываемая функция. Соответственно, первый (неявный) аргумент для этой функции, представляющий собой указатель на класс, передаётся в регистре EAX.
OnFormShow

OnFormShow proc near
push ebx
mov ebx, eax
push 0
mov cx, ds:word_0_4441F4
mov dl, 3
mov eax, offset aFormshow
call @MessageDlg
mov eax, [ebx+2DCh]
mov [eax+108h], ebx
mov dword ptr [eax+104h], offset MyClickHandler
pop ebx
retn
OnFormShow endp
Здесь тоже можно увидеть кое-что интересное. Во-первых, смещение 02DCh не напоминает Вам о компоненте BitBtn2? Во-вторых, обратите внимание, что здесь присваиваются два указателя. Почему? Потому что мы присваиваем не просто указатель на функцию. Все обработчики являются "of object" - т.е. методами классов. Соответственно, присваивается сначала указатель на экземпляр класса (в данном случае Self) по смещению 0108h, а затем - указатель на нашу функцию MyClickHandler(). Замечу, что больше указатель на эту функцию не встречается. Это сильно затрудняет поиск динамически назначенных обработчиков событий. Нам может помочь только ещё одно обстоятельство - все строковые константы, используемые в функции, Delphi располагает следом за самой функцией.
Button1Click

Button1Click proc near
var_10 = dword ptr -10h
var_C = dword ptr -0Ch
var_8 = dword ptr -8
var_4 = dword ptr -4
push ebp
mov ebp, esp ; фрейм стека для локальных переменных
xor ecx, ecx
push ecx
push ecx
push ecx
push ecx ; 4 нуля в стек
push ebx
mov ebx, eax ; в eax - указатель на экземпляр класса
xor eax, eax
push ebp
push offset loc_0_4442B0
push dword ptr fs:[eax]
mov fs:[eax], esp
...
loc_0_4442B0:
jmp @@HandleFinally
IDA Pro неправильно опознала аргументы функций - ведь они передаются в регистрах, а не через стек. Кроме того, здесь задействуется механизм обработки исключений. Для передачи управления при исключениях Delphi использует сегментный регистр FS - в FS:[0] помещается текущий указатель стека ESP, предыдущее же значение перед этим помещается в стек. Кроме того, в стек также помещается адрес функции - обработчика блока finally. Также обратите внимание на инициализацию четырёх локальных переменных типа DWORD нулями.
lea edx, [ebp+var_C]
mov eax, [ebx+2C8h] ; смещение 02C8h не напоминает Вам о Edit2?
call @TControl@GetText ; TControl::GetText
mov eax, [ebp+var_C]
lea edx, [ebp+var_8]
call @Trim
mov eax, [ebp+var_8]
push eax
lea edx, [ebp+var_C]
mov eax, [ebx+2C4h] ; а 02C4h - о Edit1?
call @TControl@GetText ; TControl::GetText
mov eax, [ebp+var_C]
lea edx, [ebp+var_10]
call @Trim
mov edx, [ebp+var_10]
lea eax, [ebp+var_4]
pop ecx
call @@LStrCat3 ; ::'intcls'::LStrCat3
push 1
mov eax, [ebp+var_4]
call @@LStrToPChar ; ::'intcls'::LStrToPChar
mov edx, eax
mov ecx, offset aButton1click
mov eax, ds:off_0_445CDC
mov eax, [eax]
call @TApplication@MessageBox ; TApplication::MessageBox
В общем-то, в этом коде нет ничего примечательного, но можно выяснить, что по адресу 00445CDCh находится указатель на экземпляр класса Application.
xor eax, eax
pop edx
pop ecx
pop ecx
mov fs:[eax], edx
push offset loc_0_4442B7

loc_0_444292: ; CODE XREF: CODE:004442B5 j
lea eax, [ebp+var_10]
call @@LStrClr ; ::intcls'::LStrClr
lea eax, [ebp+var_C]
call @@LStrClr ; ::intcls'::LStrClr
lea eax, [ebp+var_8]
mov edx, 2
call @@LStrArrayClr ; ::intcls'::LStrArrayClr
retn
...
offset loc_0_4442B7:
pop ebx
mov esp, ebp
pop ebp
retn
Рассмотрим восстановление стека подробнее. В стеке в настоящий момент содержится:
FS:[0]
указатель на finally-функцию
EBP - прежнее значение стека
EBX
ECX = 0
ECX = 0
ECX = 0
ECX = 0
оригинальное значение EBP
адрес возврата из функции
Хотя перед этим в стек была помещена 1 - её нет в стеке. Почему? Потому что она является последним аргументом функции TApplication::MessageBox(). Но ведь у этой функции всего три аргумента, и они все передаются в регистрах - скажете Вы! Ничего подобного, Вы забыли, что всем методам классов передаётся неявно ещё один аргумент (под номером ноль) - указатель на экземпляр класса. При возврате же вызываемая функция сама производит очистку стека.
Итак, сначала извлекается предыдущее значение FS:[0], указатель на finally-функцию и прежнее значение стека, и восстанавливается значение FS:[0]. Дальше в стек помещается адрес процедуры очистки стека. После инструкции retn стек будет выглядеть так:

EBX
ECX = 0
ECX = 0
ECX = 0
ECX = 0
оригинальное значение EBP
адрес возврата из функции
Далее снимается оригинальное значение регистра EBX, стек восстанавливается в первоначальное состояние (которое хранилось всё время выполнения процедуры в регистре EBP). Стек сейчас выглядит так:
оригинальное значение EBP
адрес возврата функции
Восстанавливается предыдущее значение регистра EBP (указатель стека для вызывающей процедуры) и после инструкции retn мы возвращаемся в вызывающую функцию с полностью восстановленным стеком.
Button2Click

Button2Click proc near
push ebx
push esi
mov ebx, eax ; в eax - указатель на экземпляр класса
push 0
mov cx, ds:word_0_44431C
mov dl, 3
mov eax, offset aButton2click_0
call @MessageDlg
mov esi, [ebx+2C4h] ; смещение на Edit1
mov eax, esi
mov edx, [eax]
call dword ptr [edx+50h] ; вызов TEdit::GetEnabled
mov edx, eax ; результат в eax
xor dl, 1 ; xor boolean с 1 - его же not
mov eax, esi
mov ecx, [eax]
call dword ptr [ecx+60h] ; вызов TEdit::SetEnabled
mov esi, [ebx+2D4h] ; смещение на Button3
mov eax, esi
mov edx, [eax]
call dword ptr [edx+50h]
mov edx, eax
xor dl, 1
mov eax, esi
mov ecx, [eax]
call dword ptr [ecx+60h]
pop esi
pop ebx
retn
Button2Click endp
Эта функция инвертирует свойство Enabled поля ввода и кнопки. Свойство Enabled определено для класса TComponent (общий предок для TEdit и TButton) так:


--------------------------------------------------------------------------------
property Enabled: Boolean read GetEnabled write SetEnabled

stored IsEnabledStored default True;

Доступ к этому свойству осуществляется через методы GetEnabled & SetEnabled, что мы и видим здесь - через индекс в VTBL.

Часть 2. Обработка исключений и сообщений

Итак, продолжим. На сей раз я наваял приложение, использующее несколько более продвинутые технологии, предоставляемые Delphi - exceptions handling ( перехват исключений ), virtual & dynamic функции, обработку формой сообщений Windows, производные классы и загрузку строковых ресурсов из реестра. Исходный код моей программы мог бы выглядеть как-нибудь так:

--------------------------------------------------------------------------------
uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type

TRPEnum = ( RP_One, RP_Two, RP_Tree );
TRPEnumSet = set of TRPEnum;


TRPException = class(Exception)
private
RP_Array: array[7..9] of string;
Code: TRPEnumSet;
public
Procedure Old_one_virtual; virtual;
Procedure Old_one_dynamic; dynamic;
Constructor Create;
Destructor Destroy; override;
end;


TRPExceptionChild = class(TRPException)
Procedure Old_one_virtual; override;
Procedure Old_one_dynamic; override;
end;


TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FDesignedWidth, FDesignedHeight: Integer;
procedure BuggyOne;
Procedure WMSizing( var Message: TMessage ); message WM_SIZING;
public
{ Public declarations }
end;

var

Form1: TForm1;

implementation

{$R *.DFM}

resourcestring

BuggyOneCaption = 'BuggyOne';
MalformedException = 'Malformed exception';

(* TRPException *)
Constructor TRPException.Create;
begin

Application.MessageBox('Create', 'TRPException', ID_OK);
inherited Create('BuggyOne object');
end;

Destructor TRPException.Destroy;
begin

Application.MessageBox('Destroy', 'TRPException', ID_OK);
Inherited;
end;

Procedure TRPException.Old_one_virtual;
begin

Application.MessageBox('Old_one_virtual','TRPException', ID_OK);
end;

Procedure TRPException.Old_one_dynamic;
begin

Application.MessageBox('Old_one_dynamic','TRPException', ID_OK);
end;

(* TRPExceptionChild *)
Procedure TRPExceptionChild.Old_one_virtual;
begin

Application.MessageBox('Old_one_virtual','TRPExceptionChild', ID_OK);
end;

Procedure TRPExceptionChild.Old_one_dynamic;
begin

Application.MessageBox('Old_one_dynamic','TRPExceptionChild', ID_OK);
end;

(* TForm1 *)
procedure TForm1.BuggyOne;
var

RP_E: TRPExceptionChild;
N: Integer;
begin

MessageDlg(BuggyOneCaption,mtConfirmation,[mbOk],0);
try

RP_E := TRPExceptionChild.Create;
RP_E.Code := [RP_One];
RP_E.RP_Array[7] := 'Seven';
N := 9;
RP_E.RP_Array[8] := 'Eight';
RP_E.RP_Array[N] := 'Nine inch nails';
RP_E.Code := RP_E.Code + [RP_Two];
Raise RP_E;
MessageDlg('Not will showed at the end',mtConfirmation,[mbOk],0);
finally

MessageDlg('In finally part',mtConfirmation,[mbOk],0);
end;

MessageDlg('Not will showed at the end',mtConfirmation,[mbOk],0);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin

MessageDlg('Button1Click',mtConfirmation,[mbOk],0);
try
BuggyOne;
except
on E:TRPException do
begin
MessageDlg('Button1Click in exception block',mtConfirmation,[mbOk],0);
E.Old_one_virtual;
E.Old_one_dynamic;
end;
on E:TRPExceptionChild do
begin
MessageDlg(MalformedException,mtConfirmation,[mbOk],0);
end;
end;
MessageDlg('Button1Click at the end',mtConfirmation,[mbOk],0);
end;

Procedure TForm1.WMSizing( var Message: TMessage );
var

PRect : ^TRect;
Begin

PRect := Pointer (Message . LParam );
if PRect^. Right - PRect^. Left < FDesignedWidth then
begin
if Message.WParam in [ WMSZ_BOTTOMLEFT, WMSZ_LEFT, WMSZ_TOPLEFT ]
then
PRect^.Left := PRect^ . Right - FDesignedWidth
else
PRect^.Right := PRect^ . Left + FDesignedWidth;
end;
if PRect^ . Bottom - Prect^.Top < FDesignedHeight then
begin
if Message . WParam in [ WMSZ_TOP, WMSZ_TOPLEFT, WMSZ_TOPRIGHT ]
then
PRect^.Top := PRect^ . Bottom - FDesignedHeight
else
PRect^. Bottom := PRect^ . Top + FDesignedHeight;
end;
End;

procedure TForm1.FormCreate(Sender: TObject);
begin

FDesignedWidth := Width;
FDesignedHeight := Height;
MessageDlg('FormCreate',mtConfirmation,[mbOk],0);
end;

Не вершина программистского мастерства, конечно, но для наших целей вполне годится. Итак, запустим дизассемблер ( я использовал IDA 3.8b ) и не забудьте применить файл сигнатур для библиотеки VCL версии 4 ( d4vcl ) - в моём случае IDA опознала 2172 функции.

А пока IDA делает грязную работу за нас, можно предаться чтению документации ( весьма рекомендую заниматься этим время от времени - можно узнать столько интересного :-). Итак, что мы можем узнать из официальной документации по Delphi о тонкой разнице между динамическими (dynamic) и виртуальными (virtual) методами ?

Virtual методы расположены в таблице виртуальных методах, по традиции называемой VTBL, которая дублируется для каждого производного класса. Если в производном классе переопределяется новый метод, указатель на него будет в этой таблице под тем же индексом, что и в VTBL класса-предка - но указывать он будет на перегруженный метод. За счёт этого достигается наилучшая скорость - вызов функции по указателю через смещение в VTBL. С другой стороны, для каждого нового класса полностью дублируется вся VTBL ! Короче, классический случай ножниц "скорость против размера".

Dynamic методы имеют несколько другой способ хранения. Им назначается некоторый индекс - но не в таблице, а в hash-структуре. Также эта структура не дублируется для каждого производного класса - если переопределяется dynamic метод, он переопределяется для данного класса - и всё. Но вызов dynamic методов имеет больше накладных расходов - при вызове Delphi просматривает все классы-предки данного класса в поисках метода с нужным индексом.


Продолжение ЭТОЙ статьи следует...


 

 

Zaluskiy Anton(COOLer)  и Khrapunov Kirill(Pixel)  - ведущие проекта    "Мир Delphi" (C) Pixelsoftware(Pixel)& Delphi 2000-2002(COOLer)


http://subscribe.ru/
E-mail: ask@subscrib
e.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное