Tutorial sulle API

Introduzione
Per gestire a basso livello alcuni dei principali aspetti del sistema operativo è necessario ricorrere ad alcune funzioni specifiche implementate nel kernel di W95, contenute in varie DLL (system32.dll, kernel32.dll, user32.dll) e chiamate genericamente Application Programming Interface. L’obiettivo di questo tutorial è di fornire le tecniche e i concetti di base necessari per comprendere ed utilizzare le principali procedure che sfruttano le API, come la gestione del registro, di processi e threads.

 

Concetti fondamentali
Le Windows API, come gran parte delle DLL in circolazione, sono programmate in C++. Questo significa che utilizzano strutture e concetti propri della programmazione in C, che esamineremo brevemente.

Puntatori

Il valore contenuto in una variabile viene immagazzinato in una determinata zona della memoria. Un puntatore non è altro che una variabile che contiene l’indirizzo di un’altra variabile. Facciamo un esempio, senza riferirci ad alcun linguaggio in particolare:

a = 5

puntatoreada = RESTITUISCIINDIRIZZODI(a)

b = RESTITUISCIVALOREALLINDIRIZZO(puntatoreada)

scrivi(b) >>> 5

A cosa serve tutto questo? Non certo a fare qualcosa che basterebbe fare con b = a; i puntatori in C servono prevalentemente a passare le variabili per indirizzo, e a gestire stringhe e array. In pratica, se in C scrivete una funzione dichiarata come FUNZIONE(PARAMETRO), il parametro verrà passato per valore; per creare un alias, invece, occorre passare l’indirizzo: FUNZIONE(PUNTATOREAPARAMETRO). Quello che in VB si fa con ByVal e ByRef. In C, inoltre, non esiste un tipo di variabile equivalente a String di VB: le stringhe non sono altro che arrays di singoli caratteri, e per gestirle si usa principalmente l’indirizzo del primo carattere. Per questo molte funzioni API richiedono sia la stringa che il numero di caratteri, e spesso per passare un array occorre passare il primo elemento (dal quale C ricava l’indirizzo) e il numero di elementi. Un tipo particolare di puntatori sono i puntatori a funzione, variabili che contengono l’indirizzo di una procedura.

Callback

Il callback è una tecnica basata sui puntatori a funzione. Permette di passare ad una procedura l’indirizzo di un’altra funzione che può essere richiamata all’interno del suo codice. Ad esempio:

CALLBACK(PUNTATOREAFUNZIONE)

.

.

RICHIAMA(PUNTATOREAFUNZIONE)

.

.

FINE

Questo è utile quando si scrive una procedura, e non si sa a priori quale funzione dovrà essere chiamata; questo è frequente nelle DLL: ad esempio, se si vuole scrivere una procedura che avverta quando viene premuto un tasto, bisognerà lasciare che il programma che implementerà la DLL dica alla procedura quale funzione deve essere richiamata. La gestione degli eventi, in C, è prevalentemente affidata al callback.

Subclassing

Il subclassing è una tecnica molto simile al callback, usata fondamentalmente per “intercettare” i messaggi inviati a una finestra. Il sistema operativo, per far sapere a un programma che l’utente ha schiacciato un tasto, o ha mosso il mouse, manderà un messaggio alla finestra del programma, richiamando tramite il callback una funzione predefinita. Mediante il subclassing si sostituisce l’indirizzo di una funzione definita dall’utente a quella predefinita; in questo modo tutti i messaggi diretti alla finestra vengono intercettati, possono essere esaminati ed eventualmente inoltrati alla funzione predefinita.

 

 

Il Kernel di windows
Ogni programma avviato in ambiente Windows costituisce un processo, indipendente e sostanzialmente isolato dagli altri. Suddividendo il tempo di elaborazione del processore tra vari processi, si crea l’illusione di un’esecuzione contemporanea (multithreading). Un thread non è altro che un sottoprocesso, eseguito simultaneamente ad altri. Visual Basic non supporta (purtroppo) il multithreading, quindi un programma VB conterrà un solo thread, il principale. Quasi tutte le applicazioni, per interagire con l’utente, si servono di finestre; una finestra è identificata da un handle (un valore long unico), e comunica con il sistema operativo tramite messaggi, che VB solitamente ci traduce in eventi. Se si usano le API, però, spesso occorre gestire i messaggi direttamente.

 

 

Dichiarare le functions API
Per accedere alle funzioni API occorre importarle, tramite le istruzioni Declare. Un’istruzione Declare è così composta:

[Public|Private] Declare {Sub|Function} NomeInternoFunzione Lib “NomeDLL” Alias “NomeFunzioneDLL” (Parametri) [As TipoValoreRestituito]

Le parole chiave Private/Public stabiliscono lo “scope” della funzione dichiarata. Una procedura dichiarata Public sarà accessibile a tutti i componenti del progetto, ma potrà essere dichiarata solo all’interno di un modulo standard (.bas). Una procedura Private, invece (è questa l’opzione predefinita) sarà visibile solo all’interno del modulo (standard, di classe o di form) all’interno del cui codice è dichiarata. In linea di massima non dovrebbe esserci ragione di conoscere il significato delle altre parti della dichiarazione, dato che generalmente per scrivere i Declares ci si avvale di alcuni strumenti (tipo l’Api Text Viewer, che si trova sul CD di Visual Basic e che dovrebbe potersi installare anche separatamente) che permettono di fare semplicemente un copia/incolla delle dichiarazioni complete. Comunque, il nome della funzione interno indica il nome con cui essa verrà richiamata all’interno del codice, il nome della DLL indica il percorso del file DLL (implicito se in Windows\System\), e la stringa che segue Alias indica il nome con cui ci si riferisce alla procedura all’interno della DLL, nel caso in cui esso sia diverso da quello indicato all’inizio. Per parametri e valore restituito ci si comporta come per qualsiasi function, prestando però particolare attenzione alla modalità con cui i valori vengono passati (ByRef/ByVal). Non c’è motivo per cui, disponendo di tools che velocizzano e semplificano le operazioni, si dovrebbe compilare a mano un’istruzione Declare, a meno che non si riferisca a funzioni non documentate o sviluppate in C dall’utente, nel qual caso si dovrebbe anche essere in grado di tradurre in VB le dichiarazioni C senza problemi. Non affronteremo questo argomento perché richiede un’infarinatura di C++. Lavorando con le API, comunque, bisognerebbe sempre avere una conoscenza di base del tipo di funzione che si sta usando e delle strutture dell’OS che utilizza, e avere sotto mano la documentazione specifica della funzione. Entrambe questi requisiti possono essere soddisfatti semplicemente consultando l’MSDN.

 

 

Problemi più comuni
VB ci mette a disposizione un ambiente estremamente stabile (relativamente a Windows), protetto dal punto di vista dell’accesso alla memoria (la protezione del contenuto della RAM è una delle principali ragioni che privano il programmatore VB di una razionale gestione dei puntatori), e caratterizzato da una spiegazione piuttosto chiara degli errori, derivata dal passato di VB come linguaggio interpretato. Scrivendo la nostra prima istruzione Declare rinunciamo a tutto questo. Ci mettiamo a dialogare con del codice già compilato, e che per giunta parla un’altra lingua. Per quanto la traduzione di VB sia buona, bisogna aspettarsi blocchi delle applicazioni, errori oscuri, e, se l’errore avviene all’interno della DLL, nessun’altra segnalazione che un valore restituito diverso da quello previsto. Sono problemi che si presentano prevalentemente in fase di sviluppo, e che una volta risolti non provocano instabilità del programma compilato, ma eliminarli può essere un lavoro terribile. Come regola generale, la cosa migliore da fare quando non si riesce a far funzionare una DLL è cercarsi un sample in VB che la usa (e che, si spera, funziona) e studiarne il codice, per capire in cosa si sta sbagliando. Comunque alcuni dei problemi che si presentano più frequentemente sono:

Blocchi del sistema, GPF e simili

Questi problemi avvengono generalmente quando si stanno usando puntatori a funzione o callbacks; il motivo del blocco è in genere questo: il programma VB viene sospeso o messo in pausa, la DLL non lo viene a sapere e cerca di richiamare una funzione che non esiste più. Di conseguenza, per risolvere questi problemi bisogna evitare di ricorrere a breakpoints e ad altri strumenti che interrompono l’esecuzione mentre il callback è attivo, e ricordarsi sempre di disabilitare il subclassing o comunque di interrompere le routine che usano il callback nelle sezioni di codice che precedono lo spegnimento del programma (eventi Unload, Terminate o simili).

Errori quando si richiama la funzione, con eventuali blocchi o valori errati

In generale si tratta di un problema legato alla dichiarazione della funzione e dei parametri da passare. Nel secondo caso, occorre ricordarsi che in generale quando si passa un array, occorre passare l’elemento di posto 0 ed il numero di elementi (v. Puntatori); la documentazione delle API aiuta poco in questi casi, ma quando un parametro indica un array ed il secondo il numero di elementi, probabilmente il problema è questo. Spesso il difetto sta nella dichiarazione (i declare di Api Viewer non sono perfetti); in questo caso, oltre a cercarsi un programma simile (che probabilmente conterrà la dichiarazione corretta), si può provare a cambiare la modalità di passaggio dei parametri. Confrontando il Declare con la dichiarazione C contenuta nell’MSDN si verifica che ogni parametro che è un puntatore (nella dichiarazione C il tipo è preceduto da un * o inizia con LP) venga passato come ByRef, e tutti gli altri ByVal. Di solito il problema riguarda i puntatori void, che possono contenere ogni tipo di variabile, e che in VB sono impropriamente tradotti con il tipo “As Any”, che purtroppo non sempre funziona. In tal caso conviene scrivere diversi declares specifici per ogni tipo:

invece che

Declare Sub PROC Lib “Mylib” alias “PROC” (Par as Any)

si scriverà

Declare Sub strPROC Lib “Mylib” alias “PROC” (Par as String)

Declare Sub lngPROC Lib “Mylib” alias “PROC” (Par as Long)

In ogni caso, la soluzione migliore e più veloce sta sempre nel trovare qualcuno che abbia già risolto il problema e copiare il suo codice.

Both comments and pings are currently closed.

Comments are closed.