Accedere alle
Funzioni API 32 e strutture C
Per Visual dBASE 7.01
di Nicolas Martin - traduzione Arturo Mazzi


Ringrazio particolarmente Romain Strieff e Dan Howard per i loro consigli e per il gentile contributo alla rilettura di questo articolo


Introduzione

Visual dBASE consente di realizzare praticamente tutto ciò che si può immaginare, grazie alla ricchezza delle funzioni e del suo linguaggio che deriva dalle precedenti versioni, che rappresentavano il primo linguaggio per Database su PC. L’avvento delle ultime versioni sotto Windows, e l’introduzione della programmazione orientata agli oggetti come struttura portante per la gestione di dati e l’interfaccia uomo-macchina, hanno fornito al prodotto migliori capacità per realizzare applicazioni di qualità eccellente.

Una applicazione sviluppata e concepita per Visual dBASE è, prima di tutto, un’applicazione Windows. In altre parole, essa si deve adeguare alle regole e rispettare i limiti di questo sistema operativo. Per mantenere una certa omogeneità e garantire una buona integrazione con questo sistema, Visual dBASE deve sfruttare al meglio le risorse e le librerie messe a disposizione da questo sistema operativo per gestire un’applicazione. Queste risorse sono accessibili tramite l'Application programming interface di Windows, comunemente chiamate API Windows.

Visual dBASE, attraverso i tools visuali di ultima generazione messi a disposizione per la creazione di una applicazione, orientata o meno alla gestione dei dati, consente di realizzare con una estrema semplicità la maggior parte delle complesse operazioni che vengono richieste dall’applicazione, offrendo un eccellente compromesso fra la gestione complessa delle API e la ricchezza delle possibilità offerte. Una applicazione Visual dBASE, essendo prima di tutto una applicazione Windows, utilizza già numerose funzioni offerte ’ API 32 di Windows ; ma il vantaggio principale è che trasforma, in un modo semplice, delle cose che erano riservate inizialmente ai soli specialisti e, soprattutto, consente di concentrare lo sforzo dello sviluppo non più sulla programmazione del sistema ma sulle specificità dell’applicazione.

Tuttavia, per rispondere a situazioni particolari, certe caratteristiche non sono immediatamente disponibili dal linguaggio Visual dBASE. In questo caso, bisogna abbandonare l’ambiente di sviluppo tradizionale e avventurarsi negli arcani della programmazione Windows, per poter direttamente utilizzare le l’API Windows 32 bits.

I contenuti di questo articolo sono:

  1. Introduzione
  2. Presentazione delle funzioni l'API 32
  3. Prime nozioni del linguaggio C
  4. Stringhe e puntatori C
  5. IL prototipo e la dichiarazione delle funzioni l'API con Visual dBASE
  6. La dichiarazione delle costanti l'API 32
  7. Esecuzione di una funzione l'API 32
  8. Le strutture C et Visual dBASE
  9. Creazione di un eseguibile utilizzando il file di struttura
  10. Ancora sulle strutture
  11. Conclusioni

Presentazione delle funzioni l'API 32

I creatori di Visual dBASE hanno inserito nel prodotto un’apertura in maniera da rendere utilizzabili le funzioni delle librerie 32 bits contenute nei file DLL. A questo proposito, Visual dBASE 7.01 può usare solo librerie 32 bits, al contrario della versione 5.7 che conosceva solo il mondo a 16 bits, compatibilità obbligata con Windows 3.1.

I files DLL sono files binari che contengono del codice e/o delle risorse (bitmaps, icone) che possono essere utilizzate in maniera simultanea da più applicazioni. Lo stesso Windows è costruito attorno a queste DLLs, che utilizziamo nelle nostre applicazioni.

Queste funzioni sono chiamate in Visual dBASE "funzioni esterne". L’uso di tali funzioni viene spiegato sull’help in linea, cercando alla voce "extern" oppure digitando help extern nella finestra comandi.

Vedremo come utilizzare questo tipo di funzioni, in modo particolare quelle offerte da l’API Windows 32, per conoscere le loro possibilità ed i limiti di impiego. Scopo è quello di acquisire una tecnica che permetta di utilizzare le funzioni l'API 32.

La difficoltà maggiore sarà quella di identificare, fra la pletora di funzioni disponibili, quelle che ci consentono di effettuare l’azione desiderata.

Visual dBASE 7.01 viene fornito con un file di aiuto particolare chiamato "Win 32 Programmer's Reference" che chiameremo "Aiuto su API 32". Questo file si trova nella directory _DBWINHOME\Help\MSHelp ( dove _DBWINHOME rappresenta la directory dove è installato Visual dBASE).

Cominceremo con la funzione chiamata : ShellExecute che consente di lanciare, da Visual dBASE, un’altra applicazione Windows, senza che appaia la finestra DOS, a differenza del comando RUN() che fa parte del linguaggio.

Cercando questa funzione nel file di aiuto API 32 , troveremo la seguente pagina:

Vediamo in dettaglio le indicazioni scritte in rosso:

I tipi utilizzati dalle funzioni API 32 rappresentano una parte importante che richiama qualche nozione del linguaggio C. Entriamo nel vivo dell’argomento.

Prime nozioni di C

L’interfaccia di programmazione Windows può sembrare all’inizio oscura, specialmente per chi proviene da un linguaggio di alto livello come Visual dBASE. In effetti, è stata concepita , realizzata in linguaggio C ed era inizialmente destinata ad essere utilizzata dai programmatori di C. Di conseguenza, le difficoltà per utilizzare questa interfaccia, dipendono dal fatto che ci si deve adattare alle particolari esigenze di questo linguaggio.

Scopo di questo capitolo non è quello di apprendere il linguaggio C, né ancor meno il C++. Si tratta di capire semplicemente gli elementi essenziali del linguaggio che serviranno per utilizzare le funzioni API.

Una delle principali differenze fra C e Visual dBASE dipende dal tipo e dalla struttura dei dati. Contrariamente a Visual dBASE, in C i dati devono essere sempre dichiarati con in tipo esplicito. C distingue anche fra variabili semplici e variabili puntatori. Risulta importante comprendere le differenze essenziali:

La gestione dinamica della memoria si effettua, quindi, manualmente ed è il programmatore che decide lo spazio di memoria da riservare e che lo deve liberare quando non viene più utilizzato. Quando si pensa al numero di operazioni di allocazione/liberazione della memoria necessarie in un linguaggio orientato agli oggetti ( come l’aggiunta o la modifica di una proprietà, di un metodo ecc.) si comprende come programmatori C++ che hanno conosciuto un linguaggio dove la gestione della memoria è automatica, non vogliono più ritornare all’età della pietra

Ritornando ai tipi di dati, l’API Windows utilizza naturalmente i tipi nativi del C che sono in lettere minuscole. I tipi derivati sono, in genere, in maiuscolo.

Un puntatore può essere dichiarato in modo esplicito in C, in questo caso, il nome della variabile è preceduto da un asterisco (*). Nei tipi derivati, l' * è generalmente contenuto nel nome del tipo come, per esempio, quelli di Windows, il cui nome del tipo inizia spesso con le lettere LP, ossia Long Pointer.

Ecco, per esempio, alcune dichiarazioni di variabili C:

char  c = 'a'      // Variabile carattere di un byte inizializzata con la lettera 'a'.
int   *k           // Puntatore a un intero

Esempio di array in C :

short   tab[2][3]        // Crea un array di 2 x 3 elementi che sono di tipo "short".
                         // tab è in realtà un puntatore al primo elemento short
                         // della zona di memoria riservata all’array

Esempio di stringhe in C :

char  st[] = "Bonjour!"    //  Crea una stringa il cui spazio di memoria è esattamente
                           //  la sua lunghezza + 1 (a causa del Null finale),
                           //  alla quale punta il puntatore st.
                           //  Questa stringa viene considerata come fissa in memoria.

char  st_tab[256]          //  Qui sono riservati 256 bytes per memorizzare,
                      //  sino al massimo 255 caratteri + '\0'

La tabella, fornisce i tipi di base e i puntatori utilizzati da Windows, che derivano dai tipi di C :
 

Tipo C nativo
Spazio di memoria (bytes) Esempio di altri Tipi equivalenti usati in Windows Tipi Visual dBASE Dichiarazione di variabili nei prototipi delle funzioni Visual dBASE  Dichiarazione di puntatore nei prototipi delle funzioni Visual dBASE 
char 1 BYTE  Numeric CCHAR  
unsigned char 1 UCHAR  Numeric CUCHAR  
short 2 WORD  Numeric CSHORT CPTR SHORT
unsigned short 2 USHORT  Numeric CUSHORT CPTR CUSHORT
int 4 DWORD, HWND, BOOL, HDC  Numeric CINT CPTR CINT
UINT 4    Numeric CUINT CPTR CUINT
long 4 LPVOID  Numeric CLONG CPTR CLONG
unsigned long 4 ULONG  Numeric CULONG CPTR CULONG
float 4    Numeric CFLOAT CPTR CFLOAT
double 8    Numeric CDOUBLE CPTR CDOUBLE
LDOUBLE 10    Numeric CLDOUBLE CPTR CLDOUBLE
LOGICAL 1    Logical CLOGICAL CPTR CLOGICAL
void N/A   N/A CVOID

Alcune precisazioni sulla tabella:

E’ importante osservare che non è tanto il nome del tipo che importa, ma il suo SPAZIO DI MEMORIA che gli corrisponde, direttamente o tramite puntatore. Visual dBASE effettua la conversione automatica nel tipo dichiarato quando si chiama la funzione, secondo le indicazioni fornite dal prototipo.

Nella dichiarazione di funzioni, Windows utilizza numerosi altri tipi derivati dai tipi di base C che risultano nella tabella. Tutti questi altri tipi possono essere definiti nelle dichiarazioni EXTERN a partire da tipi nativi di Visual dBASE.

Una sola categoria non è supportata da Visual dBASE, si tratta della funzione CALLBACK, che non analizzeremo. In breve si tratta di un equivalente del tipo Function Pointer (FP) di Visual dBASE.

Stringhe di caratteri e puntatori C

Dopo aver visto i tipi semplici, questo capitolo tratta le stringhe e i puntatori in C, ed il loro utilizzo con Visual dBASE nei prototipi delle funzioni API.

Una stringa è una serie di caratteri che possono essere memorizzati :

Al limite, una stringa Unicode può essere considerata come una stringa di byte dato che 1Short = 2Chars. In questo caso, un byte viene alternativamente settato a chr(0). Visual dBASE adotta al suo interno la convenzione Unicode per la memorizzazione delle stringhe, come numerose altre applicazioni a 32 bits. Bisogna stare attenti quando si manipolano direttamente i bytes della stringa con le funzioni setByte e getByte, che agiscono direttamente a livello di byte.

Eseguite il codice seguente per vedere come le stringhe sono memorizzate in dBASE. Osservate che iniziano da 0.

s = new String("Bonjour")
for j = 0 to (s.Length*2)-1
  ?s.getByte(j), chr(s.getByte(j))
next j

Quando si fornisce una stringa ad una funzione API, bisogna rispettare obbligatoriamente il tipo atteso, byte o Unicode.

Nota: Una cosa importante da conoscere che riguarda le stringhe ed il linguaggio C : esso impone che l’ultimo carattere di una stringa sia obbligatoriamente il carattere chr(0), chiamato anche Null char, o '\0'. Visual dBASE tiene conto di questa regola quando chiama le funzioni esterne ma questa regola deve essere sempre tenuta presente, in particolar modo quando si fanno conversioni byte <- Unicode.

Una stringa C è sempre composta da CHAR o da WORD (se è Unicode), alla quale punta un "puntatore" C di tipo CHAR o WORD. Questo stesso puntatore è una variabile che contiene come indirizzo di memoria, l’indirizzo del primo carattere della stringa puntata. Dichiarando una stringa si dichiara sempre un puntatore, cioè un indirizzo di memoria, a questa stringa. La lunghezza della stringa è fissata implicitamente dal primo Null della stringa.

Per usare le stringhe, in Visual dBASE esistono due prototipi di parametri : CSTRING e CPTR. Designano entrambi un puntatore alla stringa ma hanno le seguenti differenze:
 
 

Dichiarazione CSTRING
Dichiarazione CPTR
Conversione automatica implicita da Unicode a Byte quando si chiama una funzione esterna.
La stringa può contenere caratteri Null, ma ho incontrato problemi con questo stringhe contenenti Null.
Nessuna conversione alla chiamata

La stringa può contenere caratteri Null.

Conversione automatica implicita da Byte a Unicode se la funzione restituisce una stringa. 
La regola del carattere Null si applica alla stringa restituita, con le stesse restrizioni di impiego.
Il tipo restituita dalla funzione non può essere CPTR.


Il prototipo e la dichiarazione delle funzioni API con Visual dBASE

Ritorniamo alla funzione ShellExecute vista nel capitolo Presentazione delle funzioni l'API 32. E vediamo il prototipo, ossia dobbiamo indicare questa funzione, prima di utilizzarla, per esplicitare in quale modo Visual dBASE deve chiamarla dal programma.

Vediamo prima la sintassi generica di una dichiarazione di funzione esterna. L’help in linea, alla voce "extern", propone due modi, ma possiamo far riferimento alla seconda espressione:

Troviamo nell’ordine :

Torniamo a ShellExecute. Vengono usati i seguenti tipi: Questi tipi sono tipi di Windows, escluso INT. Bisogna trovare i tipi corrispondenti in Visual dBASE.

Per fortuna , il lavoro è in parte già fatto, Nella directory _DBWINHOME\Include esistono dei files prototipo, (con estensione .h), che contengono l’insieme delle dichiarazioni usate con le funzioni l’API 32.

Il file contenente le dichiarazioni dei tipi Windows e C è Windef.h. Basta quindi includerlo nel sorgente del vostro programma :
// Include il file Windef.h
#include <Windef.h
Ora bisogna dichiarare la funzione rispettando rigorosamente i tipi specificati. In _DBWINHOME\Include sono contenute informazioni ulteriori nel file win32api.prg, che contiene le dichiarazioni di funzioni API 32. Fare il prototipo delle nostre funzione diventa ora facile: basta cercare la linea corrispondente alla dichiarazione della funzione ( ShellExecute nel nostro caso) e ricopiarla nel nostro codice sorgente:
// Déclaration du Prototype de la fonction ShellExecute
extern HINSTANCE ShellExecute(HWND, LPCSTR, LPCSTR, LPCSTR, ;
                              LPCSTR, CINT) shell32 ;
                              from "ShellExecuteA"

Compiliamo ora il programma costituito da queste linee: Viene prodotto un errore dovuto alla dichiarazione, HWND , che pone dei problemi. Infatti, HWND è una parola chiave di Visual dBASE, da qui il conflitto.

La soluzione migliore è quella di sostituire HWND con HANDLE., per cui:

// Déclaration du Prototype de la fonction ShellExecute
extern HINSTANCE ShellExecute(HANDLE, LPCSTR, LPCSTR, LPCSTR, ;
                              LPCSTR, CINT) shell32 ;
                              from "ShellExecuteA"

Ricompiliamo ora senza errori e la funzione dichiarata è pronta per essere usata.

Il problema legato a HWND è l’unico incontrato. Tutte le altre funzioni di win32API.prg  che ho utilizzato sino ad ora funzionano correttamente.

Per spiegare ciò che è stato detto prima a proposito della parola chiave from di questo comando, sostituiamo la dichiarazione nel modo seguente, eliminando la parola from :

// Déclaration du Prototype de la fonction ShellExecuteA, sans le mot clé from
extern HINSTANCE ShellExecuteA(HANDLE, LPCSTR, LPCSTR, LPCSTR, ;
                              LPCSTR, CINT) shell32

Compilate. Ora bisognerà usare la funzione ShellExecuteA(<Paramètres) invece di shellExecute(<Paramètres).

Riassumendo, i passi delicati nella dichiarazione di prototipi di funzioni API sono limitati a:


Dichiarazione di costanti

Riprendiamo l’help della funzione ShellExecute e osserviamo il parametro nShowCmd che specifica il modo in cui sarà aperta la finestra del nuovo programma. I valori ammessi sono:

Come possiamo conoscere il valore di queste costanti? Ancora una volta nella directory _DBWINHOME\Include c ’e la risposta. Si tratta di vedere in altri file prototipo che raggruppano la dichiarazione di costanti usate in certe funzioni API 32. Non c’è una regola generale e l’inclusione di un file piuttosto che un altro dipende dalle funzioni che si useranno. Queste costanti sono, in genere, raggruppate nello stesso file secondo la loro natura. In genere la regola per l’inclusione di files di costanti sarà: Nel nostro caso, le costanti SW_... sono tutte definite nel file Winuser.h. Per essere sicuri basta aprire questo file e cercare il testo "SW_".

Il file ha le seguenti dichiarazioni:
. . .
//
// ShowWindow() Commands
//
#define SW_HIDE              0
#define SW_SHOWNORMAL        1
#define SW_NORMAL            1
#define SW_SHOWMINIMIZED     2
#define SW_SHOWMAXIMIZED     3
#define SW_MAXIMIZE          3
#define SW_SHOWNOACTIVATE    4
#define SW_SHOW              5
#define SW_MINIMIZE          6
#define SW_SHOWMINNOACTIVE   7
#define SW_SHOWNA            8
#define SW_RESTORE           9
#define SW_SHOWDEFAULT      10
#define SW_MAX              10
. . .
Per dichiarare tutte le costanti nel nostro esempio, basta includere:

// Inclure le fichier Winuser.h
#include <Winuser.h

Invece di usare il file include per la dichiarazione di costanti e funzioni API , si potrebbe dichiararle direttamente nel nostro codice, con delle primitive #define e con tipi disponibili in Visual dBASE. Sarebbe possibile ma fastidioso perché bisogna riprendere tutte le definizioni di funzione e costanti con il rischio di errori. I files include di Visual dBASE servono per questo.

Per chiudere il capitolo, i puristi avranno osservato che dal momento che si include il file Winuser.h, la dichiarazione di includere anche Windef.h diventa superflua. Infatti, il file Winuser.h. effettua lui stesso (per essere sicuri guardate la prima linea del file) l'inclusione del file Windef.h. Nel nostro caso, sarebbe sufficiente includere Winuser.h per usare ShellExecute.

Esecuzione di una funzione API 32

Vediamo come eseguire la funzione. Lanceremo la calcolatrice di windows che si trova nella directory C:\Windows, il nome del file eseguibile è: Calc.exe

Guardiamo ancora l’help in linea la funzione ShellExecute , che specifica i seguenti parametri:

Scriviamo ora il comando Visual dBASE completo, comprese le dichiarazioni viste nei capitoli : Per cui:

// Inclure le fichier Winuser.h
#include <Winuser.h

// Déclaration du Prototype de la fonction ShellExecute
extern HINSTANCE ShellExecute(HANDLE, LPCSTR, LPCSTR, LPCSTR, ;
                              LPCSTR, CINT) shell32 ;
                              from "ShellExecuteA"

ShellExecute( _app.FrameWin.hwnd, "open", "calc.exe", Null, "C:\Windows", SW_SHOWNORMAL )

Eseguite il comando e verrà visualizzata la calcolatrice. Questo esempio mostra l’utilizzo di parametri di tipo diverso, numerico, stringa, costanti API 32.

Resta da presentare un tipo di parametri di cui abbiamo solamente rivelato l‘esistenza, si tratta delle strutture C. Sono oggetto del prossimo capitolo in quanto prima bisognava conoscere le nozioni elementari spiegate nei capitoli precedenti.

Le strutture C e Visual dBASE

Se C è rimasto per tanto tempo un riferimento nel mondo dello sviluppo, prima dell’introduzione della programmazione ad oggetti, è senza dubbio grazie alla potenza che gli conferiscono le strutture.

Per illustrare le strutture C, utilizzeremo la funzione API 32 chiamata GetVersionEx, che consente di ottenere la versione del sistema operativo.

Cerchiamo prima questa funzione sull’help in linea delle API :

Questa funzione usa un solo parametro chiamato lpVersionInformation, che è un puntatore C ad una struttura chiamata OSVERSIONINFO. Facciamo click su questo link OSVERSIONINFO  indicato dal parametro e otteniamo la seguente finestra:

Vediamo in dettaglio le differenti indicazioni scritte in rosso:

Dato che abbiamo proposto analogie fra strutture C e oggetti, vediamo ora le differenze fondamentali fra le due  :
 
Strutture C
Classes et Objets
La struture C è un’entità statica.

Si tratta di un insieme di variabili o di puntatori dichiarati, di lunghezza fissa. Esso sono posti in memoria uno di seguito all’altro nell’ordine in cui sono dichiarati. La loro posizione in memoria non cambia mai.

Lo spazio di memoria occupato dalla struttura C è fisso e corrisponde esattamente alla somma dello spazio di memoria occupato dai singoli membr.

Un oggetto è invece un’entità dinamica.

Comprende elementi (proprietà, metodi) che possono essere modificati, aggiunti e cancellati ecc...

I membri non sono sempre inseriti in memoria in maniera contigua dato che viene effettuata una gestione dinamica della memoria.

Lo spazio di memoria di un oggetto è variabile e dipende dal suo contenuto in un dato momento.

Una struttura contiene solamente variabili o puntatori. Un oggetto può contenere proprietà( che sono esse stesse variabili o puntatori) e metodi .

Per comprendere meglio una struttura, ecco l’occupazione di memoria della struttura C OSVERSIONINFO :



 

Occupazione di memoria : Totale = 148 bytes
Campo della struttura
Offset : n a n + 3 DwOSVersionInfoSize
Offset : n + 4 a n + 7 DwMajorVersion
Offset : n + 8 a n + 11 DwMinorVersion
Offset : n + 12 a n + 15 DwBuildNumber
Offset : n + 16 a n + 19 DwPlatformId
Offset : n + 20 a n + 147 SzCSDVersion

Poiché strutture e classi sono, per loro natura, differenti, non è possibile usare direttamente le seconde per simulare le prime.

Per modificare il contenuto di una struttura con Visual dBASE bisogna avere accesso ad un blocco di memoria, rappresentato dal contenuto della struttura (i diversi membri) sui quali si interviene direttamente a livello di byte.

Fondamentalmente, il solo meccanismo offerto, consiste nel considerare il blocco di memoria come una stringa di caratteri Visual dBASE, e poi, grazie ai metodi setByte e getByte, si legge e si modifica il contenuto della stringa byte per byte. Anche se questo sistema funzione, bisogna dire che è estremamente arduo e pesante e che bisogna lavorare molto per ottenere un buon risultato.

Per fortuna esiste un metodo molto più efficace

Anche se Visual dBASE non ha strutture, abbiamo qualche cosa di molto più potende delle strutture C: gli oggetti e le classi..

Vedremo come possiamo simulare il comportamento di una struttura C ed ottenere, con un linguaggio di classi come Visual dBASE, la sostituzione delle strutture con le classi.

Fra le utility e gli esempi forniti con Visual dBASE 7.01, ci sono dei files che consentono di fare ciò. I files sono :

Commentiamo questi 4 files.
  1. Ciascuna struttura C sarà rappresentata da un oggetto creato a partire dalla classe Structure, presente nel file _DBWINHOME\Samples\Structure.prg.
     

     

    Questa classe Structure mette a disposizione la proprietà .value così come i metodi addMember(), setMember(), getMember(), e length() che permettono dei parametri e il controllo dell’oggetto creato a partire dalla classe Structure.

  2. La proprietà .value  riceve una stringa di caratteri Visual dBASE, che rappresentano precisamente il contenuto delle variabili C della struttura C.
  3. Per descrivere i membri della struttura C, si usa il metodo addMember() che consente di aggiungere un nuovo membro.
     

     

    Pe descrivere questo membro, bisogna indicare, come in C, il suo tipo ed il suo nome.
    I membri devono essere dichiarati nello stesso ordine che compaiono nella struttura C. Grazie al tipo fornito dalla chiamata a addMember, la posizione e l’occupazione del membro nella stringa .value viene calcolata e conservata con la descrizione del membro.

  4. Per accedere al contenuto del membro i due metodi, setMember() e getMember(), consentono di leggere e modificare il contenuto del membro specificato, a partire dalla stringa .value.
  5. Infine, con la chiamata al metodo length()si può conoscere la lunghezza della struttura in bytes.
La descrizione dei metodi e delle proprietà della classe Structure sono riportati nei commenti del file _DBWINHOME\Samples\Structure.prg.

Vediamo ora un caso concreto. Chiameremo la funzione API 32 GetVersionEx , e quindi avremo accesso ai membri della struttura OSVERSIONINFO.

Le linee di codice che segue, non figurano necessariamente nello stesso ordine in cui dovrebbero essere eseguite, ma questo aiuta alla comprensione ed alla spiegazione.

Per prima cosa vediamo il file Include. Una rapida ricerca sulla costanti ed i tipi API utilizzati con GetVersionEx e OSVERSIONINFO mostra che dobbiamo includere i file indicati successivamente.
Inoltre dobbiamo includere il file StructAPI.h dato che useremo la classe Structure :

// Files Include necessari
#include <Windef.h
#include <Winbase.h
#include <StructAPI.h

Dobbiamo quindi caricare la descrizione della classe Structure, contenuta nel file _DBWINHOME\Samples\Structure.prg:

// Carichiamo la classe Structure
set procedure to '&_dbwinhome.samples\structure.prg' additive

Vediamo ora come dichiarare la classe OSVERSIONINFO corrispondente alla struttura C. Questa è un’istanza della classe Structure, definita nel file _DBWINHOME\Samples\Structure.prg che abbiamo appena caricato. Quindi con il metodo addMember dichiariamo i membri della struttura. Questo metodo usa come parametri:

I membri devono essere inseriti nello stesso ordine della struttura C. Nel nostro caso la dichiarazione sarà :

// Questa definizione di classe corrisponde alla definizione della struttura C

// del tipo _OSVERSIONINFO. La funzione addMember crea i membri della struttura,
// usando il tipo ed il nome del campo.
class _OSVERSIONINFO of Structure
   super::addMember( TYPE_DWORD,   "dwOSVersionInfoSize" )
   super::addMember( TYPE_DWORD,   "dwMajorVersion" )
   super::addMember( TYPE_DWORD,   "dwMinorVersion" )
   super::addMember( TYPE_DWORD,   "dwBuildNumber" )
   super::addMember( TYPE_DWORD,   "dwPlatformId" )
   super::addMember( TYPE_STRING,  "szCSDVersion", 128 )

   // questa linea inizializza il primo membro della struttura che
  // deve, come precisato dall’help in linea API, contenere la dimensione della //struttura.
   // Il metodo length() della classe Structure consente di ottenere la //dimensione
   // totale espressa in bytes. Quando viene creato un oggetto, usando la classe //_OSVERSIONINFO, questo membro viene automaticamente inizializzato.
   super::setMember( "dwOSVersionInfoSize", this.length( ))
endclass

Avrete certamente visto che l’ultima linea della classe _OSVERSIONINFO richiama il metodo setMember. Infatti, l’help sulla struttura OSVERSIONINFO indica che il membro dwOSVersionInfoSize deve contenere la dimensione della struttura. Chiamando il metodo length che restituisce la dimensione della struttura, questo membro viene inizializzato automaticamente.

Per adesso la chiamata alla funzione GetVersionEx richiede un prototipo che può essere ottenuto ricopiando la linea corrispondente nel file _DBWINHOME\Include\Win32api.prg :

// Prototype della funczione GetVersionEx come viene fornita in Win32api.prg
extern BOOL        GetVersionEx( LPSTRUCTURE ) kernel32 ;
                   from "GetVersionExA"

Vengono definite alcune variabili locali, in particolar modo OSVERSIONINFO che contiene l’oggetto creato a partire dalla classe _OSVERSIONINFO :
local  OSVERSIONINFO, dwPlatformId

L’oggetto OSVERSIONINFO viene ora creato con una istanza della classe _OSVERSIONINFO  :

// Crée l'objet structure
OSVERSIONINFO = new _OSVERSIONINFO( )

Ora che è stato creato l’oggetto struttura OSVERSIONINFO, si può chiamare la funzione GetVersionEx per aggiornare il contenuto dell’oggetto.

Osservate che viene passato come parametro la proprietà .value , dato che questa stringa rappresenta il blocco di memoria nel quale sono memorizzati i membri della struttura :

// Achiamata alla funzione GetVersionEx passando come parametro //OSVERSIONINFO.value, che rappresenta ilk contenuto della struttura in memoria
GetVersionEx( OSVERSIONINFO.value )

Il rimanente codice visualizza il contenuto dei membri usando il metodo getMember come è stato aggiornato da GetVersionEx.

Un comando macro, chiamato LOWORD, è stato dichiarato nel file Windef.h, e consente di recuperare solamente i 16 bit low da una parola di 32 bit al contrario di HIWORD che restituisce i 16 più alti. Guardate il file Windef.h, che contiene utili macro per separare o unire delle parole binarie.

// visualizza il contenuto del membri della struttura

? ’ Major Version', OSVERSIONINFO.getMember( 'dwMajorVersion' )
? 'Minor Version', OSVERSIONINFO.getMember( 'dwMinorVersion' )
? 'Build Number', LOWORD( OSVERSIONINFO.getMember( 'dwBuildNumber' ))
dwPlatformId = OSVERSIONINFO.getMember( 'dwPlatformId' )

do case
 case dwPlatformId == VER_PLATFORM_WIN32s
  ? 'Win32s on Win 3.1'
 case dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
  ? 'Win32 on Windows 95'
 case dwPlatformId == VER_PLATFORM_WIN32_NT
  ? 'Win32 on Windows NT'
endcase

Il programma completo dBASE relativo a questo esempio, può essere scaricato usanto il link al fondo della pagina. Eseguendo il programma, vengono visualizzate nella finestra di comando Visual dBASE il numero della versione Windows, il numero di costruzione e la versione del Sistema operativo.

Creazione di un eseguibile usando i files di struttura

Quello che abbiamo visto nel capitolo precedente, si può usare in un programma eseguibile generato con Visual dBASE, a patto di seguire le seguenti istruzioni:

  1. Includere nel file progetto .prj un riferimento al files:
  1. Si deve inserire una copia del file DLL Structmem.dll nella stessa directory del programma eseguibile.

Ancora più lontano con le strutture

Anche se la classe Structure di Structure.prg è un buon punto di partenza, essa soffre di alcune piccole lacune che limitano, in alcuni casi, il suo campo d’applicazione.

Per questo motivo ho creato la mia classe StructureEx, compatibile con la classe Structure, che può sostituire questa classe e che migliora i seguenti punti.

Ecco alcuni miglioramenti:

  1. I tipi supportati dalla classe Structure, possono essere facilmente estesi, dato che altri tipi dichiarati nel file _DBWINHOME\Include\StructAPI.h,  sono supportati dalla DLL Structmem.dll. In particolare il tipo TYPE_CHAR , o  TYPE_BYTE che sono spesso usati nelle strutture C, così come il tipo TYPE_POINTER per la dichiarazione di puntatori.
  2. La classe structure è un oggetto semplice (classe object()), l'accesso ad un membro si effettua con una ricerca lineare che può essere anche lunga nel caso la struttura contenga parecchi membri. La classe StructureEx deriva da un AssocArray che è più appropriato, rapido e semplifica la gestione della classe.
  3. Alcuni tipi di strutture C, come la struttura DCB per le comunicazioni seriali, (cercate questo argomento sull’help in linea), usano campi di bit. In altri termini, alcuni membri sono descritti come gruppi di bits. L’accesso a dei gruppi di bits messo della classe Structure non è molto semplice. Con StructureEx, questo tipo di membro può essere dichiarato e gestito come gli altri tipi
La sua descrizione ed il suo utilizzo, sarà oggetto di un prossimo articolo.

Conclusioni

In fin dei conti, l’uso delle chiamate dirette a l'API 32 di Windows da Visual dBASE 7.01 non è molto difficile quando si affronta in modo razionale. Questo argomento non è stato trattato in maniera esaustiva, ma vuole rappresentare solo un punto di partenza. Esistono altri metodi ed ognuno è libero di usare le tecniche personali, ma ritengo che questo semplice approccio, unito a nozioni elementari del linguaggio C, risulti utile.

Visual dBASE dimostra ancora una volta, che grazie alla sua programmazione ad oggetti, si può adattare a situazioni per le quali non era previsto il suo utilizzo.

In ogni caso API Windows sono un po’ difficili a causa della loro concezione iniziale.

Ora è il vostro turno!

Nicolas Martin