Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Указатель на виртуальную функцию. Помогите.


DevilDevil ©   (2007-08-02 12:25:00) 

Задача на самом деле шире...
Однако постепенно убирая из своего кода лишнее пришёл к следующему:


type
 TClass1 = class
   procedure AnotherVirtual; virtual;
   procedure SomeFunc; virtual;
 end;

procedure TClass1.AnotherVirtual;
begin end;

procedure TClass1.SomeFunc;
begin
 ShowMessage('это функция TClass1');
end;

function GetProc(CLS : TClass; ProcOffs : integer) : pointer;
var
 I : integer;
begin
 Result := pointer(integer(pointer(CLS)^) + ProcOffs);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
ProcOffs : integer;
P1, P2 : pointer;
begin

asm
  mov ProcOffs, VMTOFFSET TClass1.SomeFunc
end;

P1 := @TClass1.SomeFunc;  
P2 := GetProc(TClass1, ProcOffs);

{asm
   call P2
end; }

Caption := IntToStr(Integer(P1)) + '     ' + IntToStr(Integer(P2));
end;


В чём собственно ошибка?
- По идее, P1 должен быть равен P2, а не равен!

Причём ProcOffs всегда вычисляется корректно, - сейчас 4; если в объявлении, функции поменять местами, то 0.

Предположение одно: ошибка в функции GetProc(). Как ни старался, всё приводило к AccesViolation.

Заранее спасибо.


DevilDevil   (2007-08-02 15:31:00) [1]

:)

но мы не знаем, что содержалось в регистре eax до нашего асм-кода. Поэтому свободное использование регистров eax, edx и ecx допускается в функциях, полностью написанных на ассмеблере. Я не прав?


begin...end   (2007-08-02 14:32:00) [2]

> DevilDevil ©   (02.08.07 13:21) [7]

Доступ-то (для чтения) получить проще простого -- используя те же самые смещения от начала VMT. Вообще, небольшое описание VMT можно найти в справке (сделайте поиск по символам "VMT" и выберите статью "Class types").

По этим смещениям расположены указатели на сугубо служебные структуры, пользоваться которыми непосредственно вряд ли имеет смысл. Например, таблица FieldTable содержит описание published-полей класса -- их количество, имена и смещения -- и используется методом TObject.FieldAdress. DynamicTable представляет собой таблицу адресов динамических методов (эти методы отличаются от виртуальных алгоритмом поиска их адреса), и т.д.

> DevilDevil ©   (02.08.07 14:05) [10]

An asm statement must preserve the EDI, ESI, ESP, EBP, and EBX registers, but can freely modifythe EAX, ECX, and EDX registers.

(c) Справка, "The asm statement".


DevilDevil   (2007-08-02 14:05:00) [3]

:) наверное:

var
Obj: TClass1;
P: Pointer;
begin
Obj := TClass1.Create;
try
  P := GetProc(...);
  asm
    push eax
    MOV  EAX, Obj
    CALL P
    pop eax
  end
finally
  Obj.Free
end


DevilDevil   (2007-08-02 14:02:00) [4]

> begin...end ©   (02.08.07 13:49) [8]

Алексей, спасибо, по поводу Self я знаю. Просто конкретно в данном случае Self не используется. Дело не вэтом.

Меня сейчас больше интересует:
что в себе хранят IntfTable, AutoTable, InitTable, FieldTable, MethodTable и DynamicTable ? Как получить к ним доступ ?

Тема специфическая, информации по ней мало. Вот поэтому, пользуясь случаем, мне и хотелось бы поинтересоваться у настоящего Мастера.


begin...end   (2007-08-02 13:49:00) [5]

> DevilDevil ©   (02.08.07 13:17) [6]

См. справку, статью "Class methods". Это метод, который может быть вызван без создания экземпляра класса. Поэтому компилятор не позволяет обращаться к полям внутри классовых методов. И ещё: в отличие от обычных методов, в которых неявный параметр Self является указателем на экземпляр класса, в классовых методах Self -- это указатель на класс (т.е. на VMT).

Кстати, вызов метода "вручную" в приведённом коде не совсем корректен. Обычно передачей Self в метод занимается компилятор, но, раз уж мы решили вызывать метод самостоятельно, то и о передаче Self теперь должны заботиться мы. Иначе, если внутри метода будут обращения к полям или вызов других динамических/виртуальных методов, будет ошибка. В данном случае (неклассовый метод с register-соглашением о вызове) перед командой CALL нужно поместить в регистр EAX указатель на созданный экземпляр класса:

var
 Obj: TClass1;
 P: Pointer;
begin
 Obj := TClass1.Create;
 try
   P := GetProc(...);
   asm
     MOV  EAX, Obj
     CALL P
   end
 finally
   Obj.Free
 end
end


DevilDevil   (2007-08-02 13:21:00) [6]

что себе хранят IntfTable, AutoTable, InitTable, FieldTable, MethodTable и DynamicTable ? Как получить к ним доступ ?


DevilDevil   (2007-08-02 13:17:00) [7]

ещё вопрос (пользуясь случаем):

чем class function отличается от function?


DevilDevil   (2007-08-02 13:15:00) [8]

> begin...end ©   (02.08.07 13:11) [4]

Заработало!!!

Спасибо.


begin...end   (2007-08-02 13:11:00) [9]


> Result := pointer(integer(pointer(CLS)^) + ProcOffs);

Result := PPointer(Integer(CLS) + ProcOffs)^

Переменная типа TClass -- это указатель на VMT. VMT -- это массив адресов виртуальных методов. Следовательно, переменная типа TClass -- это указатель на адрес первого виртуального метода.

Ошибка в том, что Вы в своей функции прибавляли смещение не к указателю на адрес метода, а к самому адресу метода (т.е. адресу начала его кода).


Eraser   (2007-08-02 13:02:00) [10]


> Мне нужно узнать адрес функции класса, в VMT по смещению.

тогда возможно данная статья поможет http://rsdn.ru/article/Delphi/delphiabs.xml


DevilDevil   (2007-08-02 12:49:00) [11]

это не то.

Мне нужно узнать адрес функции класса, в VMT по смещению.


Eraser   (2007-08-02 12:41:00) [12]


> DevilDevil ©   (02.08.07 12:25) 

не совсем точно понял, что требуется, но глянь в сторону TMethod.
например
procedure ButtonClick (Self : TButton; Sender : TButton);
begin
 ShowMessageFmt ('Нажата кнопка %s', [Sender.Caption]);
 ShowMessageFmt ('Self = %s', [Self.Name]);
end;

procedure AssignButtonClick (const Button : TButton);
var Method : TMethod;
begin
 Method.Code := @ButtonClick;
 Method.Data := Button;
 Button.OnClick := TNotifyEvent (Method);
end;


Наверх

Титульная страница Поиск, карта сайта Написать письмо