Регистрация интерфейсов

В этом разделе подробно описывается процесс регистрации интерфейса RPC.

Сведения в этом разделе представлены в следующих разделах:

Функции регистрации интерфейса

Серверы регистрируют свои интерфейсы, вызывая функцию RpcServerRegisterIf . Сложные серверные программы часто поддерживают несколько интерфейсов. Серверные приложения должны вызывать эту функцию один раз для каждого поддерживаемого интерфейса.

Кроме того, серверы могут поддерживать несколько версий одного интерфейса, каждая из которых имеет собственную реализацию функций интерфейса. Если это делает серверная программа, она должна предоставить набор точек входа. Точка входа — это подпрограмма руководителя, которая отправляет вызовы для версии интерфейса. Для каждой версии интерфейса должна быть одна точка входа. Группа точек входа называется вектором точек входа. Дополнительные сведения см. в разделе Векторы точек входа.

Помимо стандартной функции RpcServerRegisterIf, RPC также поддерживает другие функции регистрации интерфейса. Функция RpcServerRegisterIf2 расширяет возможности RpcServerRegisterIf , позволяя указать набор флагов регистрации (см. раздел Флаги регистрации интерфейса), максимальное количество одновременных запросов на вызов удаленной процедуры, которые может принимать сервер, и максимальный размер в байтах входящих блоков данных.

Библиотека RPC также содержит функцию с именем RpcServerRegisterIfEx. Как и функция RpcServerRegisterIf , эта функция регистрирует интерфейс. Серверная программа также может использовать эту функцию для указания набора флагов регистрации (см. раздел Флаги регистрации интерфейса), максимального количества одновременных запросов на вызовы удаленных процедур, которые может принимать сервер, и функции обратного вызова безопасности.

Функции RpcServerRegisterIf, RpcServerRegisterIfEx и RpcServerRegisterIf2 задают значения во внутренней таблице реестра интерфейсов. Эта таблица используется для сопоставления UUID интерфейса и UUID объектов с EPV диспетчера. EPV диспетчера — это массив указателей функций, содержащий ровно один указатель на функцию для каждого прототипа функции в интерфейсе, указанном в IDL-файле.

Сведения о предоставлении нескольких EPV-представлений для предоставления нескольких реализаций интерфейса см. в разделе Реализации нескольких интерфейсов.

Библиотека времени выполнения использует таблицу реестра интерфейсов (задается с помощью вызовов функции RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2) и таблицу реестра объектов (задается вызовами функции RpcObjectSetType) для сопоставления интерфейсов и объектов UUID с указателем функции.

Если вы хотите, чтобы серверная программа удалила интерфейс из реестра библиотеки времени выполнения RPC, вызовите функцию RpcServerUnregisterIf . После удаления интерфейса из реестра библиотека времени выполнения RPC больше не будет принимать новые вызовы для этого интерфейса.

Векторы точек входа

Вектор точки входа (EPV) диспетчера — это массив указателей функций, указывающих на реализации функций, указанных в IDL-файле. Количество элементов в массиве соответствует числу функций, указанных в IDL-файле. RPC поддерживает несколько векторов точек входа, представляющих несколько реализаций функций, указанных в интерфейсе .

Компилятор MIDL автоматически создает тип данных EPV диспетчера для использования при создании epv диспетчера. Тип данных называется if-name**_SERVER_EPV**, где if-name указывает идентификатор интерфейса в IDL-файле.

Компилятор MIDL автоматически создает и инициализирует EPV диспетчера по умолчанию, исходя из предположения, что подпрограмма диспетчера с тем же именем существует для каждой процедуры в интерфейсе и указана в IDL-файле.

Если сервер предлагает несколько реализаций одного интерфейса, сервер должен создать один дополнительный EPV диспетчера для каждой реализации. Каждый EPV должен содержать ровно одну точку входа (адрес функции) для каждой процедуры, определенной в IDL-файле. Серверное приложение объявляет и инициализирует одну переменную EPV диспетчера типа if-name**_SERVER_EPV** для каждой дополнительной реализации интерфейса. Чтобы зарегистрировать EPV, он вызывает RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 один раз для каждого типа объекта, который он поддерживает.

Когда клиент выполняет удаленный вызов процедуры к серверу, EPV, содержащий указатель функции, выбирается на основе UUID интерфейса и типа объекта. Тип объекта является производным от объекта UUID с помощью функции запроса объекта или табличного сопоставления, управляемого RpcObjectSetType.

EpV диспетчера

По умолчанию компилятор MIDL использует имена процедур из IDL-файла интерфейса для создания EPV диспетчера, который компилятор помещает непосредственно в заглушку сервера. Этот EPV по умолчанию статически инициализируется с помощью имен процедур, объявленных в определении интерфейса.

Чтобы зарегистрировать диспетчер с использованием EPV по умолчанию, укажите NULL в качестве значения параметра MgrEpv в вызове функции RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 . Если имена подпрограмм, используемые диспетчером, соответствуют именам в определении интерфейса, этот диспетчер можно зарегистрировать с помощью EPV по умолчанию интерфейса, созданного компилятором MIDL. Вы также можете зарегистрировать руководителя с помощью EPV, который предоставляет серверное приложение.

Сервер может (а иногда и должен) создать и зарегистрировать EPV диспетчера, отличного от null , для интерфейса. Чтобы выбрать предоставленный серверным приложением EPV, передайте адрес EPV, значение которого было объявлено сервером в качестве значения параметра MgrEpv a. Значение, отличное от NULL , для параметра MgrEpv всегда переопределяет EPV по умолчанию в заглушку сервера.

Компилятор MIDL автоматически создает тип данных EPV диспетчера (RPC_MGR_EPV) для серверного приложения, который будет использоваться при создании epv диспетчера. EPV диспетчера должен содержать ровно одну точку входа (адрес функции) для каждой процедуры, определенной в IDL-файле.

Сервер должен предоставлять EPV, отличный от NULL , в следующих случаях:

  • Если имена подпрограмм диспетчера отличаются от имен процедур, объявленных в определении интерфейса
  • Когда сервер использует EPV по умолчанию для регистрации другой реализации интерфейса

Сервер объявляет EPV диспетчера, инициализируя переменную типа if-name**_SERVER_EPV** для каждой реализации интерфейса.

Регистрация одной реализации интерфейса

Если сервер предлагает только одну реализацию интерфейса, сервер вызывает RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 только один раз. В стандартном случае сервер использует EPV диспетчера по умолчанию. (Исключением является использование диспетчером имен подпрограмм, отличных от объявленных в интерфейсе.)

В стандартном случае для вызовов RpcServerRegisterIf, RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 укажите следующие значения:

  • EpV диспетчера

    Чтобы использовать EPV по умолчанию, укажите значение NULL для параметра MgrEpv a.

  • Тип диспетчера UUID

    При использовании EPV по умолчанию зарегистрируйте интерфейс с типом UUID диспетчера nil, указав значение NULL или nil UUID для параметра MgrTypeUuid . В этом случае все удаленные вызовы процедур, независимо от объекта UUID в их дескрипторе привязки, отправляются в EPV по умолчанию, при условии, что вызовы RpcObjectSetType не были выполнены.

    Вы также можете указать UUID типа диспетчера, отличного от nil. В этом случае необходимо также вызвать подпрограмму RpcObjectSetType .

Регистрация нескольких реализаций интерфейса

Можно предоставить несколько реализаций удаленных процедур, указанных в IDL-файле. Серверное приложение вызывает RpcObjectSetType , чтобы сопоставить UUID объекта с типом UUID, и вызывает RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 , чтобы связать EPV диспетчера с типом UUID. При поступлении удаленного вызова процедуры с объектом UUID библиотека времени выполнения сервера RPC сопоставляет объект UUID с типом UUID. Затем серверное приложение использует тип UUID и UUID интерфейса для выбора EPV диспетчера.

Вы также можете указать собственную функцию для разрешения сопоставления UUID объекта с типом UUID диспетчера. Функция сопоставления указывается при вызове RpcObjectSetInqFn.

Чтобы предложить несколько реализаций интерфейса, сервер должен зарегистрировать каждую реализацию, вызвав RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 отдельно. Для каждой реализации, регистрируемой сервером, он предоставляет один и тот же параметр IfSpec , но разные пары параметров MgrTypeUuid и MgrEpv .

В случае нескольких руководителей используйте RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 следующим образом:

  • EpV диспетчера

    Чтобы предложить несколько реализаций интерфейса, сервер должен:

    • Создайте ненулевой EPV-адрес диспетчера для каждой дополнительной реализации.
    • Укажите значение, отличное от NULL , для параметра MgrEpv в RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2.

    Обратите внимание, что сервер также может зарегистрировать в диспетчере EPV по умолчанию.

  • Тип диспетчера UUID

    Укажите тип диспетчера UUID для каждого EPV интерфейса. Тип nil UUID (или значение NULL ) для параметра MgrTypeUuid можно указать для одного из epv диспетчера. Каждый тип UUID должен быть разным.

Правила для вызова подпрограмм диспетчера

Библиотека времени выполнения RPC отправляет входящий удаленный вызов процедуры руководителю, который предлагает запрошенный интерфейс RPC. Если для интерфейса зарегистрировано несколько руководителей, библиотека времени выполнения RPC должна выбрать один из них. Чтобы выбрать диспетчер, библиотека времени выполнения RPC использует объект UUID, указанный дескриптором привязки вызова.

Библиотека времени выполнения применяет следующие правила при интерпретации UUID объекта удаленного вызова процедуры:

  • Идентификаторы UUID объекта Nil

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

  • Идентификаторы UUID объектов, отличные от nil

    В принципе удаленный вызов процедуры, дескриптор привязки которого содержит UUID объекта, отличного от nil, должен обрабатываться диспетчером, тип UUID которого соответствует типу UUID объекта. Однако для идентификации правильного диспетчера требуется, чтобы сервер указал тип UUID этого объекта путем вызова подпрограммы RpcObjectSetType .

    Если серверу не удается вызвать подпрограмму RpcObjectSetType для UUID объекта, отличного от nil, удаленный вызов процедуры для этого объекта UUID отправляется в диспетчер EPV, который обслуживает удаленные вызовы процедур с UUID объекта nil (т. е. UUID типа nil).

    Удаленные вызовы процедур с UUID объекта, отличного от nil, в дескрипторе привязки не могут быть выполнены, если сервер назначил UUID ненулевого объекта UUID типа UUID путем вызова подпрограммы RpcObjectSetType , но не зарегистрировал EPV диспетчера для этого типа UUID путем вызова RpcServerRegisterIfIf, RpcServerRegisterIfEx или RpcServerRegisterIf2.

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

UUID объекта вызова Тип набора сервера для объекта UUID? Тип EPV, зарегистрированный сервером? Действие диспетчеризации
Nil Неприменимо Да Использует диспетчер с типом UUID nil.
Nil Неприменимо Нет Ошибка (RPC_S_UNSUPPORTED_TYPE); отклоняет удаленный вызов процедуры.
Без нулевых Да Да Использует диспетчер с тем же типом UUID.
Без нулевых нет Не учитывается Использует диспетчер с типом UUID nil. Если нет диспетчера с типом UUID nil, ошибка (RPC_S_UNSUPPORTEDTYPE); отклоняет удаленный вызов процедуры.
Без нулевых Да Нет Ошибка (RPC_S_UNSUPPORTEDTYPE); отклоняет удаленный вызов процедуры.

 

Объект UUID вызова — это объект UUID, найденный в дескрипторе привязки для удаленного вызова процедуры.

Сервер задает тип UUID объекта, вызывая RpcObjectSetType , чтобы указать тип UUID для объекта.

Сервер регистрирует тип для EPV диспетчера, вызывая RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 , используя тот же тип UUID.

Примечание

UUID объекта nil всегда автоматически назначается типу nil UUID. Недопустимо указывать UUID объекта nil в подпрограмме RpcObjectSetType .

 

Отправка удаленного вызова процедур в подпрограмму диспетчера сервера

В следующих таблицах показаны шаги, которые библиотека времени выполнения RPC выполняет для отправки удаленного вызова процедуры в подпрограмму диспетчера сервера.

В следующих таблицах описан простой случай, когда сервер регистрирует EPV диспетчера по умолчанию.

Таблица реестра интерфейсов

UUID интерфейса Тип диспетчера UUID Вектор точки входа
uuid1 Nil EPV по умолчанию

 

Таблица реестра объектов

UUID объекта Тип объекта
Nil Nil
(Любой другой объект UUID) Nil

 

Сопоставление дескриптора привязки с вектором точки входа (EPV)

UUID интерфейса (из дескриптора привязки клиента) UUID объекта (из дескриптора привязки клиента) Тип объекта (из таблицы реестра объектов) EPV диспетчера (из таблицы реестра интерфейсов)
uuid1 Nil Nil EPV по умолчанию
То же, что и выше uuidA Nil EPV по умолчанию

 

Ниже описаны действия, которые выполняет библиотека времени выполнения сервера RPC, как показано в предыдущих таблицах, когда клиент с интерфейсом UUID uuid1 вызывает ее.

  1. Сервер вызывает RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 , чтобы связать интерфейс, который он предлагает, с типом UUID диспетчера nil и с созданным MIDL-сервером EPV диспетчера по умолчанию. Этот вызов добавляет запись в таблицу реестра интерфейсов. UUID интерфейса содержится в параметре IfSpec a.

  2. По умолчанию таблица реестра объектов связывает все идентификаторы UUID объектов с типом UUID nil. В этом примере сервер не вызывает RpcObjectSetType.

  3. Библиотека времени выполнения сервера получает код удаленной процедуры, содержащий UUID интерфейса, к которому принадлежит вызов, и объект UUID из дескриптора привязки вызова.

    Сведения о том, как объект UUID устанавливается в дескриптор привязки, см. в следующих справочных записях о функциях:

  4. Используя интерфейс UUID из удаленного вызова процедуры, библиотека времени выполнения сервера находит этот интерфейс UUID в таблице реестра интерфейсов.

    Если сервер не зарегистрировал интерфейс с помощью RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2, удаленный вызов процедуры возвращается вызывающей с RPC_S_UNKNOWN_IF кодом состояния.

  5. Используя UUID объекта из дескриптора привязки, библиотека времени выполнения сервера находит этот объект UUID в таблице реестра объектов. В этом примере все идентификаторы UUID объекта сопоставляются с типом объекта nil.

  6. Библиотека времени выполнения сервера находит тип диспетчера nil в таблице реестра интерфейсов.

  7. Объединение UUID интерфейса и типа nil в таблице реестра интерфейсов разрешается в EPV по умолчанию, который содержит подпрограммы диспетчера сервера, которые должны выполняться для интерфейса UUID, найденного в удаленном вызове процедуры.

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

Таблица реестра интерфейсов

UUID интерфейса UUID типа диспетчера Вектор точки входа
uuid1 Nil epv1
uuid1 uuid3 epv4
uuid2 uuid4 epv2
uuid2 uuid7 epv3

 

Таблица реестра объектов

UUID объекта Тип объекта
uuidA uuid3
uuidB uuid7
uuidC uuid7
uuidD uuid3
uuidE uuid3
uuidF uuid8
Nil Nil
(Любой другой UUID) Nil

 

Сопоставление дескриптора привязки с вектором точки входа

UUID интерфейса (из дескриптора привязки клиента) UUID объекта (из дескриптора привязки клиента) Тип объекта (из таблицы реестра объектов) EPV диспетчера (из таблицы реестра интерфейсов)
uuid1 Nil Nil epv1
uuid1 uuidA uuid3 epv4
uuid1 uuidD uuid3 epv4
uuid1 uuidE uuid3 epv4
uuid2 uuidB uuid7 epv3
uuid2 uuidC uuid7 epv3

 

Ниже описаны действия, выполняемые библиотекой времени выполнения сервера, как показано в предыдущих таблицах, когда клиент с интерфейсом UUID uuid2 и объектом UUID uuidC вызывает ее.

  1. Сервер вызывает RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 , чтобы связать интерфейсы, которые он предлагает, с различными EPV диспетчера. Записи в таблице реестра интерфейсов отражают четыре вызова RpcServerRegisterIf, RpcServerRegisterIfEx или RpcServerRegisterIf2 для предоставления двух интерфейсов с двумя реализациями (EPV) для каждого интерфейса.

  2. Сервер вызывает RpcObjectSetType , чтобы определить тип каждого объекта, который он предлагает. В дополнение к сопоставлению по умолчанию объекта nil с типом nil, все остальные идентификаторы UUID объектов, которые явно не найдены в таблице реестра объектов, также сопоставляются с типом nil UUID.

    В этом примере сервер вызывает подпрограмму RpcObjectSetType шесть раз.

  3. Библиотека времени выполнения сервера получает удаленный вызов процедуры, содержащий UUID интерфейса, к которому принадлежит вызов, и объект UUID из дескриптора привязки вызова.

  4. Используя UUID интерфейса из удаленного вызова процедуры, библиотека времени выполнения сервера находит UUID интерфейса в таблице реестра интерфейсов.

  5. Используя UUID объекта uuidC из дескриптора привязки, библиотека времени выполнения сервера находит объект UUID в таблице реестра объектов и обнаруживает, что он сопоставляется с типом uuid7.

  6. Чтобы найти тип диспетчера, библиотека времени выполнения сервера объединяет интерфейсы UUID, uuid2 и uuid7 в таблице реестра интерфейсов. Это разрешается в epv3, который содержит подпрограмму диспетчера сервера, выполняемую для удаленного вызова процедуры.

Подпрограммы в epv2 никогда не будут выполняться, так как сервер не вызвал подпрограмму RpcObjectSetType для добавления объектов с типом UUID uuid4 в таблицу реестра объектов.

Удаленный вызов процедуры с интерфейсом UUID uuid2 и объектом UUID uuidF возвращается вызывающей объекту с кодом состояния RPC_S_UNKNOWN_MGR_TYPE, так как сервер не вызывал RpcServerRegisterIf,RpcServerRegisterIfEx или RpcServerRegisterIf2 для регистрации интерфейса с типом диспетчера uuid8.

Возвращаемые значения

Эта функция возвращает одно из следующих значений.

Значение Значение
RPC_S_OK Успешное завершение
RPC_S_TYPE_ALREADY_REGISTERED Тип UUID уже зарегистрирован

 

Предоставление собственной функции запроса объекта

Рассмотрим сервер, который управляет тысячами объектов различных типов. При запуске сервера серверное приложение будет вынуждено вызывать функцию RpcObjectSetType для каждого из объектов, даже если клиенты могут ссылаться только на некоторые из них (или потребуется много времени, чтобы ссылаться на них). Эти тысячи объектов, скорее всего, находятся на диске, поэтому получение их типов займет много времени. Кроме того, внутренняя таблица, которая сопоставляет UUID объекта с типом UUID диспетчера, фактически дублирует сопоставление, поддерживаемого с самими объектами.

Для удобства набор функций RPC включает функцию RpcObjectSetInqFn. С помощью этой функции вы предоставляете собственную функцию запроса объектов.

Например, можно предоставить собственную функцию запроса объектов при сопоставлении объектов 100–199 с числом 1, 200–299 с числом 2 и т. д. Функция запроса объектов также может быть расширена на распределенную файловую систему, где серверное приложение не имеет списка всех доступных файлов (UUID объектов) или когда файлы имен UUID объектов в файловой системе и не требуется предварительно загружать все сопоставления между UUID объекта и типа UUID.

RpcBindingFromStringBinding

RpcBindingSetObject

RpcNsBindingExport

RpcNsBindingImportBegin

RpcNsBindingLookupBegin

RpcObjectSetType

RpcServerRegisterIf

RpcServerRegisterIf2

RpcServerRegisterIfEx

RpcServerUnregisterIf

RpcServerUnregisterIfEx