Протокол для IE

Для неординарных вопросов. Если вы опытный программист, попавший в трудную ситуацию, — вам сюда.

Модератор: gaidar

Правила форума
Этот раздел не предназначен для того, чтобы вы адресовали свою проблему профессионалам.
Этот раздел предназначен для профессионалов, которые столкнулись с проблемой и не могут решить ее самостоятельно.
Если вы считаете себя профессионалом, а свою проблему сложной — вам сюда.
Если модератор посчитает, что вы ошиблись, то на первый раз он перенесет ваше сообщение в основной раздел без последствий для автора. Во второй раз тема будет закрыта, а автору будет выписано нарушение. В третий раз автор будет забанен.
Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Протокол для IE

Сообщение Antonariy » 22.04.2008 (Вт) 12:28

Сабж (prototest://) делаю на основе этого примера. Он в принципе работает, но с одним неприятным моментом. Если ввести адрес в адресную строку, то загрузка документа происходит моментально, но если перейти по ссылке типа <a href="prototest://...">, то IE зависает на 1-5 секунд, а потом загружает документ. Если щелкнуть правой кнопкой по такой ссылке, то опять подвисание, потом появляется меню.

Отладка в ide невозможна, IE походу не может получить интерфейс IInternetProtocol, поэтому напихал логов. Логи показали, что в момент подвисания несколько десятков раз происходит Class_Initialize/Terminate.

В описании хендлеров протоколов сказано, что для них требуется реализация двух интерфейсов, IInternetProtocol и IInternetProtocolInfo. Первый обязательно, второй - нет. Собственно в примере его тоже нет и все работает. Появилось подозрение, что в момент подвисания IE как раз таки пытается получить IInternetProtocolInfo. Добавил Implements, код в членов писать не стал, IE стал выдавать ошибку "Недействительный адрес". Дальнейшие копания в этом направлении дали это:
Код: Выделить всё
STDMETHODIMP CDataPluggableProtocol::CombineUrl(LPCWSTR pwzBaseUrl, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags,
                                    LPWSTR pwzResult, DWORD cchResult, DWORD *pcchResult, DWORD dwReserved)
{return INET_E_DEFAULT_ACTION;}

STDMETHODIMP CDataPluggableProtocol::CompareUrl(LPCWSTR pwszUrl1, LPCWSTR pwszUrl2, DWORD dwCompareFlags)
{
   ATLTRACE(_T("CompareUrl\n"));
   if (pwszUrl1 == NULL || pwszUrl2 == NULL)
      return E_POINTER;
   HRESULT hr = S_FALSE;
   CDataURL url1, url2;
   if (url1.Parse(pwszUrl1) && url2.Parse(pwszUrl2) && url1 == url2){
      hr = S_OK;
   }
   return hr;
}

STDMETHODIMP CDataPluggableProtocol::ParseUrl(LPCWSTR pwzUrl, PARSEACTION parseAction, DWORD dwParseFlags,
                                   LPWSTR pwzResult, DWORD cchResult, DWORD *pcchResult, DWORD dwReserved)
{return INET_E_DEFAULT_ACTION;}

STDMETHODIMP CDataPluggableProtocol::QueryInfo( LPCWSTR pwzUrl, QUERYOPTION QueryOption, DWORD dwQueryFlags,
                                    LPVOID pBuffer, DWORD cbBuffer, DWORD *pcbBuf, DWORD dwReserved)
{return INET_E_DEFAULT_ACTION;}
То есть нужно как минимум возвращать значение INET_E_DEFAULT_ACTION. А как возвращать-то? Члены интерфейса описаны как Sub, а не Function.

Вижу два способа решения проблемы: декомпилировать olelib.tlb (если возможно) и поменять объявления, второй способ использовал сам автор в примере. IInternetProtocol.Read тоже описано как Sub, но автору зачем-то понадобилось возвращать значения (хотя и без этого работает). Он заменяет Sub на Function функцией ReplaceVTableEntry, чье говорящее имя описывает способ замены. Она одним из параметров принимает номер функции в vtable.

Из всего этого вытекают такие вопросы:
1) Можно ли (и чем) декомпилировать tlb?
2) Как узнать номер функции в vtable?
3) Может я вообще не туда забрел?
Вложения
prototest.rar
(23.95 Кб) Скачиваний: 255
Лучший способ понять что-то самому — объяснить это другому.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 22.04.2008 (Вт) 12:44

Вижу два способа решения проблемы: декомпилировать olelib.tlb (если возможно) и поменять объявления, второй способ использовал сам автор в примере. IInternetProtocol.Read тоже описано как Sub, но автору зачем-то понадобилось возвращать значения (хотя и без этого работает). Он заменяет Sub на Function функцией ReplaceVTableEntry, чье говорящее имя описывает способ замены. Она одним из параметров принимает номер функции в vtable.

Первый способ мне полностью понятен и очевиден. Его и надо использовать по идее.

Второе способ мне непонятен вообще. Если я правильно понял (а у меня сомнения на этот счёт), то мысль заключается в том, что подменив что-то в VTable можно превратить Sub в Function?
Так вот нельзя.

1) Можно ли (и чем) декомпилировать tlb?

Ну, вообще, LoadTypeLibEx. Другое дело, чем построить из этого дерева объектов IDL-исходники. Возникает вопрос, а надо ли получать эти исходники? Ведь есть интерфейс ICreateTypeLib, который можно запросить у ITypeLib и, сделав нужные изменения, сохранить TLB-шку. Опыт показал, что в VB это по непоятным причинам не работает.

2) Как узнать номер функции в vtable?

Получить структуру FUNCDESC метода, посмотреть на поле oVft. Это оффсет ячейки в vtable.

3) Может я вообще не туда забрел?

Не исключено :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 22.04.2008 (Вт) 13:30

Второе способ мне непонятен вообще.
Ты бы исходник посмотрел что ли.
Если я правильно понял
Походу не правильно. Ничего не превращается, заменяется адрес оригинальной процедуры на адрес подставной. Оригинальная - Sub, подставная - Function.
Получить структуру FUNCDESC метода, посмотреть на поле oVft. Это оффсет ячейки в vtable.
Хм, мне бы не изобретать ничего, а какой-нибудь готовой тулзой дернуть.
Ну, вообще, LoadTypeLibEx. Другое дело, чем построить из этого дерева объектов IDL-исходники. Возникает вопрос, а надо ли получать эти исходники? Ведь есть интерфейс ICreateTypeLib, который можно запросить у ITypeLib и, сделав нужные изменения, сохранить TLB-шку. Опыт показал, что в VB это по непоятным причинам не работает.
Может проще написать новый *.idl? В принципе однажды приходилось, но без помощи не справлюсь. Тут же нужно не просто интерфейс, а конкретный.
Лучший способ понять что-то самому — объяснить это другому.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 23.04.2008 (Ср) 9:58

Хм, мне бы не изобретать ничего, а какой-нибудь готовой тулзой дернуть.
Это умеет темычевский tlbbrowse. Хорошая мысля приходит опосля. :)
Лучший способ понять что-то самому — объяснить это другому.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.04.2008 (Вт) 12:17

Итак, удалось подменить нужные процедуры в VTABLE (фокус с tlb не прошел - когда IE обнаружил в интерфейсе не sub, а function - склеил ласты, хотя и подергал пару раз функцию), нужные значения стали возвращаться. Страница отобразиласть, но подвисания никуда не делись и вдобавок перестала работать навигация по ссылкам. Зато по подвисаниям всплыли подробности - в это время происходит вызов ParseUrl несколько тысяч раз. Пяток вызовов с разными параметрами в начале, пяток в конце, все остальное - чередующиеся вызовы с двумя наборами параметров. Выглядит это примерно так:
Код: Выделить всё
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=7471205
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:33: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096

.......поскипано.......

13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630984 cchResult=4096 pcchResult=4096
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630444 cchResult=4096 pcchResult=24630548
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24631508 cchResult=4096 pcchResult=1139077569
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630968 cchResult=4096 pcchResult=24631072
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24631508 cchResult=4096 pcchResult=4096
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630968 cchResult=4096 pcchResult=24631072
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24631508 cchResult=4096 pcchResult=4096
13:07:41: ParseUrl 65660652 PARSEACTION=9 dwParseFlags=0 pwzResult=24630968 cchResult=4096 pcchResult=24631072
Еще я нашел аналогичную реализацию протокола на с++, IInternetProtocolInfo реализован точно так же, но все работает как часы (правда только в ide), всех этих многочисленных вызовов просто не происходит. Оба проекта прилагаю.
Вложения
prototest.rar
(24.66 Кб) Скачиваний: 239
CustomProtocol.zip
(142.61 Кб) Скачиваний: 239
Лучший способ понять что-то самому — объяснить это другому.

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 29.04.2008 (Вт) 12:39

Код: Выделить всё
. А как возвращать-то?
Вообщето возвращать можно с помощью
Код: Выделить всё
Err.Raise &h800C0011
Но предложенные способы мне нравятся :D
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.04.2008 (Вт) 12:53

Вообщето возвращать можно с помощью
Екарный бабай... И хоть бы где об этом было написано. Неужели автор примера не знал об этом? Хотя везде понатыкано Err.Raise E_NOTIMPL. Зачем тогда он подменял Read?
Но предложенные способы мне нравятся
Сработал из них только второй, но и его в баню. Err.Raise прекрасно работает.

А есть соображения по поводу неадекватного поведения протокола? Я нашел пару тем, где ты тоже с ним колупался.

ADD:
Ради смеха заменил Err.Raise INET_E_DEFAULT_ACTION на Err.Raise E_NOTIMPL. Навигация вернулась, подвисания остались. Изображение
Лучший способ понять что-то самому — объяснить это другому.

ANDLL
Великий гастроном
Великий гастроном
Аватара пользователя
 
Сообщения: 3450
Зарегистрирован: 29.06.2003 (Вс) 18:55

Сообщение ANDLL » 29.04.2008 (Вт) 14:58

Я бы посмотрел содержимое регистра EAX на выходе ParseUrl в работающем сишном примере, и в плохо работающем VB-шном, они точно совпадают?
Ну и обнови проект, выкинь все эти махинации с vtable что бы было читабельно
Гастрономия - наука о пище, о ее приготовлении, употреблении, переварении и испражнении.
Блог

Twister
Теоретик
Теоретик
Аватара пользователя
 
Сообщения: 2251
Зарегистрирован: 28.06.2005 (Вт) 12:32
Откуда: Алматы

Сообщение Twister » 29.04.2008 (Вт) 15:04

Как я уже говорил в аське после пробега клазами по VB-коду, скорее всего там, тупо, зацикливание - функция ParseURL зовет сама себя...
А я все практикую лечение травами...

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 29.04.2008 (Вт) 16:51

Уже все вычистил:
Ради смеха заменил Err.Raise INET_E_DEFAULT_ACTION на Err.Raise E_NOTIMPL. Навигация вернулась, подвисания остались.
Во-первых, если "сам себя", то это не зацикливание, а рекурсия. Во-вторых ни то и не другое, и это было очевидно из логов - параметры разные. Если бы было зацикливание - они были бы одинаковые. Да и осел бы наверняка сваливался из-за переполнения стека, если таки рекурсия. Кроме того поддельные QueryInfo и Read вели себя пристойно. Ну и последний аргумент был озвучен в первом посте, я уже о нем успел забыть:
Логи показали, что в момент подвисания несколько десятков раз происходит Class_Initialize/Terminate.
Согласитесь, зацикливание между сеансами невозможно.

Мнится мне, что IE неадекватно воспринимает VB-шную природу дллки.
Вложения
prototest.rar
(23.29 Кб) Скачиваний: 243
Лучший способ понять что-то самому — объяснить это другому.

Twister
Теоретик
Теоретик
Аватара пользователя
 
Сообщения: 2251
Зарегистрирован: 28.06.2005 (Вт) 12:32
Откуда: Алматы

Сообщение Twister » 30.04.2008 (Ср) 10:56

то это не зацикливание, а рекурсия
Я бы назвал это рекурсивным зацикливанием. :wink:
А я все практикую лечение травами...

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 30.04.2008 (Ср) 15:39

Может того... Сишным отладчиком прогнать, а? Скомпилить с отладочными символами и посмотреть, что же там происходит.
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 01.05.2008 (Чт) 15:53

Я бы рад, но для меня это terra incognita.
Лучший способ понять что-то самому — объяснить это другому.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Re: Re:

Сообщение tyomitch » 27.09.2008 (Сб) 2:12

Antonariy писал(а):
Вообщето возвращать можно с помощью
Екарный бабай... И хоть бы где об этом было написано. Неужели автор примера не знал об этом? Хотя везде понатыкано Err.Raise E_NOTIMPL. Зачем тогда он подменял Read?

Потому что рейзить можно только отрицательные (неуспешные) коды.
К положительным втихушку добавляется &H800A0000

Но On Error Resume Next в начале метода убивает всю эту затею на корню.

Далее, начальное предположение топика -- что IE, получив отказ на QueryInterface, не сдаётся и запрашивает его снова и снова, пока не надоест, -- представляется сомнительным.

Хоть загадка и потеряла актуальность, попытаюсь подебагать в выходные :-)
Изображение

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Re: Re:

Сообщение Antonariy » 29.09.2008 (Пн) 10:18

tyomitch писал(а):Далее, начальное предположение топика -- что IE, получив отказ на QueryInterface, не сдаётся и запрашивает его снова и снова, пока не надоест, -- представляется сомнительным.
Дело не в отказе, даже получив интерфейс, он продолжает дергать его почем зря. Точнее процедуру ParseUrl (cм. лог).
tyomitch писал(а):Хоть загадка и потеряла актуальность, попытаюсь подебагать в выходные :-)
Актуальность — да, но интерес — нет :)
Лучший способ понять что-то самому — объяснить это другому.


Вернуться в Раздел для Профессионалов

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

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

    TopList