Современная электронная библиотека ModernLib.Net

Сущность технологии СОМ. Библиотека программиста

ModernLib.Net / Программирование / Бокс Дональд / Сущность технологии СОМ. Библиотека программиста - Чтение (стр. 18)
Автор: Бокс Дональд
Жанр: Программирование

 

 


// called to inform stub to release object

// вызван для информирования заглушки об освобождении объекта

void Disconnect(void);

// called when ORPC request arrives

// вызывается, когда поступает запрос ORPC

HRESULT Invoke ([in] RPCOLEMESSAGE *pmsg,

[in] IRpcChannelBuffer *pChannel);

// used to support multiple itf types per stub

// используется для поддержки нескольких типов интерфейсов

// для одной заглушки

IRpcStubBuffer *IsIIDSupported([in] REFIID riid);

// used to support multiple itf types per stub

// используется для поддержки нескольких интерфейсов

// для одной заглушки

ULONG CountRefs(vold);

// used by ORPC debugger to find pointer to object

// используется отладчиком ORPC для поиска указателя на объект

HRESULT DebugServerQueryInterface(void **ppv);

// used by ORPC debugger to release pointer to object

// используется отладчиком ORPC для освобождения указателя на объект

void DebugServerRelease(void *pv);

}


Метод Invoke будет вызываться библиотекой СОМ, когда поступит запрос ORPC на объект. При вводе маршалированные [in]-параметры будут находиться в RPCOLEMESSAGE, а при выводе заглушка должна маршалировать HRESULT метода и любые [out]-параметры, которые будут возвращены в блоке ответов ORPC.

Интерфейсный заместитель должен выставлять интерфейс (интерфейсы), за удаленный доступ к которым он отвечает, в дополнение к интерфейсу IRpcProxyBuffer:


[ uuid(D5F56A34-593B-101A-B569-08002B2DBF7A), local, object ]

interface IRpcProxyBuffer : IUnknown {

HRESULT Connect([in] IRpcChannelBuffer *pChannelBuffer);

void Disconnect(void);

}


Интерфейс IRpcPгoxуBuffer должен быть неделегирующим интерфейсом IUnknown интерфейсного заместителя. Все остальные интерфейсы, которые выставляет интерфейсный заместитель, должны делегировать администратору заместителей свои методы IUnknown. Именно в реализациях метода этих других интерфейсов интерфейсный заместитель должен использовать канал для посылки запросов ORPC на метод интерфейсной заглушки Invoke, который затем обрабатывает этот метод в апартаменте объекта.

Интерфейсные заместители и интерфейсные заглушки динамически связываются и совместно используют единый CLSID как для заместителя, так и для заглушки. Такую раздвоенную реализацию часто называют интерфейсным маршалером (interface marshaler). Объект класса интерфейсного маршалера выставляет интерфейс IPSFactoryBuffer:


[ uuid(D5F569DO-593B-101A-B569-08002B2DBF7A), local, object ]

interface IPSFactoryBuffer : IUnknown {

HRESULT CreateProxy(

[in] IUnknown *pUnkOuter,

// ptr to proxy manager

// указатель на администратор заместителей

[in] REFIID riid,

// the requested itf to remote

// запрошенный интерфейс для удаленного доступа

[out] IRpcProxyBuffer **ppProxy,

// ptr. to proxy itf.

// указатель на интерфейс заместителя

[out] void **ppv

// ptr to remoting interface

// указатель на удаленный интерфейс

);

HRESULT CreateStub(

[in] REFIID riid,

// the requested itf to remote

// запрошенный интерфейс для удаленного доступа

[in] IUnknown *pUnkServer,

// ptr to actual object

// указатель на действующий объект

[out] IRpcStubBuffer **ppStub

// ptr to stub on output

// указатель на заглушку на выходе

);

}


Администратор заместителей вызывает метод CreateProxy с целью агрегирования нового интерфейсного заместителя. Администратор заглушек вызывает метод CreateStub с целью создания новой интерфейсной заглушки.

Когда в объекте запрашивается новый интерфейс, то администраторы заместителей и заглушек должны преобразовать запрошенные IID и CLSID интерфейсного маршалера. Под Windows NT 5.0 хранилише класса совершает эти преобразования в директории NT, и они кэшируются в локальном реестре каждой хост-машины. Отображения IID в CLSID всей машины кэшируются в


HKEY_CLASSES_ROOT\Interface


а отображения каждого пользователя кэшируются в


HKEY_CURRENT_USER\Software\Classes\Interface


Один из этих ключей или оба будут содержать подключи для каждого известного интерфейса. Под Windows NT 4.0 и в более ранних версиях не существует хранилища классов и используется только область локального реестра HKEY_CLASSES_ROOT\Interface.

Если в интерфейсе установлен интерфейсный маршалер, то в реестре будет дополнительный подключ (ProxyStubClsid32). который показывает CLSID интерфейсного маршалера. Ниже показаны необходимые ключи реестра для интерфейсного маршалера:


[HKCR\Interface\{1A3A29F0-D87E-11d0-8C4F-0080C73925BA}]

@="IRacer"

[HKCR\Interface\{1A3A29F0-D87E-11d0-8C4F-OB80C73925BA}\ProxyStubClsid32]

@="{1A3A29F3-D87E-lld0-8C4F-0080C73925BA}"


Эти элементы реестра означают, что существует внутрипроцессный сервер с CLSID, равным {1A3A29F3-D87E-11d0-8C4F-0080C73925BA}, который реализует интерфейсные заместитель и заглушку для интерфейса IRacer ({1A3A29F0-D87E-11d0-8C4F-0080C73925BA}). Из этого следует, что HKCR\CLSID будет иметь подключ для интерфейсного маршалера, отображающего CLSID в соответствующее имя файла DLL. Опять же под Windows NT 5.0 это отображение может содержаться в хранилище классов, которое способно динамически заполнять локальный реестр. Поскольку интерфейсный маршалер должен выполняться в том же апартаменте, что и администратор заместителей или администратор заглушек, они должны использовать (флаг) ThreadingModel="Both" для гарантии того, что они всегда могут загрузиться в нужный апартамент.


Реализация интерфейсных маршалеров

В предыдущем разделе было показано четыре интерфейса, используемых архитектурой стандартного маршалинга. Хотя и допустимо реализовать интерфейсные маршалеры с помощью ручного кодирования на C++, на практике это осуществляется редко. Дело в том, что компилятор IDL может автоматически генерировать исходный С-код для интерфейсного маршалера на основе IDL-определения интерфейса. Созданные MIDL интерфейсные маршалеры преобразуют параметры метода в последовательную форму, используя протокол Сетевого Представления Данных (Network Data Representation – NDR), который допускает демаршалинг этих параметров при различных архитектурах хост-машин. NDR учитывает различия в порядке следования байтов, в формате с плавающей точкой, в наборе символов и в расположении результатов. NDR поддерживает фактически все совместимые с C типы данных. Для того чтобы обеспечить передачу интерфейсных указателей как параметров, MIDL генерирует вызовы CoMarshalInterface / CoUnmarshalInterface для маршалинга любых параметров интерфейсных указателей. Если параметр является статически типизированным интерфейсным указателем:


HRESULT Method([out] IRacer **ppRacer);


то сгенерированный код маршалера будет маршалировать параметр ppRacer путем передачи IID IRacer (IID_IRacer) вызовам CoMarshalInterface / CoUnmarshalInterface . Если же интерфейсный указатель типизирован динамически:


HRESULT Method([in] REFIID riid, [out, iid_is(riid)] void **ppv);


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

MIDL генерирует исходный код интерфейсного маршалера для каждого нелокального интерфейса, определенного вне области действия оператора library . В следующем псевдо-IDL коде


// sports.idl

// виды спорта. Язык описания интерфейсов

[local, object] interface IBoxer : IUnknown { … }

[object] interface IRacer : IUnknown { … }

[object] interface ISwimmer : IUnknown { … }

[helpstring(«Sports Lib»)]

library SportsLibrary {

interface IRacer;

// include def. of IRacer in TLB

// включаем определение IRacer в библиотеку типов TLB

[object] interface IWrestler : IUnknown { … } }


только интерфейсы IRacer и ISwimmer будут иметь исходный код интерфейсного маршалера. MIDL не будет генерировать маршалирующий код для IBoxer, поскольку атрибут [local] подавляет маршалинг. MIDL также не будет генерировать маршалер для IWrestler, поскольку он определен внутри области действия библиотечного оператора.

Если MIDL представлен в IDL такого типа, он будет генерировать пять файлов. Файл sports.h будет содержать С/С++-определения интерфейсов, sports_i.с – IID и LIBID, a sports.tlb – разобранный (tokenized) IDL для IRacer и IWrestler , который можно использовать в средах разработки, поддерживающих СОМ. Файл sports_p.c будет содержать фактические реализации методов интерфейсных заместителей и заглушек, которые осуществляют преобразования вызова методов в NDR. Этот файл также может содержать С-определения виртуальной таблицы (vtable) для интерфейсных заместителей и заглушек наряду со специальным управляющим кодом MIDL. Поскольку интерфейсные маршалеры являются внутрипроцессными серверами СОМ, то четыре стандартные точки входа (DllGetClassObject и другие) должны быть также определены. Эти четыре метода определены в пятом файле dlldata.c.

Для того чтобы построить интерфейсный маршалер из этих сгенерированных файлов, необходимо лишь создать сборочный файл (makefile), который скомпилирует три исходных С-файла (sports_i.с, sports_p.c, dlldata.c) и скомпонует их имеете для создания библиотеки DLL. Четыре стандартные точки входа СОМ должны быть явно экспортированы с помощью либо файла определения модуля, либо переключателей компоновщика. Отметим, что по умолчанию dlldata.c содержит только определения DllGetClassObject и DllCanUnloadNow . Это происходит потому, что поддерживающая RPC динамическая библиотека под Windows NT 3.50 поддерживала только эти две подпрограммы. Если интерфейсный маршалер будет использоваться только под Windows NT 3.51 или под более поздние версии (а также под Windows 95), то символ С-препроцсссора REGISTER_PROXY_DLL должен быть определен при компиляции файла dlldata.c , чтобы стандартные входные точки саморегистрации также были скомпилированы. Интерфейсный маршалер после создания должен быть установлен в локальном реестре и/или в хранилище классов.

В реализацию библиотеки СОМ под Windows NT 4.0 введена поддержка полностью интерпретируемого (interpretive) маршалинга. В зависимости от интерфейса использование интерпретируемого маршалера может значительно увеличить эффективность приложения путем сокращения объема рабочей памяти (working set). Предварительно установленные интерфейсные маршалеры для всех стандартных интерфейсов СОМ используют интерпретируемый маршалер. Microsoft Transaction Server (MTS) обязывает интерфейсные маршалеры использовать интерпретируемый маршалер[1]. Чтобы задействовать интерпретируемый маршалер, просто запустите компилятор MIDL с переключателем /Oicf в командной строке:

midl.exe /0icf sports.idl

В то время, когда пишется этот текст, компилятор MIDL не перезаписывает существующий файл _p.c, так что при изменении данной установки этот файл должен быть удален. Поскольку интерфейсные маршалеры, основанные на /Oicf, не будут работать на версиях СОМ до Windows NT 4.0, то при компиляции исходного кода маршалера символу С-препроцессора _WIN32_WINNT нужно присвоить целое значение, большее или равное 0х400. С-компилятор сделает это во время компиляции.

Третья методика для генерирования интерфейсных маршалеров поддерживается ограниченным числом интерфейсных классов. Если интерфейс использует только простые типы данных, которые поддерживаются VARIANT[2], то можно использовать универсальный маршалер. Использование универсального маршалера разрешается путем добавления атрибута [oleautomation] к определению интерфейса:


[ uuid(F99D19A3-D8BA-11d0-8C4F-0080C73925BA), version(1.0)]

library SportsLib {

importlib(«stdole32.tlb»);

[

uuid(F99D1907-D8BA-11D0-8C4F-0080C73925BA), object,

oleautomation

]

interface IWrestler : IUnknown {

import «oaidl.idl»;

HRESULT HalfNelson([in] double nmsec);

} }


Наличие атрибута [oleautomation] информирует функцию RegisterTypeLib , что при регистрации библиотеки типов ей следует добавить следующие дополнительные элементы реестра:


[HKCR\Interface\{F99D1907-D8BA-11d0-8C4F-0080C73925BA}]

@="IWrestler"

[HKCR\Interface\{F99D1907-D8BA-11d0-8C4F-0080C73925BA}\ProxyStubClsid32]

@="{O0020424-0000-0000-C000-000000000046}"

[HKCR\Interface\{F99D1907-D8BA-11d0-8C4F-0080C73925BA}\ProxyStubClsid]

@="{O0020424-0000-0000-C000-000000000046}"

[HKCR\Interface\{F99D1907-D8BA-11d0-8C4F-0080C73925BA}\TypeLib]

@="{F99D19AЗ-08BA-11d0-8C4F-0080C73925BA}"

Version="1.0"


CLSID {O0020424-0000-0000-C000-000000000046} соответствует универсальному маршалеру, который предварительно устанавливается на всех платформах, поддерживающих СОМ, в том числе в 16-разрядных Windows.

Основное преимущество использования универсального маршалера заключается в том, что это – единственная поддерживаемая методика осуществления стандартного маршалинга между 16– и 32-разрядными приложениями. Кроме того, универсальный маршалер совместим с Microsoft Transaction Server. Другое преимущество универсального марщалера заключается в следующем: если библиотека типов установлена на хост-машинах и клиента, и объекта, то не потребуется никакой дополнительной DLL интерфейсного марщалера. Основной же недостаток использования универсального маршалера – ограниченная поддержка типов данных параметров. Это то же самое ограничение, которое устанавливают динамический вызов и среды выполнения сценариев, но является серьезным ограничением при разработке интерфейсов системного программирования низкого уровня[3]. Под Windows NT 4.0 начальные затраты на вызов CoMarshalInterface / CoUnmarshalInterface несколько повысятся при использовании универсального маршалера. Однако после обработки интерфейсных заместителя и заглушки затраты на вызов метода становятся эквивалентными затратам на /0icf -маршалер.


Стандартный маршалинг, потоки и протоколы

Подробности того. как СОМ на самом деле преобразует запросы ORPC в потоки, нигде не документированы и подлежат изменениям но мере развития реализации библиотеки СОМ. Описания, содержащиеся в данном разделе, относятся только ко времени написания этого текста, но, конечно, некоторые детали реализации могут измениться в последующих выпусках СОМ.

Когда в процессе инициализируется первый апартамент, СОМ включает RPC-слой времени выполнения, переводя процесс в RPC-сервер. Если апартамент имеет тип МТА или RTA, то используется RPC-протокол ncalrpc , который является оберткой вокруг портов LPC (local procedure call – локальный вызов процедуры) под Windows NT. Если тип апартамента – STA, то используется последовательность закрытого протокола, основанная на очередях сообщений MSG Windows. При первом обращении внехостовых клиентов к объектам, постоянно находящимся в процессе, в процессе регистрируются дополнительные сетевые протоколы. Когда процесс впервые начинает использовать протоколы, отличные от протокола MSG Windows , запускается кэш потоков RPC. Этот кэш потоков начинается с одного потока, который ожидает приходящих запросов на установление соединения, запросов RPC или других действий, специфических для протоколов. Как только произойдет любое из этих событий, кэш потоков RPC переключит поток на обслуживание запроса и будет ждать следующих действий. Для предотвращения излишних затрат на создание/уничтожение потоков эти потоки возвращаются в потоковый кэш, где они будут ждать дальнейшей работы. Если работы нет, то потоки самоуничтожатся по истечении заранее определенного периода бездействия. Теневая сторона этого заключается в том, что кэш потоков RPC растет или сжимается в зависимости от занятости объектов, экспортированных из апартаментов процессов. С точки зрения программирования важно заметить, что кэш потоков RPC динамически размещает потоки, основанные на ORPC-запросах, приходящие по любым протоколам, кроме протокола Windows MSG, который будет обсуждаться позднее в этом разделе.

Когда поступающий ORPC-запрос переадресуется потоку из кэша. поток выделяет из заголовка ORPC-вызова IPID (идентификатор указателя интерфейса) и находит соответствующий администратор заглушек и интерфейсную заглушку. Поток определяет тип того апартамента, в котором хранится объект, и если объект находится в апартаменте типа МТА или RTA, поток входит в апартамент объекта и вызывает метод IRpcStubBuffer::Invoke на интерфейсную заглушку. Если апартамент имеет тип RTA, то в течение вызова метода последующие потоки не будут допускаться к объекту. Если апартамент имеет тип МТА, то последующие потоки могут обращаться к объекту одновременно. В случае внутрипроцессных RTA/MTA-связей канал может сократить кэш потоков RPC и использовать поток клиента повторно, временно входя в апартамент объекта. Если бы МТА и RTA были единственными типами апартаментов, то этого было бы достаточно.




Диспетчеризация вызовов в STA более сложна, так как в существующий STA не могут войти никакие другие потоки. К сожалению, когда ORPC-запросы поступают от внехостовых клиентов, они координируются с использованием потоков из RPC кэша потоков, которые по определению не могут выполняться в STA объекта. Для того чтобы войти в STA и направить вызов потока STA, RPC-поток использует АРI-функцию PostMessage , которая ставит сообщение в специальную MSG -очередь сообщений STA-потоков, как показано на рис. 5.5. Эта очередь представляет собой ту же очередь FIFO (first-in, first-out), которую применяет оконная система. Это означает, что для завершения диспетчеризации вызова STA-поток должен обслуживать очередь с помощью одной из вариаций следующего кода:


MSG msg;

while (GetMessage(&msg, 0, О, 0))

DispatchMessage(&msg);


Этот код означает, что STA-поток имеет по меньшей мере одно окно, которое может принимать сообщения. Когда поток входит в новый STA посредством вызова CoInitializeEx , СОМ создает новое невидимое окно, вызывая CreateWindowEx. Это окно связано с зарегистрированным в СОМ оконным классом, элемент которого WndProc ищет определенные заранее оконные сообщения и обслуживает соответствующий ORPC-запрос посредством вызова метода IRpcStubBuffer::Invoke на интерфейсную заглушку. Отметим, что поскольку окна, подобно объектам на основе STA, обладают потоковой привязкой, WndProc будет выполняться в апартаменте объекта. Чтобы избежать чрезмерного количества переключения потоков, в версии СОМ для Windows 95 предусмотрен транспортный RPC-механизм, который обходит RPC-кэш потоков и вызывает PostMessage из потока вызывающего объекта. Этот перенос возможен только в том случае, если клиент находится на том же хосте, что и объект, поскольку API-функция PostMessage не работает в сети.

Для предотвращения взаимоблокировки все типы апартаментов СОМ поддерживают реентерабельность (повторную входимость)[1]. Когда поток в апартаменте делает запрос на объект вне апартамента вызывающего объекта посредством заместителя, то могут обслуживаться и другие поступающие запросы методов, пока поток вызывающего объекта находится в ожидании ORPC-ответа па первый запрос. Без этой поддержки было бы невозможно создавать системы, основанные на совместно работающих объектах. При написании следующего кода предполагалось, что CLSID_Callback является внутрипроцессным сервером, поддерживающим модель вызывающего потока, и что CLSID_Object является классом, сконфигурированным для активации на удаленной машине:


ICallback *pcb = 0;

HRESULT hr = CoCreateInstance(CLSID_Callback, 0, CLSCTX_ALL,

IID_ICallback, (void**)&pcb);

assert(SUCCEEDED(hr));

// callback object lives in this apt.

// объект обратного вызова живет в этом апартаменте

I0bject "po = 0;

hr = CoCreateInstance(CLSID_Object, 0, CLSCTX_REMOTE_SERVER,

IID_Iobject, (void **)&po);

assert(SUCCEEDED(hr));

// object lives in different apt.

// объект живет в другом апартаменте

// make a call to remote object, marshaling a reference to

// the callback object as an [in] parameter

// делаем вызов удаленного объекта, маршалируя ссылку

// на объект обратного вызова как на [in]-параметр

hr = po->UseCallback(pcb);

// clean up resources

// очищаем ресурсы

pcb->Release();

pco->Release();


На рис. 5.6 показано, что если апартамент вызывающего объекта не поддерживает реентерабельность, то следующая реализация метода UseCallback вызовет взаимоблокировку:


STDMETHODIMP Object::UseCallback(ICallback *pcb) {

HRESULT hr = pcb->GetBackToCallersApartment();

assert(SUCCEEDED(hr));

return S_OK;





Напомним, что когда [in]–параметр передается через метод заместителя UseCallback, то заместитель вызывает CoMarshalInterface для маршалинга интерфейсного указателя ICallback. Поскольку указатель ссылается на объект, находящийся в апартаменте вызывающего объекта, то этот апартамент становится экспортером объектов и поэтому любые межапартаментные вызовы объекта должны обслуживаться в апартаменте вызывающего объекта. Когда заглушка интерфейса IObject демаршалирует интерфейс ICallback, она создает заместитель для передачи его реализации метода UseCallback. Этот заместитель представляет объект при промежуточном соединении с объектом обратного вызова, которое продолжается на протяжении всего времени вызова. Время существования этого заместителя/соединения может превысить время вызова, если реализация метода просто вызовет AddRef на заместитель[2]:


STDMETHODIMP Object::UseCallback(ICallback *pcb) {

if (!pcb) return E_INVALIDARG;

// hold onto proxy for later use

// сохраняем в заместителе для дальнейшего использования

(m_pcbMyCaller = pcb)->AddRef();

return S_OK; }


Обратное соединение с апартаментом клиента будет длиться до тех пор, пока заместитель не будет освобожден объектом. Поскольку все апартаменты СОМ могут получать ORPC-запросы, объект может послать обратный вызов в апартамент клиента в любое время.

Реентерабельность реализуется для каждого типа апартаментов по-разному. Наиболее проста реализация в случае МТА, так как МТА-апартаменты не гарантируют параллелизма и не указывают, какой поток будет обслуживать заданный вызов метода. Повторный вызов может прийти в то время, когда МТА-поток заблокирован в канале в ожидании ORPC-ответа. Тогда RPC-поток, получающий повторный запрос, просто входит в МТА и обслуживает вызов своими ресурсами. Тот факт, что другой поток апартамента заблокирован и ожидании ORPC-ответа, не влияет на диспетчеризацию вызова. В случае реализации RTA – когда поток, выполняющийся в RTA, делает межапартаментный вызов посредством заместителя, – канал уступает контроль над апартаментом, снимая блокировку всего RTA и разрешая тем самым обработку поступивших вызовов. И снова, но причине отсутствия привязки объектов к потокам в RTA, RPC-поток, получающий ORPC-запрос, может просто войти в апартамент RTA и обслужить вызов сразу после блокирования всего RTA.

Реализация реентерабельности для апартаментов STA более сложна. Поскольку STA-объекты обладают привязкой к потоку, то когда поток делает межапартаментный вызов из STA, СОМ не может разрешить потоку сделать блокирующий вызов, который предотвратил бы обработку входящих ORPC-запросов. Когда поток вызывающего объекта входит в метод канала SendReceive , чтобы послать ORPC-запрос и получить ORPC-ответ, этот канал захватывает поток вызывающего объекта и помещает его во внутренний оконный MSG -цикл. Это аналогично тому, что происходит при создании потоком модальных диалоговых окон. В обоих случаях поток вызывающего объекта вынужден обслуживать определенные классы оконных сообщений во время выполнения этой операции. В случае модальных диалоговых окон поток должен обслуживать основные оконные сообщения, чтобы разморозить основной пользовательский интерфейс. В случае межапартаментного вызова метода в СОМ поток должен обслуживать не только обычные оконные сообщения пользовательского интерфейса, но и оконные сообщения, относящиеся к поступающим ORPC-запросам. По умолчанию канал будет разрешать обслуживание всех поступающих ORPC-вызовов, пока клиентский поток ожидает ORPC-ответа. Такой режим можно настроить с помощью установки в потоке специального фильтра сообщений.

Фильтры сообщений являются уникальными для STA. Фильтр сообщений – это объект СОМ для каждого STA, который используется для решения вопроса, организовать диспетчеризацию поступающих ORPC-запросов или нет. Кроме того, фильтры сообщений используются для размещения задержанных сообщений пользовательского интерфейса, пока поток STA ожидает ORPC-ответа внутри канала. Фильтры сообщений выставляют интерфейс IMessageFilter:


[ uuid(00000016-0000-0000-C000-000000000046),local, object ]

interface IMessageFilter : IUnknown {

typedef struct tagINTERFACEINFO {

IUnknown *pUnk;

// which object?

// чей объект?

IID iid;

// which interface?

// чей интерфейс?

WORD wMethod;

// which method?

// чей метод?

} INTERFACEINFO;

// called when an incoming ORPC request arrives in an STA

// вызывается, когда входящий ORPC-запрос поступает в STA

DWORD HandleInComingCall(

[in] DWORD dwCallType,

[in] HTA5K dwThreadIdCaller,

[in] DWORD dwTickCount,

[in] INTERFACEINFO *pInterfaceInfo

);

// called when another STA rejects or postpones an ORPC request

// вызывается, когда другой STA отклоняет или откладывает ORPC-запрос

DWORD RetryRejectedCall(

[in] HTASK dwThreadIdCallee,

[in] DWORD dwTickCount,

[in] DWORD dwRejectType

);

// called when a non-COM MSG arrives while the thread is

// awaiting an ORPC response

// вызывается, когда поступает не СОМ'овское MSG, пока

// поток ожидает ORPC-ответа

DWORD MessagePending(

[in] HTASK dwThreadIdCallee,

[in] DWORD dwTickCount,

[in] DWORD dwPendingType

); }


Для установки специального фильтра сообщений в СОМ существует API-функция CoRegisterMessageFilter:


HRESULT CoRegisterMessageFilter([in] IMessageFilter *pmfNew, [out] IMessageFilter **ppmfOld);


CoRegisterMessageFilter связывает указанный фильтр сообщений с текущим STA. Предыдущий фильтр сообщений возвращается для того, чтобы вызывающий объект мог восстановить его в дальнейшем.

Когда бы входящий ORPC-запрос ни пришел в STA-поток, вызывается метод фильтра сообщений HandleIncomingCall, который дает апартаменту возможность принять, отклонить или отложить вызов. HandleIncomingCall используется как реентерабельными, так и нереентерабельными вызовами. Параметр dwCallType показывает, какой тип вызова был получен:


typedef enum tagCALLTYPE {

CALLTYPE_TOPLEVEL,

// STA not in outbound call

// STA не в исходящем вызове

CALLTYPE_NESTED,

// callback on behalf of outbound call

// обратный вызов от имени исходящего вызова

CALLTYPE_ASYNC,

// asynchronous call

// асинхронный вызов

CALLTYPE_TOPLEVEL_CALLPENDING,

// new call while waiting

// новый вызов во время ожидания

CALLTYPE_ASYNC_CALLPENDING

// async call while waiting

// асинхронный вызов во время ожидания

} CALLTYPE;


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

В СОМ определено перечисление, которое должна возвратить реализация HandleIncomingCall, чтобы указать, что произошло с вызовом:


typedef enum tagSERVERCALL {

SERVERCALL_ISHANDLED,

// accept call and forward to stub

// принимаем вызов и направляем его заглушке

SERVERCALL_REJECTED,

// tell caller that call is rejected

// сообщаем вызывающему объекту, что вызов отклонен

SERVERCALL RETRYLATER

// tell caller that call is postponed

// сообщаем вызывающему объекту, что вызов отложен

} SERVERCALL;


Если функция HandleIncomingCall фильтра сообщений возвращает SERVERCALL_ISHANDLED, то вызов будет направлен в интерфейсную заглушку для демаршалинга. Фильтр сообщений, принятый по умолчанию, всегда возвращает SERVERCALL_ISHANDLED. Если HandleIncomingCall возвращает SERVERCALL_REJECTED или SERVERCALL_RETRYLATER, то фильтр сообщений вызывающего объекта будет информирован о положении вызова и ORPC-запрос будет отклонен.


  • Страницы:
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33