Programmazione di VxD: Fondamenti

Ora sappiamo costruire un VxD che non fa nulla. In questo capitolo, lo renderemo piu' produttivo, aggiungendo un handler di messaggi di controllo.

Inizializzazione e chiusura del VxD

Ci sono due tipi di VxD: quelli statici e quelli dinamici. Ognuno di questi due tipi ha diversi metodi di caricamento. Essi quindi ricevono tipi diversi di messaggi di inizializzazione e di chiusura.

VxD statici:

La VMM carica un VxD specifico: Nel periodo di sviluppo, vi consiglio di caricare il VxD dal system.ini dato che, se qualcosa non va per il verso giusto, e windows non riesce a partire, potete comunque modificare il file dal DOS. Non potete invece far cio' con il registro.
Quando la VMM carica il vostro VxD statico, il VxD ricevera' tre messaggi di controllo dal sistema nel seguente ordine:

Il vostro VxD deve resettare il flag di carry se l'inizializzazione ha avuto successo, altrimenti , se c'e' stato un errore, lo dovete impostare ad 1 prima del return . Se il vostro VxD non necessita di alcu na inizializzazione non e' necessario che elaboriate alcuno di questi tre messaggi .
Quando giunge il momento di chiudere il VxD statico, la VMM invia i seguenti messaggi di controllo:

Alla maggior parte dei VxD non serve elaborare questi due messaggi, tranne quando volete preparare il sistema ad entrare in real-mode. Dovete sapere che quando si chiude Windows 95, esso entra in real-mode. Quindi se il vostro VxD fa qualcosa allimmagine real-mode che la rende instabile, esso la deve ripristinare in questo momento.
Potreste magari chiedervi perche' questi due messaggi hanno attaccato un "2". Ricordate che quando la VMM carica i VxD statici, essa carica innanzitutto quelli con il piu' basso ordine di inizializzazione in modo che i VxD possano affidarsi a servizi che altri VxD hanno gia' caricato in precedenza. Per esempio, se il VxD2 si affida ai servizi del VxD1, esso deve impostare un ordine di inizializzazione maggiore di quello del VxD1. L'ordine con il quale verranno caricati sara':
..... VxD1 ===>  VxD2 === > VxD3 .....
Ora, durante lo scaricamento dalla memoria, cio' comporta che i VxD inizializzati per primi devono poi anche essere chiusi per primi dato che essi potrebbero chiamare nuovamente servizi di VxD caricati prima di loro. Riferendoci all'esempio di prima l'ordine sara':
.... VxD3 ===> VxD2 === > VxD1.....
Nell'esempio sopra, se il VxD2 ha chiamato alcuni servizi del VxD1 durante la sua inizializzazione, esso potrebbe anche utilizzare i servizi del VxD1 durante il suo scaricamento. Le System_Exit2 e Sys_Critical_Exit2 sono inviate in un ordine inverso all' inizializzazione. Cio' significa che, quando il VxD2 riceve questi messaggi, il VxD1 non e' ancora stato chiuso e si possono quindi utilizzare ancora i servizi di tutti i VxD caricati precedentemente. Questi messaggi non verranno usati per i VxD piu' recenti.
Ci sono anche altri due messaggi di uscita: Ora potete supporre ci sono i messaggi Device_Reboot_Notify e Crit_Reboot_Notify, ma essi non vengono inviati in un ordine inverso, come invece avviene per la versione "2".

VxD dinamici:

Questi VxD possono essere caricati e scaricati dinamicamente durante una sessione di Windows 95. Questa opzione non e' disponibile sotto Windows 3.x. L'obiettivo fondamentale dei VxD dinamici e' di supportare la riconfigurazione dinamica delle periferiche hardware Plug and Play. Comunque, li potete caricare e scaricare dalla vostra applicazione win32, e cio' li rende ideali come estensione a ring-0 della vostra applicazione.
L'esempio della spiegazione precedente e' un VxD statico. Potete convertirlo in un VxD dinamico aggiungendo la parola chiave DYNAMIC alla dichiarazione VXD nel file .DEF.
VXD   FIRSTVXD   DYNAMIC
Cio' e tutto quello che dovete fare per convertire un VxD statico in uno dinamico.
I VxD dinamici possono essere caricati: La flag FILE_FLAG_DELETE_ON_CLOSE specifica che il VxD viene scaricato quando l'handle ritornato dalla CreateFile viene chiuso.
Se usate la CreateFile per caricare un VxD dinamico, il VxD deve gestire il messaggio w32_DeviceIoControl. La VWIN32 invia questo messaggio di controllo al vostro VxD quando esso viene caricato per la prima volta col metodo CreateFile. Il vostro VxD deve ritornare 0 in eax in risposta a questo messaggio. Il messaggio w32_DeviceIoControl viene inviato anche quando l'applicazione chiama l'API DeviceIoControl per comunicare con il VxD. Esamineremo l'interfaccia DeviceIoControl in spiegazioni successive.
Un VxD dinamico riceve un messaggio durante l'inizializzazione: E un messaggio di controllo al momento della sua chiusura: Un VxD dinamico non ricevera' i messaggi di controllo Sys_Critical_Init, Device_Init e Init_Complete poiche' questi messaggi vengono inviati durante l'inizializzazione della VM di sistema. Oltre a cio' i VxD dinamici ricevono tutti gli altri messaggi di controllo quando sono in memoria. Possono fare tutto cio' che fa un VxD statico. In poche parole, un VxD dinamico viene caricato con diversi meccanismi e riceve differenti messaggi di inizializzazione/chiusura, ma tranne cio' esso e' in grado di fare cio' che fa un VxD statico.

Altri messaggi di controllo di sistema

Durante il tempo in ci un VxD sta in memoria, esso ricevera' molti altri messaggi di controllo oltre a quelli relativi all'inizializzazione ed alla chiusura. Alcuni di essi sono relativi alla gestione di macchine virtuali e altri a vari eventi. Per esempio i messaggi di controllo relativi alla VM sono: Sta nella vostra responsabilita' rispondere ai messaggi di controllo ai quali siete interessati.

Creare procedure all'interno di un VxD

Dichiarate una procedura in un VxD all'interno di un segmento. Dovete innanzitutto definire un segmento e quindi inserire al suo interno una procedura. Per esempio, se volete che la vostra funzione stia in un segmento pageable, dovete prima definirlo come questo:
VxD_PAGEABLE_CODE_SEG

[Mettete qui la vostra procedura]

VxD_PAGEABLE_CODE_ENDS

Potete mettere piu' procedure all'interno di un segmento. Voi, in quanto programmatori del VxD dovete scegliere in quale segmento mettere le vostre procedure. Se esse devono stare in memoria in ogni momento come dei gestori di interrupt hardware, mettetele in un segmento locked. Altrimenti potete metterle in un segmento pageable.
Definite le vostre procedure con le macro BeginProc e EndProc.
BeginProcnome

EndProcnome

nome e' il nome della vostra procedura. La macro BeginProc puo' ricevere molti piu' parametri, consultate la documentazione DDK di Win95 per maggiori dettagli. Ma la maggior parte delle volte userete solo il nome della procedura.
Dovreste usare le macro BeginProc-EndProc all'interno delle normali direttive proc-endp poiche' le macro BeginProc-EndProc forniscono maggiori funzionalita' rispetto proc-endp.

Convenzioni di programmazione del VxD

Utilizzo dei registri

Il vostro VxD puo' usare ogni registro, FS e GS. Ma dovreste stare attenti nel modificare i registri di segmento. Specialmente, non dovreste modificare CS e SS a meno che non siate totalmente sicuri di cio' che state facendo. Potete usare DS e ES purche' vi ricordiate di ripristinare i loro valori al ritorno della funzione. Due flag sono particolarmente importanti: le flag di direzione e di interrupt. Non dovreste disattivare gli interrupt per un periodo di tempo troppo esteso e se modificate la flag di direzione, non dimenticate di riportarla allo stato originario al ritorno della funzione.

Convenzione di passaggio dei parametri

Ci sono due convenzioni di chiamata per i servizi dei VxD: register-based e stack-based. Nei servizi basati sul registro, passate i parametri al servizio tramite vari registri e potete controllare il flag di carry dopo la chiamata al servizio per vedere se essa ha avuto successo. Non potete presumere che i valori nei registri generali vengano mantenuti al ritorno della chiamata. Nei servizi basati sullo stack, "pushate" i parametri sullo stack e ottenete il valore di ritorno in eax. I servizi basati sullo stack preservano i registri ebx, esi, edi e ebp. La maggior parte dei servizi basati sui registri hanno avuto origine in Windows 3.x. La maggior parte delle volte, potete differenziare questi due tipi di servizi guardando i loro nomi. Se esso comincia con una sottolineatura come _HeapAllocate, esso e' un servizio basato sullo stack (C) (eccetto per pochi servizi esportati dal VWIN32.VXD). Se il servizio non inizia con una sottolineatura, esso e' un servizio basato sui registri.

Chiamata dei servizi dei VxD

Chiamate la VMM e i servizi dei VxD usando le macro VMMCall e VxDCall. Entrambe le macro hanno la stessa identica sintassi. Usate la macro VMMCall quando volete chiamare i servizi VxD esportati dalla VMM e utilizzate invece  a VxDCall quando chiamate i servizi esportati da VxD diversi dalla VMM.
VMMCallservizio                ; per chiamate ai servizi basate sui registri
VMMCall  _servizio, <lista dei parametri>    ; per chiamate ai servizi basate sullo stack
La VMMCall e VxDCall sono espanse in un int 20h seguito da una dword che vi ho gia' descritto nei capitoli precedenti ma esse sono piu' convenienti da usare. Nel caso di servizi basati sullo stack, dovete inserire la lista dei parametri fra i simboli di minore e maggiore:
VMMCall  _HeapAllocate, <<dimensione del miobuffer>, HeapLockedIfDP>
_HeapAllocate e' un servizio basato sullo stack. Accetta due parametri. Dobbiamo inserirli all'interno di altri simboli di minore e maggiore. Tuttavia il primo parametro e' una espressione che la macro puo' interpretare scorrettamente, di conseguenza la delimitiamo  ulteriormente con dei simboli di minore e maggiore.

Indirizzi Flat

Nei vecchi tools, l'assembler e il linker generavano indirizzi incorretti quando usavate l'operatore offset. Cosi' i programmatori di VxD usano offset flat: invece di offset. La vmm.inc contiene una macro per rendere cio' piu' facile. OFFSET32 si espande in offset flat:. Cosi' se volete usare l'operatore offset, potete invece usare OFFSET32.
Nota: Ho sperimentato l'operatore offset quando ho scritto questa guida. Esso ha generato indirizzi corretti e quindi penso che il bug sia stato rimosso nel MASM 6.14. Ma per essere sicuri, dovreste utilizzare comunque la macro OFFSET32 al posto del normale offset.

Traduzione italiana a cura di: fabio@privacy.nu

Torna alla main page! Torna alla pagina principale