Condividi tramite


Modifica delle interfacce in modo compatibile con le versioni precedenti

I metodi illustrati nella teoria del controllo delle versioni per RPC e COM possono essere inaccettabili per molti motivi. La modifica di una versione dell'interfaccia in base alle regole richiede essenzialmente che i nuovi client non comunichino con i server precedenti. Questo è spesso impossibile con il software commerciale distribuito nel campo. In alcuni casi, Windows ha introdotto modifiche all'interfaccia assenti di GUID o versioni modificate. Si tratta di un risultato di nuovi client che devono comunicare con i server legacy e perché la soluzione che un nuovo client supportava sia le interfacce precedenti che le nuove erano considerate indesiderate.

Procedura consigliata

Questi sono i metodi ragionevoli di risolvere il problema di incompatibilità di rete quando il GUID e la versione dell'interfaccia non possono essere modificati.

  1. Fare in modo che l'applicazione sia a conoscenza delle funzionalità dell'altro lato.

    Il client e il server hanno un protocollo che consente a ogni client (o almeno al nuovo client) di stabilire l'identità del partner. In genere è sufficiente fare in modo che il nuovo client sia a conoscenza delle funzionalità supportate dai server vecchi e nuovi. Questa operazione può essere eseguita facilmente quando un'applicazione si connette a un contesto di connessione e può essere supportata tramite un tipo XxxGetInfo di chiamata di funzione eseguita dal client prima di eseguire qualsiasi operazione RPC. Quando un'applicazione gestisce le funzionalità in base al rilascio per server, una chiamata con incompatibilità al server/client precedente non può mai verificarsi, poiché l'applicazione controlla le chiamate emesse a quale server. La linea finale è che l'applicazione è proattiva per impedire che si verifichi una mancata corrispondenza. Questa operazione può essere eseguita insieme alla seconda pratica.

  2. Introdurre una nuova API remota.

    Un nuovo metodo remoto non è in conflitto con i metodi esistenti se viene aggiunto alla fine dell'interfaccia. I vecchi client possono chiamare nuovi server perché hanno sempre. Il nuovo client può chiamare il nuovo metodo senza conoscere l'identità del server, purché osservi gli errori provenienti dal server chiamato. Il tempo di esecuzione RPC controlla sempre il numero di metodo per ogni interfaccia prima di un dispatch per assicurarsi che il metodo si trova all'interno di una tabella v-table appropriata. Per un metodo sconosciuto a un server, il runtime RPC genera l'eccezione RPC_S_PROCNUM_OUT_OF_RANGE. Questa eccezione viene generata solo in questa situazione specifica. Pertanto, un nuovo client può watch per l'eccezione come segno che la chiamata è andata a un server precedente e può modificarne il comportamento normalmente.

  3. Introdurre nuovi parametri o nuovi tipi di dati solo nei nuovi metodi.

    Un motivo per introdurre un nuovo metodo consiste nell'evitare l'incompatibilità dei dati. Se un nuovo tipo di dati viene introdotto o semplicemente modificato, in linea di principio deve essere usato solo in un nuovo metodo (o metodi). Vedere Esempi di modifiche incompatibili per esempi di modifiche del tipo di dati incompatibili. L'unica eccezione rilevante per questa regola è descritta nell'elemento 4.

  4. Eseguire il mapping di nuovi parametri o nuovi tipi di dati tramite un wrapper.

    Questa soluzione si applica quando un nuovo parametro o tipo di dati deve essere esposto a un utente, ma in realtà non deve essere remoto separatamente o può essere mappato ai tipi di dati o ai parametri precedenti. Ad esempio, molte API di sistema girano ed eseguono una chiamata remota. Possono eseguire o meno un mapping dai tipi di dati noti dell'utente ai tipi di dati effettivamente usati nella chiamata RPC sottostante. È quindi consigliabile esaminare sempre se la modifica nell'interfaccia utente deve propagarsi come modifica a un'interfaccia remota.

    Una situazione simile può verificarsi quando l'utente chiama direttamente un'API remota, ma è possibile introdurre un wrapper per eseguire un nuovo mapping dei tipi o altre azioni aggiuntive che sono diventate necessarie. Interface Definition Language (IDL) offre diversi modi per facilitare il mapping, vale a dire [call_as], [transmit_as], e [wire_marshal]. L'attributo [call_as] introduce un wrapper di funzione nel client e nel server. Entrambi vengono posizionati tra il codice utente e il gestore di marshalling. Gli altri attributi gestiscono il mapping diretto dei tipi. Per i problemi di estensione, [call_as] è l'uso più frequente ed è più semplice da comprendere e manipolare senza insidie.

  5. Modificare i tipi di dati tramite un'unione senza valori predefiniti.

    La modifica di un attributo o di un tipo di dati comporta in genere l'incompatibilità dei cavi. Per esempi, vedere Esempi di modifiche incompatibili . Tuttavia, nel caso di un'unione senza una clausola predefinita, l'incompatibilità può essere gestita in modo simile al caso di una routine non compreso nell'intervallo, come descritto in precedenza. Questo schema è facilmente applicabile ai tipi XxxINFO più diffusi che usano unioni.

    Ad esempio, una chiamata simile alla seguente

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

    potrebbe restituire informazioni al livello 1, 2 o 3, con XxxINFO come unione con tre rami: 1, 2 e 3.

  6. Usare l'attributo [range] per specificare l'intervallo.

    È possibile specificare l'attributo [range] in un tipo di scala semplice senza compromettere la compatibilità con le versioni precedenti. Questo attributo non influisce sul formato di collegamento, ma durante il controllo RPC di unmarshalling controlla il valore in transito per confermare che si trova all'interno dell'intervallo specificato nel file con estensione idl. In caso contrario, viene generata un'eccezione RPC_X_INVALID_BOUND. Ciò è particolarmente utile se il server conosce le dimensioni massime di una matrice di dimensioni.

    Ad esempio:

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

Il comportamento RPC quando il livello indicato è 4 e il braccio manca dipende dalla definizione dell'unione. Per un'unione con la clausola predefinita definita, RPC trasmette un tipo indicato nella clausola predefinita per qualsiasi valore diverso dalle etichette arm note (in questo caso, diverso da 1, 2 o 3). Per un'unione senza valori predefiniti, l'unmarshaler genera un'eccezione perché per definizione non esiste alcun valore predefinito per il fallback. L'eccezione è RPC_S_INVALID_TAG.

Anche in questo caso, un nuovo client può modificarne il comportamento individuando che ha chiamato un server precedente.

Ciò che segue da queste procedure consigliate è che se un tipo di dati remotabile deve essere progettato che può essere esteso in futuro, usare un'unione predefinita nel file IDL. Data una scelta, un'unione incapsulata è leggermente più pulita.

A causa di strani problemi di rappresentazione interna del protocollo di filo NDR64, la raccomandazione per l'aggiunta di armi fornite in precedenza in questa sezione deve essere qualificata come segue: il nuovo braccio da aggiungere non può modificare l'allineamento dell'unione, e in particolare, l'allineamento più grande delle braccia non deve cambiare. Questo non è in genere un problema, come puntatore in un braccio forza l'allineamento a 8. Una progettazione in cui ogni braccio è un puntatore a un tipo di braccio è un modo pulito per soddisfare il requisito.