Poco tempo fa ho dovuto mettere mano a del codice scritto in VB6: un sistema legacy “in fase di dismissione” da diversi anni, ma ancora perfettamente funzionante nonchè ampiamente utilizzato.

Il problema da risolvere riguardava anche il sistemare alcune dipendenze (dll COM): per tale motivo ho dovuto riprendere in mano alcuni appunti che avevo scritto a suo tempo per districarmi nella materia.

Rileggendoli mi sono reso conto che non erano affatto male, e quindi ho deciso di sistemarli e condividerli con Voi.

Avvertimento iniziale

Questo articolo parla di cose veramente “vintage”, anche se ancora ampiamente supportate da tutti i moderni sistemi operativi e linguaggi di programmazione.

In tutto l'articolo userò spesso il tempo presente, anche più correttamente avrei dovuto usare ovunque il passato poichè il mondo COM/ActiveX fa oramai parte dell'archeologia informatica.

Parlerò anche di codice e IDE Visual Basic 6, anche in questo caso spesso usando il presente: anche qui sono ben conscio di parlare di un passato veramente …… tanto passato.

Ma... il blog è mio.... e a pensarci bene il tempo da usare per i post lo decido io......

Fine Avvertimento.

Inizio con il dire che esisteva un tempo in cui non era così facile scrivere delle librerie che, una volta compilate e distribuite, potevano essere utilizzate da altri pezzi di codice, questi ultimi magari scritti in un differente liguaggio.

 Intorno agli anni 90, per risolvere questo problema, è stata creata la tecnologia COM/ActiveX, che permette ancora oggi di implementare questa condivisione anche a fronte di linguaggi di programmazione e compilatori non omogenei.

L'unica condizione affinchè questa condivisione possa avvenire è che la libreria sia compilata con un formato compatibile con gli standard COM/ActiveX, e che sia “registrata” correttamente all'interno di diverse voci del registro di sistema: ovviamente anche l'ambiente del linguaggio consumatore (compilatore e IDE) deve essere in grado di comprendere tale standard.

La tecnologia successiva è legata all'avvento di .NET, che ha ulteriormente esemplificato il tutto superando di fatto COM/ActiveX: in particolare il formato utilizzato è diverso e il registro di sistema può anche non essere più coinvolto, a patto che in fase di deploy le dll delle librerie stiano al posto giusto (normalmente nella stesso percorso dell'eseguibile).

Comunque alla data attuale moltissimi software si affidano ancora a COM/ActiveX, che è pienamente supportato da tutti i sistemi operativi moderni.

COM/ActiveX

Prima di adentrarci nei dettagli occorre parlare subito della differenza che esiste tra il termine COM e di ActiveX, che spesso sono fonte di confusione poichè perennemente utilizzati insieme.

ActiveX nasce a metà degli anni 90 come un'estensione allo standard COM (che è precedente), e che permetteva l'interazione di questo tipo di oggetti con le pagine web.

Successivamente poi con il passare del tempo i due termini sono diventati assolutamente intercambiabili, e quindi oggi in buona sostanza è possibile esemplificare dicendo che un oggetto COM è un oggetto ActiveX, e viceversa !

COM: di che si tratta ?

Il COM è una specifica che permette di creare dei componenti che possono essere utilizzati da altri pezzi di codice (librerie), quindi permettendo l'interazione tra codici diffenti.

Ovviamente occorre che sia il compilatore per il linguaggio con cui è stata scritta la libreria, sia l'ambiente con cui si sta scrivendo il codice che deve consumare la libreria stessa, siano compatibili con la tecnologia COM.

Il risultato di una libreria COM-compatibile è chiamato genericamente oggetto COM (più avanti dettaglieremo meglio i vari termini coinvolti), e usualmente contiene al suo interno una o più classi che possono essere consumate da codice esterno.

Un oggetto COM si presenta come un file con estensione dll, o anche exe, e che al suo interno presenta una struttura binaria compatibile con tale formato (cioè deve seguire la “binary specification” relativa agli oggetti COM).

Proprio grazie al fatto che la struttura interna dell'oggetto COM segue delle precise specifiche si otterrà anche la “language neutrality”: cioè una libreria oggetto COM può essere scritta con qualsivoglia linguaggio, l'importante è che alla fine il compilatore generi una dll on un exe con il formato binario corretto compatibile.

In tal modo essa sarà utilizzabile (consumabile) anche da linguaggi non omogenei.

Questo ha portato a grosse esemplificazioni del processo di scrittura di software: da allora grossi progetti potevano agilmente essere scomposti in parti più piccole e ognuna di queste poteva essere implementata con un liguaggio di programmazione diverso, e adatto a risolvere solo questa parte.

Grazie al COM tutte le varie parti costitutive essere in grado di interagire correttamente.

Permettetemi un esempio pratico e reale. Si immagini la necessità di scrivere un software di contabilità aziendale usando solo gli strumenti in auge negli anni '90.

Un software di questo genere presenta parti per le quali le perfomance sono importantissime, altre parti per le quali, invece, le perfomance non risultano essere un fattore indispensabile. Per esempio l'accesso alla base dati risulta essere sicuramente un pezzo per il quale le perfomance di esecuzione sono molto importanti. Invece per esempio la logica che sottende l'inserimento di una fattura fiscale sicuramente non risulta essere critica sotto il punto di vista delle perfomance, mentre invece il codice che lo implementa deve permettere una facile manutentibilità al fine di permettere di eseguire i complessi algoritmi fiscali coinvolti.

Quindi all'epoca grazie a COM si poteva scrivere la parte di accesso dati in C++, mentre le maschere di inserimento fattura e logica di funzionamento potevano essere scritte in Visual Basic.

E' noto che il C++ è molto perfomante, spesso a scapito di chiarezza del codice: diversamente il VB6, che sicuramente pecca di perfomance di esecuzione ma presenta una chiarezza del codice difficilmente raggiungibile: da ciò ne deriva anche una migliore manutentibilità e implementazione degli algoritmi.

Anche oggi nel mondo .Net si possono trovare situazioni come quella sopra: infatti qui il C# sicuramente presenta caratteristiche di perfomance maggiori rispetto a VB.Net, mentre quest'ultimo presenta sempre chiarezza espositiva sicuramente maggiore.

Ma occorre comprendere che all'epoca in cui era stato introdotta la tecnologia COM l'interazione tra vari linguaggi era molto complessa, e comunque poneva serie limitazioni.

Quindi in buona sostanza COM ha rotto le barriere tra i vari linguaggi di programmazione grazie all'utilizzo di una tecnologia facile da implementare e utilizzare. ...quindi… perchè cambiare ??

Esistono ovviamente anche alcune grosse problematiche, che hanno reso necessario l'introduzione del mondo .Net: per esempio il mondo COM si basa pesantemente sull'utilizzo del registro del sistema operativo, e inoltre non era affatto facile implementare il versioning delle librerie. Infatti affinchè una libreria possa essere utilizzata occorre che sia correttamete denunciata in diversie voci del registro del sistema operativo.

Ripeto per meglio chiarire: come noto in .Net per usare una libreria compatibile è sufficiente che il codice consumatore conosca l'ubicazione fisica della dll stessa. Nel mondo COM occorre, invece, che la dll sia correttamente registrata nel registro di sistema operativo per poter essere utilizzata: solo allora il codice consumatore della stessa potrà utilizzrla.

Con il termine versioning si immagini la seguente situazione. Il codice A utilizza la libreria B, che appunto è una libreria COM: detto in altri termini A rappresenta il codice che consuma quanto offerto dalla libreria B. I produttori della libreria B a un certo punto si accorgono di alcuni bug, oppure implementanto nuove funzionalità (magari aggiungendo nuovi metodi) e quindi rilasciano una nuova versione della stessa: questa nuova versione va a sostituire i file relativi della vecchia versione.

Il codice A deve poter continuare a usare le funzionalità della libreria B, quindi anche a fronte di una nuova versione della stessa senza dover fare alcuna modifica o dover essere ricompilata.

Ovviamente affinchè tutti funzioni come descritto sopra la la nuova versione della libreria B deve essere rilasciata rispettando alcune limitazioni, che la rendono compatible con la versione precedente. Come suggerisce la logica occorre che i vecchi metodi pubblici presenti nella versione precedente (e quindi usati dal codice A) non siano eliminati nelle versioni successive: questo vedremo che non basta, ma è il punto di partenza !

Spero di ritrovarvi al prossimo post che parla di questo argomento.