![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Указатель на виртуальную функцию. Помогите.
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;