Passiamo dunque ad esaminare le funzioni ("metodi") elencati
nel corpo di una interface
COM, così come
viene definita in C, usando la macro DECLARE_INTERFACE_
,
vista al capitolo precedente.
Ciascuna di queste funzioni è dichiarata con la macro STDMETHOD
(che garantisce l'uso di convenzione di chiamata standard, e il
ritorno di un HRESULT
come risultato), e ha come primo argomento
la macro THIS_
(THIS
, senza underline, per ragioni sintattiche, se
si tratta dell'unico argomento); infine, ogni funzione è etichettata
come PURE
, il che, in C, non ha effetto, ma serve per l'espansione
C++ (nella quale non servirebbero invece le macro THIS
e THIS_
,
che corrispondono al "primo implicito argomento" passato da ogni
chiamata a funzione "membro di un oggetto" in C++).
Notiamo, inoltre, fra gli argomenti di queste molte funzioni: una
enum
(di cui, per ragioni sintattiche, dobbiamo fornire una
dichiarazione fittizia), vari puntatori a
interface IQualcheNome
(la parola
interface
, grazie al preprocessore, si trasforma in struct
; e,
di una struct
che usiamo solo via puntatore, non siamo tenuti,
a norma delle regole del linguaggio C, a
dare una definizione), i tipi che già avevamo menzionato
(BSTR
, VARIANT
, SAFEARRAY
), e vari altri tipi definiti con le
classiche macro "alla Windows", alcuni dei quali sono piuttosto
chiari (UINT
, ad esempio, è "unsigned int"), altri meno (ad
esempio, VARIANT_BOOL
è uno "short" usato come booleano,
con 0==false e -1==true; OLECHAR
è
il tipo standard C wchar_t
,
che rappresenta un carattere di 16 bit a codice Unicode; ecc).
Notiamo che parecchie funzioni, per convenzione, hanno nomi
che iniziano con get_
o put_
, spesso "in coppia"
(e con parametri
di tipo X*
e X
rispettivamente);
questa è una tipica traccia
di "Automation", che, fra le sue convenzioni, ha quella che ciò
che, concettualmente, è un "accesso ad una proprietà di un
oggetto", avviene sempre attraverso la chiamata a una simile
coppia di funzioni (per "proprietà in sola lettura", esisterà il
get_
senza il relativo put_
)
-- una convenzione simile esiste in
Java (dove i prefissi usati sono get
,
senza underline, e set
,
idem, invece di get_
e put_
);
le recenti versioni di compilatori C++ della
Borland (C++Builder) e della Microsoft (Visual C++) permettono,
facoltativamente, di usare queste coppie di funzioni,
get_
e put_
,
con la sintassi che si userebbe per leggere o scrivere un "membro
dati pubblico" della classe, approccio comune anche al Visual Basic,
ma che in C++ costituisce una "estensione non-standard al
linguaggio", il cui uso non può quindi
essere consigliato (infatti, usando delle estensioni al
linguaggio, ci si impedisce il porting ad altri
compilatori standard, come qui, ad esempio, l'ottimo e
gratuito GCC).
Se "in qualche modo" avessimo ottenuto un puntatore ad una simile interfaccia, lo useremmo poi, in C, in questo modo...:
Si noti l'idioma indispensabile: da pSC
si trova con -> la
sua lpVtbl
; da quella, il metodo da chiamare; e lo stesso
pSC
va passato anche come primo argomento al metodo
stesso. In C++ (senza CINTERFACE
), diremmo invece:
lpVtbl
)
e il passaggio del this
(pSC
),
ma ottenendo esattamente lo stesso codice eseguibile.
Si noti, inoltre, l'importante test
SUCCEEDED(hr)
, che
garantisce che, se la get_Timeout
dà errore, non si
procede oltre, ma si segnala l'errore al chiamante; in
C++, si potrebbe, facoltativamente, scegliere di avere
un "vestito", sopra l'interfaccia usata, che trasformi gli
HRESULT
di fallimento in "exception" del linguaggio (VC++,
per default, usa quest'ottimo approccio se si adopera la
sua estensione non-standard #import
per accedere
al COM), con un minimo costo in termini di esecuzione
(ma solo in caso d'errore), e una grande semplificazione
di scrittura del codice (risparmiando i continui test di
if(SUCCEEDED(hr))
che altrimenti "ingolfano" il codice
COM scritto in C o C++). Un approccio semplice ed
efficace, in C, per ottenere una analoga semplificazione,
"senza costi", è una macro simile a...:
con la quale il corpo della funzione vista diventerebbe:
Naturalmente, la risultante "linearizzazione" del codice, in una funzione così piccola, si apprezza poco, ma, quando le chiamate COM in una singola funzione sono invece parecchie, e magari vengono allocate risorse che vanno poi rilasciate, eccetera, il vantaggio di chiarezza, leggibilità, e manutenibilità, è grande.
L'unico vero costo di questo approccio sta nel parecchio tempo che esso inevitabilmente porta a sprecare negli interminabili litigi con quei "soloni della programmazione strutturata" che, non avendo capito niente sul perchè il goto vada evitato nel 99+% dei casi (la sua eccessiva potenza, e quindi il rischio che esso porta di rendere il codice meno leggibile), "naturalmente" schiumano alla bocca nel 100% dei casi in cui viene proposto, anche quando, come qui, esso semplifica il codice, e lo rende piu` leggibile, poichè usato in modo disciplinato, mirato, specifico.
Mah, tant'è: personalmente, non ho mai lasciato che la prevedibile reazione degli imbecilli mi impedisse di fare quello che considero giusto, soprattutto in campo tecnico; se il lettore, invece, apprezza più il quieto vivere, che non il senso di interna soddisfazione che viene dalla adozione delle scelte tecniche ottimali, potrà poi decidere lui di avere codice "ingombro" e/o inefficiente, per non dover subire polemiche...!-)
Benissimo, dunque, siamo "quasi" in grado di scrivere
clienti COM (a parte "piccoli" dettagli come gestire le BSTR
,
i SAFEARRAY
, le VARIANT
,
ottenere i "puntatori a interfaccia"
che ci servono, e simili...:-), ma, può venire spontaneo
chiederselo, perchè poi dovremmo preoccuparcene...?
La ragione è che, attraverso interfacce COM, possiamo trovare
disponibili (spesso gratuitamente) dovizie di funzionalità molto
interessanti, dei tipi più disparati. Ad esempio, si consideri
nuovamente l'interfaccia già dettagliata -- pur restandoci
misteriosa in molti suoi aspetti, non ha essa forse qualcosa
di interessante, di
accattivante...? AddCode
, ExecuteStatement
,
sembra quasi un'interfaccia verso un interprete...
...beh, non lo è: è una interfaccia verso un numero, in potenza,
infinito
di interpreti; quella put_Language
permette, infatti, di
predisporre un oggetto Script Control
per mettere a nostra disposizione un qualsiasi linguaggio
interpretativo sia "correttamente disponibile e istallato" sulla
nostra macchina!
Due simili linguaggi sono distribuiti dalla Microsoft (e quindi usabili con i vari "programmi ospite" che sanno pilotare i "linguaggi di scripting", come Internet Explorer, e il nuovo Microsoft Scripting Host, che permette di usarli al posto del vecchio e dannato .BAT per scrivere procedure di gestione della propria macchina!): VBScript, e JScript; altri ancora sono gratuitamente disponibili in rete, ce n'è proprio per tutti i gusti, dalla pragmatica potenza di Perl (Perlscript), alla sottile e potente eleganza di Python.
Se solo agganciamo, e usiamo correttamente, questa interfaccia,
e permettiamo in qualche modo all'utente di passarci la stringa da
usare nella put_Language
, stiamo implicitamente permettendo al
nostro utente di usare qualsiasi linguaggio di scripting gli aggradi,
purchè correttamente istallato sulla sua macchina, anche un
linguaggio che noi non abbiamo mai sentito nominare -- è questa
la potenza della "separazione fra interfaccia e implementazione",
che costituisce uno dei cardini dell'informatica moderna, e che COM
elegge a propria ragion d'essere!
L'implementazione più agevolmente disponibile di questa interfaccia sarà offerta dall'oggetto "Microsoft Script Control" (che a sua volta sa come agganciare i "veri" interpreti di cui sopra), msscript.ocx; se avete una istallazione recente di Windows, può darsi che esso si trovi già nel vostro folder di sistema, correttamente istallato, visto che non poche delle applicazioni Microsoft "se lo portano dietro"; se no, vi conviene sicuramente scaricarlo, gratuitamente, dal sito della Microsoft stessa -- sarà accompagnato dal suo file di help, msscript.hlp, orientato purtroppo all'uso da Visual Basic, ma, non preoccupatevi: alla prossima puntata, vedremo quel poco (vabbè...:-) che ci manca, per permettere anche l'uso di alcune delle sue funzionalità dai programmi C!
Capitolo 35: il COM, visto da C (1)
Capitolo 37: COM: alcune API, e le BSTR
Elenco dei capitoli