Compartir a través de


Cambio de interfaces de una manera compatible con versiones anteriores

Los métodos explicados en La teoría de versiones para RPC y COM pueden ser inaceptables por muchas razones. El cambio de una versión de interfaz según las reglas requiere básicamente que los nuevos clientes no se comuniquen con servidores antiguos. Esto suele ser imposible con el software comercial implementado en el campo. A veces, Windows ha introducido cambios de interfaz ausentes de guid modificados o versiones. Esto fue el resultado de los nuevos clientes que necesitan comunicarse con los servidores heredados y porque la solución que un nuevo cliente admitiría las interfaces antiguas y nuevas se considera no deseada.

Procedimiento recomendado

Estos son los métodos razonables para solucionar el problema de incompatibilidad de conexión cuando no se puede cambiar el GUID de la interfaz y la versión.

  1. Haga que la aplicación tenga en cuenta las funcionalidades del otro lado.

    El cliente y el servidor tienen un protocolo que permite a cada uno (o al menos el nuevo cliente) establecer la identidad del asociado. Normalmente, es suficiente que el nuevo cliente tenga en cuenta las características compatibles con los servidores antiguos y nuevos. Esto se puede hacer fácilmente cuando una aplicación se mantiene en un contexto de conexión y se admite a través de un tipo XxxGetInfo de la llamada de función ejecutada por el cliente antes de realizar cualquier operación RPC. Cuando una aplicación administra las características según la versión por servidor, nunca se puede producir una llamada con incompatibilidad con el servidor o cliente antiguo, ya que la aplicación controla qué llamadas se emiten al servidor. La conclusión es que la aplicación es proactiva para evitar que se produzca un error de coincidencia. Esto puede realizarse junto con la segunda práctica.

  2. Presenta una nueva API remota.

    Un nuevo método remoto no entra en conflicto con los métodos existentes si se agrega al final de la interfaz. Los clientes antiguos pueden llamar a nuevos servidores como siempre lo tienen. El nuevo cliente puede llamar al nuevo método sin conocer la identidad del servidor, siempre que busque los errores procedentes del servidor al que se llama. El tiempo de ejecución de RPC siempre comprueba el número de método de cada interfaz antes de un envío para asegurarse de que el método está dentro de una tabla virtual adecuada. Para un método desconocido para un servidor, el tiempo de ejecución de RPC genera la excepción RPC_S_PROCNUM_OUT_OF_RANGE. Esta excepción solo se genera en esta situación en particular. Por lo tanto, un nuevo cliente puede watch para la excepción como un signo de que la llamada fue a un servidor antiguo y puede modificar su comportamiento correctamente.

  3. Introduzca nuevos parámetros o nuevos tipos de datos solo en los nuevos métodos.

    Una razón para introducir un nuevo método es evitar la incompatibilidad de datos. Si se introduce o simplemente se modifica un nuevo tipo de datos, en principio solo se debe usar en un nuevo método (o métodos). Consulte Ejemplos de cambios incompatibles para ver ejemplos de cambios de tipo de datos incompatibles. La única excepción notable a esta regla se describe en el elemento cuatro.

  4. Asigne nuevos parámetros o nuevos tipos de datos a través de un contenedor.

    Esta solución se aplica cuando se debe exponer un nuevo parámetro o tipo de datos a un usuario, pero en realidad no tiene que estar remoto por separado o se puede asignar a los tipos de datos o parámetros antiguos. Por ejemplo, muchas API del sistema giran y ejecutan una llamada remota. Es posible que realicen o no algún tipo de asignación de los tipos de datos conocidos del usuario a los tipos de datos que realmente se usan en la llamada RPC subyacente. Por lo tanto, siempre merece la pena examinar si el cambio en la interfaz de usuario debe propagarse como un cambio en una interfaz remota.

    Una situación similar puede ocurrir cuando el usuario llama directamente a una API remota, pero se podría introducir un contenedor para realizar una asignación de tipos nueva o algunas otras acciones adicionales que se han vuelto necesarias. El lenguaje de definición de interfaz (IDL) tiene varias maneras de facilitar dicha reasignación, es decir, [call_as], [transmit_as] y [wire_marshal]. El atributo [call_as] introduce un contenedor de funciones en el cliente y el servidor. Ambos se colocan entre el código de usuario y el serializador. Los demás atributos tratan la asignación directa de tipos. En el caso de los problemas de extensión, [call_as] es el más usado y es más fácil de entender y manipular sin problemas.

  5. Modifique los tipos de datos a través de una unión sin valores predeterminados.

    El cambio de un atributo o tipo de datos suele dar lugar a incompatibilidad de conexión. Consulte Ejemplos de cambios incompatibles para obtener ejemplos. Sin embargo, en el caso de una unión sin una cláusula predeterminada, la incompatibilidad se puede administrar de forma similar al caso de un procedimiento fuera del intervalo, como se ha descrito anteriormente. Este esquema es fácilmente aplicable a los tipos XxxINFO populares que usan uniones.

    Por ejemplo, una llamada como esta

    XxxGetInfo( [in] level, [out] XxxINFO  * pInfo );
    

    podría devolver información en el nivel 1, 2 o 3, y XxxINFO es una unión con tres ramas: 1, 2 y 3.

  6. Use el atributo [range] para especificar el rango.

    Puede especificar el atributo [range] en un tipo de escala simple sin interrumpir la compatibilidad con versiones anteriores. Este atributo no afecta al formato de conexión, pero durante unmarshalling RPC comprueba el valor de la conexión para confirmar que está dentro del intervalo especificado en el archivo .idl. Si no es así, se produce una excepción de RPC_X_INVALID_BOUND. Esto es especialmente útil si el servidor conoce el tamaño máximo de una matriz de tamaño.

    Por ejemplo:

    HRESULT Method1( [in, range(0,100)] ULONG m, [size_is(m)] ULONG *plong); 
    

El comportamiento rpc cuando el nivel indicado es 4 y falta el brazo, depende de la definición de la unión. Para una unión con la cláusula predeterminada definida, RPC transmite un tipo indicado en la cláusula predeterminada para cualquier cosa diferente de las etiquetas de brazo conocidas (en este caso, cualquier cosa distinta de 1, 2 o 3). En el caso de una unión sin valor predeterminado, el unmarshaler genera una excepción porque por definición no hay ningún valor predeterminado al que recurrir. La excepción es RPC_S_INVALID_TAG.

De nuevo, un nuevo cliente puede ajustar su comportamiento al detectar que llamó a un servidor antiguo.

Lo siguiente de estos procedimientos recomendados es que si se debe diseñar un tipo de datos remoto que se puede extender en el futuro, use una unión sin valores predeterminados en el archivo IDL. Dada una opción, una unión encapsulada es ligeramente más limpia.

Debido a las peculiaridades de la representación interna del protocolo de alambre NDR64, la recomendación para agregar brazos proporcionados anteriormente en esta sección debe calificarse como sigue: El nuevo brazo que se va a agregar no puede cambiar la alineación de la unión y, en particular, la mayor alineación de los brazos no debe cambiar. Normalmente, esto no es un problema, ya que un puntero de un brazo fuerza la alineación a 8. Un diseño donde cada brazo es un puntero a un tipo de brazo es una manera limpia de satisfacer el requisito.