![]() |
|
![]() ![]() ![]() |
|
Fantasist |
|
|||
![]() Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: нет Всего: 41 |
Вот тут случайно втянулся в разрабоку поддержки COM для KOL. KOL - это такая библиотека написанная под Делфи без использования VCL, и что самое заковыристое - без исползования классов, а на чистом Object Pascal. В это и заключается проблема поддержки COM - в Делфи интервейсы встраиваются в классы, но классов нет нет и интерфейсов. А значит надо за компилятор делать работу по созданию таблиц. Вот код - весь его читать не нужно, если только сами не хотите над это проблемой подумать - нужно обратить внимание на асмовский код в процедурах:
В чем хитрость? Хитрость в том, что я передаю указатель на подставной объект (InterfaceSet), который якобы имеет таблицу виртуальных методов. На самом деле, эта таблица набор указателей, значения которых являются адресами методов главного объекта (самый последний метод в листинге). Так вот дело в том, что метод-то по адресу вызывается тот что нужен, но в параметре self естесственно передается указатель на мой подставной объект, а не на реальный. Но в подставном объекте я храню указатель, на реальный объект, и код на асме в каждом методе как раз для того, чтобы переписать self этим правильным значением. Но асм то я не знаю. Вот и вопрос - можно это как-нибудь упростить? Лучше всего конечно обернуть это в функцию, но без асмовской вставки, кажись, все равно не обойтись. Будут идеи? -------------------- Волны гасят ветер... |
|||
|
||||
Chingachguk |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1232 Регистрация: 25.3.2002 Где: Москва Репутация: нет Всего: 18 |
Не знаю, что все это извращение означает, оптимизация тут не причем: скорее, обходятся стандартные ограничения языка (разнотипные присваиваивания и т.п.). Вот попытка упростить этот код:
Образно говоря, делается следующее: Данную функцию можно представить себе как ф-цию, получающую как минимум один параметр типа long(4 байта). Скорее всего это указатель на что-то: function TInterfacedObj.AddRef: Longint; <=> function TestFunc(...Pn): Longint; При этом ПОСЛЕДНИЙ (или вообще один) параметр я называю Pn. Далее делается следующее: - Получается ЗНАЧЕНИЕ этого параметра Pn: mov eax,ebp ; Получить указатель на стековый фрейм eax->Сохраненный ebp add eax,$08 ; Указать на параметр Pn mov edx,[eax] ; Получит его в edx И мы получили значение Pn в регистре edx. - Судя по всему, далее это значение трактуется как указатель на какую-то структуру (указатели на методы ?) размером по 4 байта (long) и выбирается второй элемент: add edx,$04 ; Сослаться на второй элемент (Pn+4) mov edx,[edx] ; Получить зачение элемента Table[2] в edx - После этого на место СТАРОГО параметра Pn, переданного в функцию TestFunc кладется значение этого самого Pn[2]: mov [eax],edx ; Поместить вместо переданного Pn этот элемент. Зачем все это нужно, тебе виднее ;) -------------------- I don't like the drugs (but the drugs like me). M.Manson. |
|||
|
||||
Fantasist |
|
||||||||||||||||||||||
![]() Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: нет Всего: 41 |
Что-то я не понял, где тут упрощение.
![]()
Это, однозначно, указатель на экземпляр класса. Тот что self называется.
Почему последний? Он должен быть первым. Или в том смысле, что он последний в стеке? Что этот код делает я знаю - я же его сам написал. Асм я, конечно, можно сказать не знаю, но не до такой степени чтобы совсем не понимать. У компилера подсмотрел, как правильно воспользоваться командами mov и add и какие регистры использовать. А в коде делается вот что (я там выше правда уже писал). Посмотри на тип InterfaceSet в самом начале листинга. Член _vmt - это указатель на таблицу указателей. В эти указатели я записываю адреса методов, а клиентский код потом по ним эти методы вызывает.
Если выбросить переменную this, то получиться чистый COM интерфейс. Обычно на него передают указатель, и клиентский код, зная смещения соответсвующих методов, находит правильный указатель на метод. То есть если у нас есть интерфейс IUnknown то по сути это такая структура:
и в памяти она распологается так:
_vmt - это "переменная" генерируется компилятором и является указателем, на первый указатель на метод этого интерфейса. Теперь зная значение _vmt мы можем вызвать метод AddRef так: мы знаем, что AddRef - это второй метод, а размер указателя 4 байта - берем значение _vmt прибавляем к нему 4 - получаем адрес по которому находиться pointer2, который и содержит адрес метода AddRef. По этому адресу вызываем метод. В COM мы передаем указатель на этот _vmt. Так что клиент знает этот указатель, и знает в каком порядке расположенны методы - на этой информации он и вызывает нужные методы. Но так как это методы, которые принадлежат объекту, то чтобы знать, для какого объекта этот метод вызывается, компилятором генерируется еще один параметр, который вставляется перед всеми остальными параметрами, и в этом параметре он передает указатель на объект, для которого этот метод был вызван. Например для того же IUnknown откомпилированные функции на самом деле будут выглядеть так:
В COM это стандартизированно. Теперь клиент знает имеет указатель на _vmt, и в качестве переменной self он и передает этот указатель. То есть вызов AddRef примерно такой (Ipointer - это указатель на _vmt):
Теперь посмотрим, что делается в методе AddRef:
Так, как fRefCount - это член класса, то на самом деле это выглядет так:
Я так чувствую, что в общем-то то, о чем я все говорил и так известно. Теперь в чем проблема. Посмотрим на последний метод из моего листинга:
Как видно, в переменной fIUnknown я создаю как бы создаю виртуальную таблицу - то есть присваиваю указателям значения адресов методов класса TInterfacedObj. Потом я делаю так:
И потом, я передаю адрес ifc COM клиенту, как указатель на IUnknown. То есть я создаю таблицу методов в ручную и передаю клиенту. Теперь клиент вызывает с помощью этой таблицы AddRef. А у нас в AddRef делается: Inc(self^.fRefCount) (это так компилятор это сделал). Но что клиент передаст в качестве self? Естесственно указатель на переменную ifc, который я сам ему вначале и передал. Но мне-то нужен указатель на TInterfacedObject! Ибо в нем эта переменная, которую мне надо поменять. Что делать? У меня в ifc записан указатель на объект TInterfacedObject. Теперь, все что мне нужно, это переписать self этим значением. Для этого я и написал тот код на асме. Теперь представте, что у меня много интерфейсов и методов. Тогда мне в каждом методе придется писать 8 строчек одного и того же асмовского кода. Это не приятно. Хотелось бы все это записать одной строчкой. Почему в паскале нет макро подстановок! А так хотелось бы функцией тогда - но это кажись без кода на асме все равно не получиться. Переменная self храниться по адресу ebp+8, и легально кажись до этого места никак не добраться. Или я ошибаюсь? -------------------- Волны гасят ветер... |
||||||||||||||||||||||
|
|||||||||||||||||||||||
Chingachguk |
|
||||||||||
Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1232 Регистрация: 25.3.2002 Где: Москва Репутация: нет Всего: 18 |
Ну как же, как же, Семен Семеныч ! Не все же только в си есть ! ![]() Вот пример: Текст на старом добром Пасе (в Дельфи все также, я уверен):
Т.е. "процедура" AsmCode - это фактически МАКРОС. Вот дамп экзешника:
Таким образом, видно как Паскаль выполнил ПОДСТАНОВКУ текста процедуры, а вовсе не ее вызов (call). В твоем случае можно даже упростить твой код:
На следующий:
Иначе говоря, на байтики: Procedure MyTrashCode;inline($8B/$45/$08/$8B/$40/$04/$89/$45/$08); И вставлять его всюду, где нужно. -------------------- I don't like the drugs (but the drugs like me). M.Manson. |
||||||||||
|
|||||||||||
Fantasist |
|
|||
![]() Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: нет Всего: 41 |
А вот это разговор.
![]() Я, кстати, тоже думал про inline процедуры - где-то я у них в коде видел использования inline. Это конечно, не макрос, но для моего случая должно было подойти. Так вот, полез я в хелп по слову inline - и там было написанно что оно оставленно только для совместимости и компилятор его игнорирует. Вот облом, подумал я и пошел в форум. -------------------- Волны гасят ветер... |
|||
|
||||
Vit |
|
|||
![]() Vitaly Nevzorov ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 10964 Регистрация: 25.3.2002 Где: Chicago Репутация: нет Всего: 207 |
Оператор Inline присутствует с первых версий TurboPascal и во всех версиях Дельфи для выполнения непосредственно машинных комманд.
-------------------- With the best wishes, Vit I have done so much with so little for so long that I am now qualified to do anything with nothing Самый большой Delphi FAQ на русском языке здесь: www.drkb.ru |
|||
|
||||
Chingachguk |
|
||||
Эксперт ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1232 Регистрация: 25.3.2002 Где: Москва Репутация: нет Всего: 18 |
На самом деле допустим и такой вариант: вызывать ПРОЦЕДУРУ. Те на Паскале это будет выглядеть примерно так:
Если ее вызывть так: Const ; Значения параметров Param2: Longint = 66; ; та же таблица методов ? Param1: Longint = 1; Pointer1: Pointer = @Param2; Begin TestProgram(Param1); То должно вывести число 66 вместо ожидаемого 1. Набросал тут все это на асме:
-------------------- I don't like the drugs (but the drugs like me). M.Manson. |
||||
|
|||||
Fantasist |
|
||||
![]() Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: нет Всего: 41 |
Забавно, я же говорю набирал inline и нажимаю F1. Выскакивает топик "asm statement" последние предложение перед Remarks:
![]() 2Chingachguk: Спасибо, приму к сведению. ![]() -------------------- Волны гасят ветер... |
||||
|
|||||
Fantasist |
|
|||
![]() Лентяй ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 1517 Регистрация: 24.3.2002 Репутация: нет Всего: 41 |
Нет. Не хочет D6 inline жрать. Говорит что ожидает декларашен, а находит inline. Второй способ с вызывом процедуры тоже не работает, почему точно пока не скажу - но что-то там не так записывается. Пока только один способ с непосредственным написанием асмовских строчек в коде работает. Но все оказалось проще - можно просто вынести эти строчки в процедуру. Хоть и происходит call, но значение регистров не меняется, и в стек ничего нового не добавляется, а значит меняется то что нужно.
-------------------- Волны гасят ветер... |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Asm: Общие вопросы" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, MAKCim. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Asm: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |