Eccoci alla quarte parte della serie di post dedicata al mondo COM: in questo post parleremo della vTable, e dei vari metodi cui accedere ai metodi esposti dai COM Component.
Qui i post precedenti
Componenti COM: Introduzione - parte 1 di 6
Componenti COM: Le interfacce - parte 2 di 6
Componenti COM: Interface Definition Language .....anche chiamato IDL - parte 3 di 6
Continueremo a utilizzare, per esporre alcuni dettagli, l'esempio esplicitato nei post precedenti e relativo alla calcolatrice e del COM Server dll che implementa la logica di funzionamento della stessa, esponendo i metodi Init e Somma.
Oss.: Tutto quanto detto varrà sostanzialmente anche se si intende utilizzare un COM Server di tipo exe.
vTable
Come visto nei post precedenti un COM server è in grado di ospitare, al suo interno, uno o più componenti COM, e la sua struttura interna segue una precisa specifica binaria che indica, tra l'altro, come devono essere poste le informazioni all'interno del relativo file (dll o exe che sia).
Questa specifica prevede anche la presenza all'interno del file compilato di una tabella chiamata vTable.
Il contenuto della vTable é creata partendo dalle interfacce di tutti i componenti COM ivi contenuti, e rappresenta l'associazione tra il nome del metodo pubblico e il codice binario che lo implementa e contenuto all'interno del file che repparesenta il COM Server.
In altri termini ogni riga contenuta nella vTable rappresente un metodo pubblico dell'intefaccia di un COM Component, e conterrà un valore che permetterà di accedere, all'interno del file COM Server stesso, all'ubicazione ove è presente il codice binario che implementa tale metodo.
Esempio (esemplificato) di struttura di vTable
Nome Metodo | Lista Parametri | Indirizzo Metodo all'interno del file |
Init | aParametro01, aParametro02 | Indirizzo codice binario metodo Init |
Somma | NN | Indirizzo codice binario metodo Somma |
Nel vecchio e glorioso Visual Basic 6 questa tabella viene creata in fase di compilazione del COM Server, e usualmente avviene tutto in modo silente e senza che il programmatore ne abbia alcun cenno: per altri linguaggi di programmazione le cose possono essere diverse.
Early binding vs Late binding
Quando si vuole usare un componente COM è possibile utilizzarne le funzionaltià, tramite i relativi metodi pubblici, usando due metodi: early binding e late binding
Ognuno di questi ha le sue peculiarità e caratteristiche distintive.
Utilizzo di un componente COM in Early Bindining
Proseguendo il nostro esempio di realizzazione di una calcolatrice creiamo un nuovo progetto EXE, che dovrà consumare I servizi esposti dall'oggetto COM definito in precedenza, e che sarà delegato a presentare la UI all'utilizzatore. Per tale motivo tale progetto sarà costituito da solo una form.
Con l'early binding per utilizzare i metodi esposti dal componente COM creato nei precedenti post all'interno del nuovo progetto occorrerà innanzitutto riferire la dll, usando Project → Reference.
Fatto questo sarà possibile usare i metodi usando il codice nel seguito.
Dim objInfPressapochista as InfPressapochista
set objInfPressapochista = new InfPressapochista
objInfPressapochista.Init(4,5)
Questo metodo di utilizzo di un componente COM prende il nome di early binding.
Quello che avverrà in fase di compilazione è che per ogni metodo consumato del COM Component verrà memorizzato l'offset rispetto all'inizio della vtable che identifica il metodo stesso.
In pratica il codice consumatore che usa la libreria memorizzerà che alla posizione 0 della vtable vi è indicato l'indirizzo all'interno del file dll da cui parte il codice che implementa il metodo Init, e così per i metodi successivi, e quindi userà tale offset per richiamare tutti i metodi.
Quando il codice consumatore dovrà richiamare, ad esempio, il metodo Init posto nel COM Server accederà alla vTable contenuta nella relativa dll usando l'offset memorizzato in precedenza per accedere alla corretta rige che rappresenta in metodo, e da qui preleverà il puntatore all'indirizzo dove è presente il codice binario relativo all'interno del file dll COM Server.
Ottenuto questo valore il codice binario posto a quell'indirizzo sarà posto in esecuzione.
Questo "indirezione" sembra una complicazione: perchè non memorizzare direttamete l'indirizzo all'interno del file dll ove è posto il codice che implementa il metodo ?
La risposta è semplice: nel caso si modifichi il COM Server l'indirizzo memorizzato che punta al metodo potrebbe non essere più valido. Meglio memorizzare il punto ove viene indicato l'indirizzo che memorizzare l'indirizzo stesso !
Ora ipotizziamo che si voglia modificare il codice del COM Server, magari aggiungendo dei metodi e/o modificando quelli esistenti.Nel caso in esempio si supponga che si voglia aggiungere un nuovo metodo per eseguire la sottrazione.
Option Explicit Private Param01 As Integer Private Param02 As Integer
Public Sub Init(aParametro01 As Integer, aParametro02 As Integer)
Param01 = aParametro01 Param02 = aParametro02 End Sub Public Function Somma() As Integer
Somma = Param01 + Param02
End Function
Public Function Sottrazione() As Integer
Sottrazione = Param01 - Param02
End Function
Una volta terminata questa modifica, e ricompilata la dll in modalità compatile, il codice consumatore (cioè nel nostro caso il progetto che contiene la UI form), senza alcun intervento, continuerà a funzionare a patto che la nuova vTable generata riporti alla stessa posizione gli stessi metodi di prima: gli eventuali nuovi metodi saranno posti in righe successive lasciando le righe dei metodi preesistenti alla medesima posizione in modo tale che l'offest memorizzato precedentemente dal codice consumatore continui a rimanere valido.
Detto in altro modo un COM server ricompilato in "modalità compatibile" (binary compatibility) con la precedente versione avrà la vTable dotata della stessa struttura precedente, e con gli eventuali nuovi metodi aggiunti nel fondo.
Eventualmente nella nuova vTable cambieranno anche i puntatori di ogni riga alla nuova collocazione di dove si trova il codice del metodo relativo (poichè magari il codice che implementa qualche metodo è cambiato), ma mantenendo sempre la stessa posizione all'interno della tabella.
Affinchè in fase di compilazione sia possibile ricostruire la stessa vTable compatibile occorre però che siano rispettate almeno le due specifiche nel seguito.
1) Non deve essere cancellato alcun metodo rispetto alla versione precedente
2) Non deve essere cambiato alcun argomento dei metodi esistenti
Oss.: Mentre la regola 1 è intuitiva, la regola 2 non lo è. Non mi addentrerò nelle motivazioni, poichè per comprenderle occorrerebbe introdurre altre specificità della vTable che appensantirebbero ulteriormente la trattazione. Ovviamente per esporre il funzionamento del COM ho introdotte alcune esemplificazioni espositive.
Se queste regole non sono rispettate il compialtore emetterà un errore bloccante, e non sarà possibile procedere alla ricompilazione della dll in binary compatibility.
Oss.: In VB6 è possibile speficare se si vuole compilare la nuova versione di una dll in modalità binary compatiblity o meno: nel caso si scelga questa opzione come intuibile sarà richiesto di specificare il percorso di un COM Server che dovrà servire da modello per la nuova vTable.
E' necessario dire che è possibile ricompilare il Com Server anche in modalità NON binary compatibility (No Compatibility) in tale caso il compilatore se ne impiperà di eventuali vTable preesistenti e la ricostruirà completamente.
Come intuibile in tale caso non verrà richiesto di specificare il percorso di una dll per la quale verrà generata una vTable compatibile.
Ovviamente in tale caso ogni codice che utilizza detta dll smetterà di funzionare. Per ripristinarne eventualmente il funzionamento occorrerà riaprire il progetto del codice consumatore, rieseguire il riferimento alla dll e ricompilare tutto.
VB6 offre anche una modalità di compilazione in Project Compatibilty, di cui non intendo specificare alcunchè, se non che è una modalità di ricompilazione intermedia tra le due viste sopra e che è utile in fase di debug e scrittura codice.
Per quanto visto progettare un COM Component prevede di stabilire in modo abbastanza preciso la relativa interfaccia, e quindi i suoi metodi pubblici, perchè poi è impossibile variarli pena la perdita di compatibilità e la necessità mettere mano ai progetti che consumano i suoi metodi in ealy binding.
Quindi in sostanza quando si usa early binding è possibile nel codice del componente COM aggiungere nuovi metodi o modificare il codice di quelli esistenti !
Utilizzo di un componente COM in Late Binding.
Ecco un modo alternativo di riferire la libreria che non soffre dei problemi di compatilità visti prima, ma introduce altre criticità cui porre attenzione.
Dim objInfPressapochista as object set objInfPressapochista = CreateObject("ComServer.InfPressapochista") objInfPressapochista.Init(4,5)
In questo caso si utilizza il comando CreateObject, che accetta come argomento una stringa che rappresenta il PROGOd.
Il PROGId è costituito come nel seguito.
[ProjectName].[ClassModuleName]
Nel caso in analisi l'accesso ai metodi esposti dal COM Component avviene in modo totalmente diverso: a tal scopo verrà utilizzata un'altra interfaccia che il compilatore di una libreria COM crea in fase di compilazione, IDispatch.
Anche in questo caso il tutto avviene in modo silente e nascosto se si usa, ad esempio, VB6, che si occupa, in fase di compilazione, di associare ad ogni interfaccia anche quest'altra interfaccia, nonchè aggiungere il codice necessario per implementarne i metodi.
In sostanza grazie a IDispatch è possibile a runtime rintracciare la dll e la sua interfaccia di default, e quindi interrogarla ed utilizzarla.
In tal caso tutto quanto visto relativo alla vTable e alla sua compatilità in caso di modifica decade: infatti se si ricompila la dll non è necessaria alcuna compatibilità della vTable con la versione precedente della stessa affinchè codice consumatore che usi il COM tramite late binding possa continuare a utilizzarla senza problemi. Questo perchè l'accesso ai metodi del componente COM avviene in modo totalmente diverso e senza l'utilizzo di offset: a runtime viene caricata la dll e la relativa vTable, che viene acceduta non usando l'offeste ma ricercando la relativa riga con il nome metodo.
Soffrirebbero di problemi solo i consumatori che accedano al COM tramite early binding.
Al prossimo post, dove evidenzierò il perchè esistono due metodi di accesso ai componenti COM, e pregi e difetti di ciascuno.