45825 (Перехват методов COM интерфейсов), страница 3

2016-07-31СтудИзба

Описание файла

Документ из архива "Перехват методов COM интерфейсов", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "45825"

Текст 3 страницы из документа "45825"

Рисунок 3. Стек вызова

__declspec(naked) void ItfThunk::thunk()

{

__asm

{

push [esp] // кладем в стек n (параметр метода preprocess)

push [esp+0Ch] // кладем в стек this для вызова preprocess

call preprocess // вызываем ItfThunk::preprocess(n)

call store // вызываем ItfThunk::store

mov eax, [esp+8] // заменяем this в стеке на исходный

mov eax, [eax+4] // из переменной ItfThunk::m_p

mov [esp+8], eax

lea eax, post_thunk // заменяем адрес возврата на post_thunk

mov [esp+4], eax

mov eax,[esp+8] // получаем vptr из исходного указателя

mov eax, [eax]

pop ecx // убираем из стека лишний параметр n

mov eax, [eax+4*ecx] // полчаем адрес метода из vtbl

jmp eax // переходим в исходный метод

post_thunk:

sub esp, 10h // выделяем в стеке место для CallInfo

push esp

push eax // результат вызова исходного метода в eax

call restore // восстанавливаем инфрмацию из TLS

add esp,8

call postprocess // постобработка

ret

}

}

Использовать перехватчик очень просто – клиент передает указатель на настоящий интерфейс конструктору ItfThunk и затем использует ItfThunk в качестве указателя:

CComPtr spFoo;

HRESULT hr = spFoo.CoCreateInstance(__uuidof(Foo));

thunks::ItfThunk t(spFoo.p);

spFoo.p = reinterpret_cast(&t);

spFoo->F();

Теперь мы можем выполнять постобработку вызова, но есть еще одна задача, которую этот перехватчик не решает – предположим, что в некоторых случаях в результате предобработки мы принимаем решение, что вызов исходного метода должен быть заблокирован. Типичный пример – ролевая безопасность. Вызов метода не проходит проверку ролевой безопасности и должен быть отклонен. Но мы не можем сделать этого, так как точное количество параметров метода неизвестно, и наш перехватчик делегирует очистку стека после вызова самому методу.

В общем случае для COM-интерфейсов мы не можем узнать сигнатуру их методов, но для интерфейсов, использующих typelib-маршалинг или итерфейсов, proxy/stub которых сгенерирован с ключом MIDL /oicf, эта информация доступна.

ПРИМЕЧАНИЕ

Ключ /oicf компилятора midl позволяет генерировать интерпретируемый код для proxy/stub и, как результат, информация о сигнатурах метода доступна программно. Подробнее об этом можно прочитать в статье “Секреты маршалинга”.

Получив информацию о количестве параметров метода, мы смогли решить несколько задач:

Заблокировать вызов метода.

Выполнять отложенный/асинхронный вызов.

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

Необходимости самостоятельно разрабатывать перехватчик, опирающийся на информацию из библиотеки типов, нет – начиная с W2K документирован API, позволяющий использовать стандартные перехватчики из инфраструктуры COM/COM+ в своих целях.

CoGetInterceptor, CoGetInterceptorFromTypeInfo

В предыдущем разделе статьи мы рассмотрели несколько технологий перехвата вызовов методов интерфейсов (и могли почувствовать сложность создания универсального перехватчика). Но ни одна из этих технологий не позволила решить задачу перехвата полностью. В частности, не решена задача асинхронных/отложенных вызовов.

К нашей радости, теперь документированы API-функции, позволяющие использовать в приложениях перехватчики из инфраструктуры COM/COM+.

ПРИМЕЧАНИЕ

Это те самые перехватчики, с помощью которых COM+ обеспечивает свои сервисы прозрачно для компонента и клиента – ролевую безопасность, синхронизацию и т.д.

Получить перехватчик для произвольного интерфейса можно с помощью функции CoGetInterceptor:

HRESULT CoGetInterceptor(

REFIID iidIntercepted, // IID перехватываемого интерфейса

IUnknown * punkOuter, // IUnknown для агрегации

REFIID iid, // IID интерфейса, запрашиваемого у перехватчика

void ** ppv // указатель на интерфейс перехватчика

);

Перехватчики COM+ используют информацию из библиотеки типов, чтобы определить сигнатуру метода и количество/типы параметров, а также выполнить маршалинг. Поэтому, если быть более точным, в качестве первого параметра (iidInterceptor) годятся не произвольные интерфейсы, а только те из них, которые совместимы с oleautomation и описаны в библиотеке типов.

Основной интерфейс перехватчика – ICallInterceptor, его мы и будем запрашивать в вызове CoGetInterceptor:

#include

CComModule _Module;

int _tmain(int argc, _TCHAR* argv[])

{

CoInitialize( 0);

_Module.Init(0, 0 );

{

CComPtr spFoo;

HRESULT hr = spFoo.CoCreateInstance(__uuidof(Foo));

CComPtr spInt;

hr = CoGetInterceptor(__uuidof(IFoo), 0, __uuidof(ICallInterceptor),

reinterpret_cast(&spInt));

}

_Module.Term();

CoUninitialize();

return 0;

}

Результатом выполнения приведенного выше приложения будет … Access Violation в недрах ntdll.dll. Этот неприятный сюрприз вызван тем, что перехватчики используют распределитель памяти RPC, который по умолчанию не проинициализирован. Исправить эту проблему можно либо с помощью вызова CoInitializeSecurity, либо вызовом любых функций маршалинга, которые проинициализируют RPC heap (есть еще вариант с прямым вызовом функции инициализации из rpcrt4.dll, но она не документирована).

ПРИМЕЧАНИЕ

Проблема с инициализацией RPC-кучи была исправлена в Windows 2003 Server.

Исправленный код клиента:

HRESULT hr = CoInitializeSecurity(NULL, -1, NULL, NULL,

RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,

NULL, EOAC_NONE, NULL);

CComPtr spFoo;

hr = spFoo.CoCreateInstance(__uuidof(Foo));

CComPtr spInt;

hr = CoGetInterceptor(__uuidof(IFoo), 0, __uuidof(ICallInterceptor),

reinterpret_cast(&spInt));

С помощью указателя на интерфейс ICallInterceptor мы можем зарегистрировать свои собственные обработчики вызовов:

Методы ICallInterceptor

Описание

HRESULT RegisterSink(ICallFrameEvents * psink);

Зарегистрировать обработчик

HRESULT GetRegisteredSink(ICallFrameEvents ** ppsink);

Получить зарегистрированный обработчик

ПРИМЕЧАНИЕ

Другие методы ICallInterceptor описаны в MSDN

Обработчик должен реализовать интерфейс ICallFrameEvents.

Методы ICallFraneEvent

Описание

HRESULT OnCall(ICallFrame * pFrame);

Вызов метода перехватываемого интерфейса

После регистрации обработчика мы будем получать событие OnCall каждый раз, когда клиент будет осуществлять вызов через перехватываемый интерфейс.

Дополним код клиента (см. выше) – теперь мы будем регистрировать свой обработчик вызовов:

class CallHandler : public CComObjectRoot,

public ICallFrameEvents

{

public:

BEGIN_COM_MAP(CallHandler)

COM_INTERFACE_ENTRY(ICallFrameEvents)

END_COM_MAP()

STDMETHOD(OnCall)(ICallFrame* pFrame)

{

return S_OK;

}

};

...

CComPtr spInt;

hr = CoGetInterceptor(__uuidof(IFoo), 0, __uuidof(ICallInterceptor),

reinterpret_cast(&spInt));

CComObject* pHandler = 0;

CComObject::CreateInstance(&pHandler);

hr = spInt->RegisterSink(pHandler);

CComPtr spFooInt;

hr = spInt.QueryInterface(&spFooInt);

hr = spFooInt->F();

ПРИМЕЧАНИЕ

Если обработчик вернет HRESULT с ошибкой, ошибку получит и клиент, но ее код, к сожалению, не передается пользователю. Если клиент не зарегистрирует ни одного обработчика, то вызов метода также завершится с ошибкой.

Мы запрашиваем указатель на перехватываемый интерфейс у перехватчика, а затем выполняем вызов метода IFoo::F, в результате мы попадем в код обработчика ICallFrameEvent::OnCall.

Задача обработчика – решить, что делать дальше с вызовом:

Отклонить его, вернув ошибку.

Сохранить стек параметров вызова, чтобы выполнить его асинхронно.

Выполнить вызов немедленно.

Прямые/синхронные вызовы

Информацию о вызове обработчик получает с помощью указателя на интерфейс ICallFrame, передаваемый ему в качестве параметра pFrame.

Интерфейс ICallFrame позволяет получить информацию о сигнатуре метода, размере стека параметров, значения отдельных параметров и результат вызова метода. Кроме того, с помощью ICallFrame можно изменить значения отдельных (или всех) параметров и дополнить стек параметров в случае, если клиент передал не все необходимые параметры (например, клиент сделал вызов не через указатель на перехватываемый интерфейс, а с помощью ICallInterceptor::CallIndirect, передавая частичный стек параметров).

ПРИМЕЧАНИЕ

Подробнее описание методов интерфейса ICallFrame см. в MSDN

Расширим код нашего обработчика CallHandler так, чтобы он выдавал отладочные сообщения о вызове и его результатах и выполнял немедленный вызов с помощью ICallFrame::Invoke:

template

class CallHandler : public CComObjectRoot,

public ICallFrameEvents

{

public:

BEGIN_COM_MAP(CallHandler)

COM_INTERFACE_ENTRY(ICallFrameEvents)

END_COM_MAP()

void init(CComPtr spItf)

{

m_spItf = spItf;

}

STDMETHOD(OnCall)(ICallFrame* pFrame)

{

LPWSTR itf, method;

HRESULT hr = pFrame->GetNames(&itf, &method);

hr = pFrame->Invoke(m_spItf.p);

ATLTRACE("call %s::%s %8x\n", itf, method, hr);

CoTaskMemFree(itf);

CoTaskMemFree(method);

return hr;

}

private:

CComPtr m_spItf;

};

Вызывая ICallFrame::Invoke, мы не передаем никаких параметров – значения для параметров перехватываемого метода были переданы клиентом, когда он выполнял вызов через перехватчик.

ПРИМЕЧАНИЕ

Метод ICallFrame::Invoke имеет переменное количество параметров (что редко встречается у COM-интерфейсов). Если стек параметров вызова заполнен только частично, в Invoke могут передаваться дополнительные параметры вызова (которые будут добавлены в стек перед вызовом).

Косвенные и асинхронные/отложенные вызовы

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

С помощью перехватчиков COM+ можно выполнять косвенные и асинхронные вызовы. Вместо прямого вызова ICallFrame::Invoke мы можем:

сохранить содержимое параметров, находящихся в стеке, в специальный буфер (фактически выполнить маршалинг параметров);

передать их с помощью любого доступного транспорта (RPC, MSMQ, SOAP, файлы и т.п.) компоненту;

выполнить вызов;

Свежие статьи
Популярно сейчас
Зачем заказывать выполнение своего задания, если оно уже было выполнено много много раз? Его можно просто купить или даже скачать бесплатно на СтудИзбе. Найдите нужный учебный материал у нас!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5173
Авторов
на СтудИзбе
436
Средний доход
с одного платного файла
Обучение Подробнее