Изменение интерфейсов с обратной совместимостью
Методы, описанные в статье Теория управления версиями для RPC и COM , могут быть неприемлемыми по многим причинам. Изменение версии интерфейса в соответствии с правилами по сути требует, чтобы новые клиенты не взаимодействовали со старыми серверами. Это часто невозможно при использовании коммерческого программного обеспечения, развернутого на местах. Иногда в Windows вводятся изменения интерфейса без измененных идентификаторов GUID или версий. Это было вызвано тем, что новые клиенты должны взаимодействовать с устаревшими серверами, а решение, которое новый клиент будет поддерживать как старый, так и новый интерфейсы, было признано нежелательным.
Рекомендации
Это разумные методы обхода проблемы несовместимости провода, когда невозможно изменить GUID и версию интерфейса.
Убедитесь, что приложение должно знать о возможностях другой стороны.
У клиента и сервера есть протокол, позволяющий каждому (или по крайней мере новому клиенту) устанавливать удостоверение партнера. Как правило, этого достаточно, чтобы новый клиент знал о функциях, поддерживаемых старыми и новыми серверами. Это можно легко сделать, если приложение сохраняет контекст подключения и поддерживается с помощью типа XxxGetInfo вызова функции, выполняемого клиентом перед выполнением каких-либо операций RPC. Когда приложение управляет функциями на основе выпуска для каждого сервера, вызов с несовместимостью со старым сервером или клиентом никогда не может произойти, так как приложение контролирует, какие вызовы выдаются на том или ином сервере. Суть в том, что приложение активно предотвращает несоответствие. Это может быть выполнено в сочетании со второй практикой.
Введите новый удаленный API.
Новый удаленный метод не сталкивается с существующими методами, если он добавляется в самом конце интерфейса. Старые клиенты могут вызывать новые серверы, как всегда. Новый клиент может вызывать новый метод, не зная удостоверения сервера, при условии, что он следит за ошибками, поступающими от вызываемого сервера. Время выполнения RPC всегда проверяет номер метода для каждого интерфейса перед отправкой, чтобы убедиться, что метод находится в соответствующей виртуальной таблице. Для метода, неизвестного серверу, во время выполнения RPC возникает исключение RPC_S_PROCNUM_OUT_OF_RANGE. Это исключение возникает только в данной конкретной ситуации. Таким образом, новый клиент может watch исключение в качестве признака того, что вызов перешел на старый сервер и может корректно изменять его поведение.
Ввод новых параметров или новых типов данных только в новых методах.
Одна из причин введения нового метода заключается в том, чтобы избежать несовместимости данных. Если новый тип данных вводится или просто изменяется, в принципе его следует использовать только в новом методе (или методах). Примеры несовместимых изменений см. в разделе Примеры несовместимых изменений типов данных. Единственное заметное исключение из этого правила описано в четвертом пункте.
Сопоставляйте новые параметры или новые типы данных с помощью оболочки.
Это решение применяется, когда новый параметр или тип данных должен быть предоставлен пользователю, но на самом деле его не нужно удалять отдельно или его можно сопоставить со старыми типами данных или параметрами. Например, многие системные API обращаются и выполняют удаленный вызов. Они могут выполнять или не выполнять какое-либо сопоставление известных пользователем типов данных с типами данных, фактически используемыми в базовом вызове RPC. Поэтому всегда стоит изучить, должно ли изменение пользовательского интерфейса распространяться как изменение на удаленный интерфейс.
Аналогичная ситуация может произойти, когда пользователь вызывает удаленный API напрямую, но может быть введена оболочка для выполнения нового сопоставления типов или других дополнительных действий, которые стали необходимыми. Язык определения интерфейса (IDL) имеет несколько способов упрощения такого переопределения, а именно [call_as], [transmit_as] и [wire_marshal]. Атрибут [call_as] вводит оболочку функции на клиенте и сервере. Оба находятся между пользовательским кодом и маршалером. Другие атрибуты имеют дело с прямым сопоставлением типов. Для проблем с расширением наиболее часто используется [call_as], и его проще всего понимать и обрабатывать без ошибок.
Изменение типов данных с помощью объединения по умолчанию.
Изменение атрибута или типа данных обычно приводит к несовместимости проводов. Примеры см. в разделе Примеры несовместимых изменений . Однако в случае объединения без предложения по умолчанию несовместимостью можно управлять аналогично процедуре вне диапазона, как описано выше. Эта схема легко применима к популярным типам XxxINFO , которые используют объединения.
Например, вызов, подобный этому
XxxGetInfo( [in] level, [out] XxxINFO * pInfo );
может возвращать сведения на уровне 1, 2 или 3, при этом XxxINFO является объединением с тремя ветвями: 1, 2 и 3.
Используйте атрибут [range], чтобы указать диапазон.
Атрибут [range] можно указать в простом типе масштабирования без нарушения обратной совместимости. Этот атрибут не влияет на формат провода, но во время отмены проверки RPC проверяет значение в проводе, чтобы убедиться, что оно находится в диапазоне, указанном в IDL-файле. В противном случае создается RPC_X_INVALID_BOUND исключение. Это особенно полезно, если серверу известен максимальный размер массива.
Пример:
HRESULT Method1( [in, range(0,100)] ULONG m, [size_is(m)] ULONG *plong);
Поведение RPC, когда указанный уровень равен 4, а рука отсутствует, зависит от определения объединения. Для объединения с определенным предложением по умолчанию RPC передает тип, указанный в предложении по умолчанию, для всех элементов, отличных от известных меток arm (в данном случае все, кроме 1, 2 или 3). Для объединения без по умолчанию unmarshaler вызывает исключение, так как по умолчанию не существует по умолчанию, к которым можно вернуться. Исключением является RPC_S_INVALID_TAG.
Опять же, новый клиент может изменить свое поведение, обнаружив, что он вызвал старый сервер.
Из этих рекомендаций следует, что если необходимо спроектировать тип данных с возможностью изменения, который можно расширить в будущем, используйте в IDL-файле объединение без использования по умолчанию. Учитывая выбор, инкапсулированный союз немного чище.
Из-за причуд внутреннего представления протокола провода NDR64 рекомендация по добавлению оружия, представленная ранее в этом разделе, должна быть квалифицирована следующим образом: Добавляемый новый рука не может изменить выравнивание союза, и, в частности, наибольшее выравнивание оружия не должно меняться. Обычно это не проблема, так как указатель в руке приводит к выравниванию на 8. Конструкция, в которой каждая рука является указателем на тип руки, является одним из чистых способов удовлетворения требования.