45825 (Перехват методов COM интерфейсов), страница 5
Описание файла
Документ из архива "Перехват методов COM интерфейсов", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.
Онлайн просмотр документа "45825"
Текст 5 страницы из документа "45825"
Рисунок 5. Буфер с in-параметрами вызова
Работа с параметрами вызова в обработчике
В примере выше мы делегировали работу по преобразованию стека вызова в бинарный буфер перехватчику. Бинарные буферы с параметрами вызова отлично подходят для многих видов транспорта – RPC, MSMQ. Однако если бы мы захотели использовать SOAP для передачи вызова компоненту, такое бинарное представление было бы неприемлемо, так как SOAP-сообщение представляет собой XML-текст, содержащий значения каждого из in-параметров по отдельности. Подробнее о протоколе SOAP и формате SOAP-сообщений можно прочитать в статье: “Использование протокола SOAP в распределенных приложениях Microsoft SOAP Toolkit 3.0”.
В этой статье был рассмотрен способ создания Proxy, работающей через ранее связывание (SOAP Toolkit использует IDispatch и позднее связывание для вызовов). Proxy поддерживала интерфейс:
interface ISoapProxy : IDispatch { [id(1), helpstring("method Initialize")] HRESULT Initialize([in]BSTR wsdl, [in]BSTR wsml, [in]BSTR service, [in]BSTR port); [propget, id(2), helpstring("property ConnectorProperty")] HRESULT ConnectorProperty([in]BSTR prop, [out, retval] VARIANT *pVal); [propput, id(2), helpstring("property ConnectorProperty")] HRESULT ConnectorProperty([in]BSTR prop, [in] VARIANT newVal); [propget, id(3), helpstring("property ProxyProperty")] HRESULT ProxyProperty([in]BSTR prop, [out, retval] VARIANT *pVal); [propput, id(3), helpstring("property ProxyProperty")] HRESULT ProxyProperty([in]BSTR prop, [in] VARIANT newVal); [id(4), helpstring("method GetOperation")] HRESULT GetOperation([in]BSTR name, [out,retval]IWSDLOperation** ppOp); [id(5), helpstring("method Execute")] HRESULT Execute([in]IWSDLOperation* pOp); }; |
Для каждого из интерфейсов/методов был написан код, перенаправляющий вызовы Proxy, которая, в свою очередь, использовала низкоуровневые компоненты из SOAP Toolkit для передачи вызова SOAP-серверу.
Например, реализация одного из методов выглядела так:
STDMETHOD(KillProcess)(LONG processID) { try { // получаем описание операции KillProcess IWSDLOperationPtr spOp = m_spSoapProxy->GetOperation(L"KillProcess"); IEnumSoapMappersPtr spEnum; // заполняем значения входных параметров spOp->GetOperationParts(&spEnum); while(true) { long l = 0; ISoapMapperPtr spMap; spEnum->Next(1, &spMap, &l); if(l == 1) { if(spMap->PartName == _bstr_t(L"processID")) spMap->ComValue = processID; } else break; } // передаем вызов серверу m_spSoapProxy->Execute(spOp); } catch(_com_error & e) { return e.Error(); } return S_OK; } |
ПРИМЕЧАНИЕ В этом коде m_spSoapProxy – экземпляр Proxy (описание интерфейса см. выше) |
Реализации для разных методов различались лишь названиями методов (или операций, в терминах SOAP) и названиями параметров. Вместо того, чтобы писать однотипный код, можно создать перехватчик для нужного интерфейса CoGetInterceptor, а в методе ICallFrameEvents::OnCall, напрямую манипулируя с параметрами вызова, создать SOAP-сообщение.
Получить значение параметра позволяет метод ICallFrame::GetParam:
HRESULT GetParam( ULONG iparam, VARIANT * pvar ); |
Нам нужен номер параметра, который можно получить из описания SOAP-операции ISoapMapper::get_CallIndex:
[propget] HRESULT callIndex([out, retval] long* par_lCallIndex); |
После вызова метода нам потребуется метод для задания нового значения out-параметра в стеке ICallInfo::SetParam и метод для задания результата выполнения метода ICallInfo::SetReturnValue:
HRESULT SetParam( ULONG iparam, VARIANT * pvar ); HRESULT SetReturnValue( HRESULT hr ); |
И, наконец, нужно отличать in- и out-параметры. Сделать это можно вызовом ISoapMapper::get_IsInput.
Полный код реализации обработчика вызова приведен ниже:
STDMETHOD(OnCall)(ICallFrame* pFrame) { LPWSTR lpszItf ,lpszMethod; HRESULT hr = pFrame->GetNames(&lpszItf, &lpszMethod); CoTaskMemFree(lpszItf); // получаем описание SOAP-операции из WSML CComPtr spOp; hr = m_spProxy->GetOperation(CComBSTR(lpszMethod) , &spOp); CoTaskMemFree(lpszMethod); if(SUCCEEDED(hr)) { CComPtr spEnum; hr = spOp->GetOperationParts(&spEnum); if(SUCCEEDED(hr)) { // перебираем все параметры while(hr == S_OK) { CComPtr spMapper; long lFetched = 0; hr = spEnum->Next(1, &spMapper, &lFetched); if(!lFetched || hr != S_OK) break; long idx = 0; smIsInputEnum paramType; hr = spMapper->get_IsInput(¶mType); // для in-параметров берем значения из стека if(paramType == smInput || paramType == smInOut) { hr = spMapper->get_callIndex(&idx); if(SUCCEEDED(hr) && (idx >= 0)) { CComVariant value; hr = pFrame->GetParam(idx, &value); hr = spMapper->put_ComValue(value); } } } if(SUCCEEDED(hr)) { // выполняем вызов hr = m_spProxy->Execute(spOp); } } if(SUCCEEDED(hr)) { // перебираем все параметры spEnum->Reset(); while(hr == S_OK) { CComPtr spMapper; long lFetched = 0; hr = spEnum->Next(1, &spMapper, &lFetched); if(!lFetched || hr != S_OK) break; smIsInputEnum paramType; hr = spMapper->get_IsInput(¶mType); // для out-параметров устанавливаем новое значение if(paramType == smOutput || paramType == smInOut) { long idx = 0; hr = spMapper->get_callIndex(&idx); if(SUCCEEDED(hr) && idx >= 0) { CComVariant value; hr = spMapper->get_ComValue(&value); hr = pFrame->SetParam(idx, &value); } } } } else { // если вызов завершился с ошибкой – устанавливаем return value pFrame->SetReturnValue(hr); } } return hr; } |
Приведенный выше код работать не будет. :с))
Во-первых, вызов ISoapMapper::get_callIndex всегда возвращает -1, независимо от параметра.
Во-вторых, вызов ICallFrame::SetParam возвращает ошибку E_NOTIMPL, т.е. он попросту не реализован для перехватчика.
Обходной путь для первой проблемы заключается в использовании другого метода – IsoapMapper::get_ParameterOrder, возвращающего порядковый номер параметра в описании WSML. Как правило, порядковый номер в описании WSML соответствует порядковому номеру параметра в сигнатуре метода.
ПРИМЕЧАНИЕ По крайней мере, стандартный генератор WSML из SOAP Toolkit генерирует WSML именно так. Возможно, в будущих версиях SOAP Toolkit эта проблема будет исправлена, и мы сможем использовать более уместный в данном случае метод callIndex. |
Решение второй проблемы не так очевидно. Необходимо каким-либо образом поместить в стек вызова значение out-параметра, но единственный подходящий для этих целей метод ICallFrame::SetParam возвращает E_NOTIMPL.
Разумеется, мы могли бы остановиться на этом. Наш пример корректно работает с in-параметрами, но не умеет передавать out-параметры.
Но все же есть способ добраться до местоположения адреса нужного параметра в стеке. Можно узнать адрес стека вызова с помощью ICallFrame::GetStackLocation:
PVOID GetStackLocation(void); |
А также получить информацию о параметре метода, его местоположение в стеке:
typedef struct { BOOLEAN fIn; BOOLEAN fOut; ULONG stackOffset; ULONG cbParam; } CALLFRAMEPARAMINFO; HRESULT GetParamInfo( ULONG iparam, CALLFRAMEPARAMINFO * pInfo ); |
Теперь, если сложить адрес первого аргумента в стеке вызова и смещение нужного параметра CALLFRAMEPARAMINFO::stackOffset, мы получим адрес параметра в стеке. Код, заполняющий out-параметр выглядел так:
CComVariant value; hr = spMapper->get_ComValue(&value); hr = pFrame->SetParam(idx, &value); |
Мы перепишем его так:
CComVariant value; hr = spMapper->get_ComValue(&value); PVOID pStack = pFrame->GetStackLocation(); CALLFRAMEPARAMINFO info = {0}; hr = pFrame->GetParamInfo(idx, &info); if(SUCCEEDED(hr) && info.cbParam == sizeof(long*)) { long** pParam = reinterpret_cast( reinterpret_cast(pStack) + info.stackOffset); if(!IsBadReadPtr(*pParam, sizeof(long))) { VARIANT var = {}; value.Detach(&var); **pParam = var.lVal; } } |
ПРИМЕЧАНИЕ Такой способ не выглядит изящным, к тому же он не будет работать, если размер параметра в стеке отличается от 4. |
Заключение