Как работать с интерфейсами.

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Как работать с интерфейсами.

Сообщение GSerg » 19.05.2004 (Ср) 9:08

Что-то я разошёлся в последнее время :)

Предложение Заценить и Высказаться остаётся в полной силе.


Как мы все знаем, VB всё делает за нас при работе с объектами. После
Код: Выделить всё
Dim obj As Object
Set obj = something

можно просто вызывать методы: obj.method1, obj.method2 - VB справляется с такими вызовами, даже если у него нет описания данного объекта. Как у него это получается? Очень просто. У него это не получается :) Всё дело в том, что Object в VB - это всегда такой объект (COM-объект), который реализует интерфейсы IUnknown и IDispatch. Через IUnknown VB спрашивает: "Поддерживаешь IDispatch?". "Yep...". VB переходит на общение по IDispatch. "Есть метод по имени 'method1'?". "Есть!". "Параметры?". "Такие-то!". "Ага, совпадает... Тогда лови!..". Не будем говорить о том, сколько времени уходит на подобные диалоги, ни к чему это :) За лёгкость надо платить :wink:

Но существует куча прекрасных объектов, реализующих IUnknown (его реализуют абсолютно все COM-объекты), но не реализующих IDispatch! Диалог с такими объектами либо завершается на стадии "Поддерживаешь IDispatch?". "Oops...", либо приводит к относительно допустимому сворачиванию коврика. Не имея механизма опроса объекта на предмет его методов, VB делает вывод, что работать с ним ну никак нельзя. Но когда это нас останавливало встроенное органичение? :)

Начнём с основ, чтобы не создавать смутного и невразумительного впечатления :wink:
Вот создали мы описание класса (в VB или ещё где - не суть). Вот создали экземпляр этого класса, и ещё один - да много насоздавали мы экземпляров... У каждого экземпляра свои внутренние данные, каждый обладает методами. Так вот, данные у каждого экземпляра действительно свои, а вот методы у всех экземпляров общие. Какой бы экземпляр не вызвал метод - вызов пойдёт в одну и ту же точку кода. Что, в общем, логично - откомпилированный код процедуры не должен изменяться (хотя можно пошалить и тут), зачем же его клонировать. А раз у всех методы общие, значит, каждый должен иметь представление о том, где эти общие находятся. Это представление называется vTable. Это такая таблица (одномерный массив, если угодно), каждый элемент которого имеет размер 4 байта и является указателем на метод. Все методы, поддерживаемые объектом, идут в этой таблице подряд. Упорядочены они по интерфейсам (сначала все методы одного интерфейса, потом все другого...), а в рамках конкретного интерфейса порядок методов определяется порядком описания этих методов в исходнике. Первым идёт всегда интерфейс IUnknown, а его методы - всегда в порядке: QueryInterface, AddRef, Release. Только так. Всегда и везде. Благодаря этим трём фиксированным указателям, этим 12 байтам, собственно, и живёт технология COM :)

Те, кто смотрел проект VB + ASM, наверняка уже догадались :) "Таблица указателей? Но мы же уже умеем вызывать по указателю!". В точечности так. Осталось подкорректировать совсем немного.
Как получить указатель на vTable? Очень просто. Интерфейс - это и есть указатель на vTable.
Вот он, указатель pInterface, вернутый нам апишкой. Что будем с ним делать?
Код: Выделить всё
GetMem4 pInterface, VarPtr(vTable)     'дереференс: находим положение vTable

И вот мы уже в начале таблицы. Дальше? А дальше идём в мануал. И в этом мануале читаем описание этого интерфейса. Если нет мануала, лезем в .h. В общем, узнаём, каким же идёт интересный нам метод. Узнали? Метод номер 6? Методы нумеруются с нуля! Так что метод номер 5. Чтобы больше не лезть никогда в этот мануал, определяем константу типа
Код: Выделить всё
Const MYINTERAFACE_MYMETHOD As Long = 5

Метод номер 5. Размер указателя 4 байта. А мы в начале таблицы. А они все подряд. Так что, само собой,
Код: Выделить всё
GetMem4 vTable + MYINTERAFACE_MYMETHOD * 4, VarPtr(MyMethodPointer)

Ну что, дружно идём вызывать MyMethodPointer через cFuncCall.CallFunction? Уже пошли? Молодцы! А почему не сохранились? Но уже поздно, в любом случае :) Потому что любой метод интерфейса на клеточном, так сказать, уровне имеет на один параметр больше, чем описано в мануале. Параметр этот - указатель this (сишники в восторге), он же Self (дельфисты счастливы), он же Me (ну как же про нас забыть). И параметр этот идёт всегда первым. Собственно, благодаря этому параметру общий для всех экземпляров код определяет, какой именно экземпляр его вызвал.

Вот теперь у нас достаточно информации для того, чтобы немного мутировать CallFunction в CallInterface. Все дружно идём в VB + ASM и смотрим обновлённый класс, данную фичу содержащий :wink:

Сколько раз рушилась IDE, пока я разобрался с уровнями дереференса - это отдельный разговор...



В качестве теста я тут попробовал от RichTextBox получить IRichEditOle - и вы знаете, получил :) И метод вызвался, собака :)

А возможность вручную управлять временем жизни объектов? AddRef - номер 1, Release - номер 2! И больше никаких неуничтоженных, знаете ли, объектов... Захотим уничтожить - будем вызывать Release, пока не сдохнет... Если, конечно, мы точно знаем, что никто в это время не попытается вызвать объект, а то коврик, знаете ли...
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

FaKk2
El rebelde gurú
El rebelde gurú
Аватара пользователя
 
Сообщения: 2031
Зарегистрирован: 09.03.2003 (Вс) 22:10
Откуда: Los Angeles

Сообщение FaKk2 » 19.05.2004 (Ср) 9:14

Тебе больше звание "Экстремал" подходит :P

А вообще, написано толково :wink:
Для получения ответа надо продемонстрировать качества, позволяющие стать компетентным — внимательность, вдумчивость, наблюдательность, желание активно участвовать в выработке решения.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 19.05.2004 (Ср) 9:52

GSerg, если реализуешь работу с IShellLink, то я признаюсь тебе в любви ;)
Lasciate ogni speranza, voi ch'entrate.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 19.05.2004 (Ср) 9:55

В любви ты мне уже признавался. Когда я про [хорошие имена] фишку двинул :) А насчёт IShellLink - домой сейчас вот пойду и тогда... :wink:
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 19.05.2004 (Ср) 10:00

Надеюсь нас не поймут неправильно :)

В MSDN IShellLink документирован очень даже хорошо, но вот как его использовать на VB это для меня была загадка :)
Я там ковырялся, ковырялся, добрался до IPersistFile и понял, что надо изучать C :)
Lasciate ogni speranza, voi ch'entrate.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 19.05.2004 (Ср) 10:07

А чем http://bbs.vbstreets.ru/viewtopic.php?p=33968#33968 плохо? :)
К тому же, интерфейс - это просто описание. Нет объекта "интерфейс". Я вот получил от rtb IRichEditOle только потому, что он его имплементит. Какой объект имплементит IShellLink? Дай мне этот объект, и я вытрясу из него интерфейс :)
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 19.05.2004 (Ср) 16:42

В общем, это :)
Ну, ты понял :)
Вложения
Shell Link.zip
Юзание IShellLink
(4.77 Кб) Скачиваний: 87
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 19.05.2004 (Ср) 17:03

Респект!!!
Правда под XP он не работает, вернее ярлык создает, но пустой :)

Ну да это ерунда, это я уже сам :)
Lasciate ogni speranza, voi ch'entrate.

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 19.05.2004 (Ср) 20:17

GSerg - давай статью пиши, на сайте повесим!
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

hCORe
VB - Экстремал
VB - Экстремал
Аватара пользователя
 
Сообщения: 2332
Зарегистрирован: 22.02.2003 (Сб) 15:21
Откуда: parent directory

Сообщение hCORe » 19.05.2004 (Ср) 20:29

GSerg, ну ты просто мастер! Спасибо за Shell Link, уже к программе прикрутил 8)
Моду создают модоки, а распространяют модозвоны.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 20.05.2004 (Чт) 2:23

alibek
А где признание? :wink: :P
В любом случае, твою проблему диагностирую следующим образом...
IPersistFile изначально юникодовский. У меня в коде тоже. А IShellLink есть в двух версиях. Сильно подозреваю, что тебе нужно создавать W, а в коде везде убрать StrConv(, vbFromUnicode). Надо бы это отразить через #If, как думаешь? :)

gaidar
Мои статьи связаны :) Придётся приводить в порядок их все :)
Кроме того, твоя фраза нескольку двусмысленна... "Пиши статью, а то на сайте повесим!" :wink:
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 20.05.2004 (Чт) 9:12

Повесим-повесим :)
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 20.05.2004 (Чт) 10:02

GSerg, официальное признание :)


Утром не завтракаю, т.к. думаю о тебе.
Днем не обедаю - думаю о тебе.
Вечером не обедаю - думаю о тебе.
Ночью не сплю - КУШАТЬ ХОЧУ!

Lasciate ogni speranza, voi ch'entrate.

sanches
El compañero
El compañero
 
Сообщения: 823
Зарегистрирован: 09.01.2003 (Чт) 3:58
Откуда: Р_О_С_С_И_Я ! (Питер)

Сообщение sanches » 20.05.2004 (Чт) 12:39

GSerg
Интересно... даже очень. А вот не мог бы ты помочь с такой фишкой. Я вот сколько не парился, не знаю, как проверить, поддерживает ли объект такой-то интерфейс. А проверять это с отловом ошибки(*) слишком долго (если надо объект проверить интерфейсов на 10 или еще больше), а таких объектов до сотни и даже больше.

-------
(*) Ну то есть так:
Dim objMyInterf As MyInterf

Set objMyInter = objMyObject
'ну короче вот здесь отлавливаем ошибку, которая выскакивает, если объект не поддерживает интерфейс
--------

А хотелось бы метод по-быстрее...
Изображение

sanches
El compañero
El compañero
 
Сообщения: 823
Зарегистрирован: 09.01.2003 (Чт) 3:58
Откуда: Р_О_С_С_И_Я ! (Питер)

Сообщение sanches » 20.05.2004 (Чт) 12:41

FAKK2 писал(а):Тебе больше звание "Экстремал" подходит :P

А вообще, написано толково :wink:


Как раз экстремал в данном случае не подходит. Экстремал - тот, кто пульзуется методами эктремального программирования.
Изображение

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 20.05.2004 (Чт) 13:21

Вызывай QueryInterface у базового интерфейса. Только придётся узнать IID запрашиваемого.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

sanches
El compañero
El compañero
 
Сообщения: 823
Зарегистрирован: 09.01.2003 (Чт) 3:58
Откуда: Р_О_С_С_И_Я ! (Питер)

Сообщение sanches » 20.05.2004 (Чт) 17:27

GSerg писал(а):Вызывай QueryInterface у базового интерфейса. Только придётся узнать IID запрашиваемого.

Ну мне бы пример. IID - Это тоже, что и GUID? или нет?
Изображение

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 21.05.2004 (Пт) 10:02

Да в общем-то то же самое.
Смотри прилагаемый Shell Link, там идёт как раз запрос на IPersistFile в Class_Initialize.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 23.05.2004 (Вс) 13:43

gaidar писал(а):Повесим-повесим :)

Видим-видим :)
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 25.05.2004 (Вт) 6:40

sanches писал(а):... проверять это с отловом ошибки(*) слишком долго (если надо объект проверить интерфейсов на 10 или еще больше), а таких объектов до сотни и даже больше.
...
А хотелось бы метод по-быстрее...

Для таких целей есть интерфейс IMultiQI, не объектный, правда, но ведь теперь этим не остановить. :)
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Re: Как работать с интерфейсами.

Сообщение Vi » 25.05.2004 (Вт) 7:00

GSerg писал(а):...А возможность вручную управлять временем жизни объектов? AddRef - номер 1, Release - номер 2! И больше никаких неуничтоженных, знаете ли, объектов... Захотим уничтожить - будем вызывать Release, пока не сдохнет... Если, конечно, мы точно знаем, что никто в это время не попытается вызвать объект, а то коврик, знаете ли...

А здесь есть заблуждение. Да, возможность управления временем есть: это действительно AddRef и Release. Но она носит характер не абсолютный, а только относительный.

Во-первых, счетчики объекта по СОМ стандарту работают поинтерфейсно. Т.е. если запрос прошел на интерфейсы IA и IB объекта, то и Release-ить нужно отдельно по каждому интерфейсу, а не по одному несколько раз.

Во-вторых, даже если объект все счетчики хранит в одном месте, то прокси работают именно так. Т.е. если между тобой и объектом есть прокси, то она (вкупе со своим манагером) не даст сделать большего количества Release, чем имеет.

В-третьих, VB и прочие среды специально убирают работу со счетчиками, скрывая этот тонкий механизм. Вы просто не представляете сколько геморроя поимеете, если начнете баловаться с этим.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 25.05.2004 (Вт) 8:23

Release возвращает количество ссылок, оставшихся у интерфейса. Я могу вызывать его, пока не станет 0. Это нарушение COM, но, тем не менее, это возможно. А если интерфейс создан мною же, то какие тут проблемы?
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 26.05.2004 (Ср) 6:52

А если интерфейс создан мною же, то какие тут проблемы?

Да ради бога. Если человек идет сознательно на нарушение правил, то тут нет особых проблем: Undefined Behaviour (или Неопределенное Поведение) ему обеспечено. И если это поведение - цель его программы, то он ее добился.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 29

    TopList  
cron