Adesso gia' sapete qualcosa su VMM
e i VxDs, ora possiamo apprendere come programmare un VxD. Avete pero'
bisogno del Windows 95/98 Device Driver Development
Kit. E' essenziale che lo abbiate. Il Windows 95 DDK e'
disponibile solo ai sottoscrittori dell'MSDN. Al contrario il Windows 98 DDK e'
ottenibile gratuitamente dalla Microsoft. Potete utilizzare il Windows 98 DDK
anche per sviluppare VxD benche' sia orientato verso i WDM. E' possibile
scaricare il Windows 98 DDK da questo indirizzo http://www.microsoft.com/hwdev/ddk/install98ddk.htm.
Potete scaricare l'intero pacchetto, circa 30 MB, o
potete scaricare selettivamente solo quelle parti a cui siete interessati.
Se scegliete di non prendere l'intero package, non dimenticatevi di scaricare la
documentazione del Windows 95 DDK inclusa in other.exe
Il Windows 98 DDK contiene MASM versione 6.11d. Dovreste tuttavia
aggiornarlo all'ultima versione. Per informazioni su dove scaricare gli
upgrade per le ultime versioni, controllate a quest'indirizzo.
Il Windows 9x DDK contiene inoltre parecchi file
include essenziali che non sono compresi nel package di MASM32.
Gli esempi di questo tutorial sono prelevabili da
qui.
Un VxD utilizza il formato file linear executable (LE). Questo e' il formato file sviluppato per OS/2 versione 2.0. Esso puo' contenere sia codice a 16 e 32-bit, il che e' uno dei requisiti dei VxDs. Ricordatevi che i VxDs risalgono ai giorni di Windows 3.x. A quell'epoca, Windows si eseguiva a partire dal DOS cosicche' alcuni VxDs potevano richiedere una qualche inizialiazzazione in modo reale prima che Windows commutasse la macchina in modo protetto. Il codice in modo reale a 16 bit deve quindi risiedere nello stesso file eseguibile di quello in modo protetto a 32 bit. Di conseguenza il formato di file LE e' la scelta logica. I driver NT, fortunatamente, non devono avere a che fare con alcuna inizializzazione in modo reale cosi' non devono usare il formato file LE. Essi invece utilizzano il formato file PE.
Il formato file LE
Codice e dati in un file LE sono salvati in
segmenti con
differenti attributi di esecuzione. Di seguito e' disponibile l'elenco delle
classi di segmento.
Quanto detto non significa che il vostro
VxD debba possedere TUTTI questi segmenti. Potete scegliere quali volete utilizzare. Per
esempio, se il VxD non possiede nessuna inizializzazione in modo reale, non e'
necessario includere il segmentoRCODE .
La maggior parte delle
volte utilizzerete solo LCODE, PCODE e PDATA
. E' una vostra
valutazione, come programmatori del VxD, scegliere i segmenti piu' appropriati
per il vostro codice/dati. In generale, dovreste utilizzare il piu'
possibilePCODE e
PDATA in
quanto VMM puo' paginare i segmenti fuori o dentro la memoria se e' necessario.
Dovreste poi usare LCODE per memorizzare i gestori degli interrupt hardware e i
servizi che saranno chiamati da questi handlers. Voi non
utilizzate direttamente le classi di segmento. Dovete invece dichiarare i
segmenti basati su queste classi. Queste dichiarazioni di segmento sono
contenute in un file di definizione del modulo (.def). La dichiarazione completa
di un module definition file di un VxD e' la seguente :
VXD FIRSTVXD
SEGMENTS
_LPTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LTEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LDATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TEXT CLASS 'LCODE' PRELOAD NONDISCARDABLE
_DATA CLASS 'LCODE' PRELOAD NONDISCARDABLE
CONST CLASS 'LCODE' PRELOAD NONDISCARDABLE
_TLS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_BSS CLASS 'LCODE' PRELOAD NONDISCARDABLE
_LMGTABLE CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_LMSGDATA CLASS 'MCODE' PRELOAD NONDISCARDABLE IOPL
_IMSGTABLE CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_IMSGDATA CLASS 'MCODE' PRELOAD DISCARDABLE IOPL
_ITEXT CLASS 'ICODE' DISCARDABLE
_IDATA CLASS 'ICODE' DISCARDABLE
_PTEXT CLASS 'PCODE' NONDISCARDABLE
_PMSGTABLE CLASS 'MCODE' NONDISCARDABLE IOPL
_PMSGDATA CLASS 'MCODE' NONDISCARDABLE IOPL
_PDATA CLASS 'PDATA' NONDISCARDABLE SHARED
_STEXT CLASS 'SCODE' RESIDENT
_SDATA CLASS 'SCODE' RESIDENT
_DBOSTART CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBOCODE CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_DBODATA CLASS 'DBOCODE' PRELOAD NONDISCARDABLE CONFORMING
_16ICODE CLASS '16ICODE' PRELOAD DISCARDABLE
_RCODE CLASS 'RCODE'
EXPORTS
FIRSTVXD_DDB @1
Il primo
statement e' la dichiarazione del nome del VxD. Il nome di un VxD
DEVE essere sempre
in maiuscolo. Personalmente ho sperimentato con nomi in minuscolo e il VxD ha
rifiutato di eseguire alcunche' eccetto di caricarsi in
memoria.
Gli statements seguenti sono invece le dichiarazioni dei
segmenti. La dichiarazione consiste di tre parti: il nome del segmento, la
classe e le proprieta' di esecuzione dello stesso. Potete vedere che ci sono
molti segmenti basati sulla stessa classe: per esempio _LPTEXT, _LTEXT, _LDATA sono tutti basati sulla
classe di segmento LCODE con esattamente le stesse proprieta'. Questi segmenti sono
dichiarati allo scopo di rendere la stesura del codice piu' semplice da
comprendere. Ad esempio, LCODE puo' contenere sia codice che
dati. Sara' tutto piu' semplice per il programmatore se puo' memorizzare i dati
nel segmento _LDATA
e il codice nel segmento _LTEXT . Eventualmente nell'eseguibile finale entrambi i segmenti
possono essere combinati in uno solo .
Un VxD esporta soltanto
un simbolo, il device descriptor block (DDB). Il DDB e' in sostanza la
struttura che contiene ogni cosa di cui VMM abbisogna di conoscere di un
VxD. Voi DOVETE
necessariamente esportare il DDB nel file di definizione del
modulo.
Nella maggior parte dei casi potrete utilizzare il
precedente file .DEF nei vostri nuovi progetti di VxD. Dovrete solo cambiare il
nome del VxD nella prima e nell'ultima linea del file .DEF. Le dichiarazioni dei
segmenti sono superflue per un progetto di VxD in asm. Sono riportate per un
loro utilizzo con un progetto di VxD in C, ma usarle per un progetto in asm e'
ugualmente corretto. Riceverete molti messaggi di warning ma poi si assemblera'.
Potete evitare questi noiosi messagi di warning cancellando le dichiarazioni di
segmenti che non utilizzate nel vostro progetto.
vmm.inc
contiene parecchie macro per la dichiarazione dei segmenti nel vostro file
sorgente:
_LTEXT | VxD_LOCKED_CODE_SEG |
_PTEXT | VxD_PAGEABLE_CODE_SEG |
_DBOCODE | VxD_DEBUG_ONLY_CODE_SEG |
_ITEXT | VxD_INIT_CODE_SEG |
_LDATA | VxD_LOCKED_DATA_SEG |
_IDATA | VxD_IDATA_SEG |
_PDATA | VxD_PAGEABLE_DATA_SEG |
_STEXT | VxD_STATIC_CODE_SEG |
_SDATA | VxD_STATIC_DATA_SEG |
_DBODATA | VxD_DEBUG_ONLY_DATA_SEG |
_16ICODE | VxD_16BIT_INIT_SEG |
_RCODE | VxD_REAL_INIT_SEG |
Ogni macro ha la sua controparte di chiusura. Ad esempio, se volete dichiarare un segmento _LTEXT nel vostro file sorgente, dovreste farlo in questo modo:
VxD_LOCKED_CODE_SEG<inserite il vostro codice qui>
VxD_LOCKED_CODE_ENDS
.386p
include vmm.incDECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER
Begin_control_dispatch FIRSTVXD
End_control_dispatch FIRSTVXDend
A prima
vista, il sorgente non sembra nemmeno codice sorgente asm. Questo e' per via
dell'uso delle macro. Ma analizziamo questo codice e presto vi sara' tutto
chiaro.
.386p
Dice all'assemblatore che vogliamo utilizzare il set di istruzioni 80386 incluse le istruzioni CPU privilegiate. Potete anche usare .486p o .586p.include vmm.incDovete includere vmm.inc in ogni file sorgente per VxD perche' esso contiene le definizioni delle macro che utilizzate nei sorgenti. Potete aggiungere altri file include secondo le esigenze.
DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDERCome menzionato precedentemente, VMM apprende tutto cio' che ha bisogno di conoscere su un VxD dal device descriptor block (DDB). Un device descriptor block e' una struttura che contiene le informazioni vitali sul VxD, come in nome del VxD, il suo device ID, l'entrypoint dei suoi servizi VxD (se esistono) e cosi' via. Potete consultare questa struttura in vmm.inc. E' dichiarata come VxD_Desc_Block. Dovrete esportare questa struttura nel file .DEF . Vi sono 22 membri in questa struttura, ma di solito avrete bisogno di riempire solo alcuni di essi. In virtu' di cio' vmm.inc contiene una macro che inizializzera' e riempira' i membri della struttura per voi. Questa' macro e' DECLARE_VIRTUAL_DEVICE. Essa ha il seguente formato:
Declare_Virtual_Device Name, MajorVer, MinorVer, CtrlProc, DeviceID, InitOrder, V86Proc, PMProc, RefData
Una cosa che potete notare e' che nei sorgenti di un VxD le labels sono case-insensitive. Vi e' consentito utilizzare caratteri in maiuscolo, in minuscolo o una combinazione di questi. Procediamo coll'analizzare ogni parametro di Declare_virtual_device.
Come
vedete VMM,
DEBUG e
DEBUGCMD sono i
primi VxDs ad essere caricati, seguiti da PERF e APM. Il VxD con un piu' basso
valore dell'ordine di inizializzazione e' caricato prima. Se il vostro
VxD richiede i servizi di un'altro VxD durante la fase di
inizializzazione, dovreste specificare come ordine di inizializzazione un
valore piu' grande di quello del VxD che volete chiamare, in modo tale che, al
momento in cui il vostro VxD e' caricato, il VxD e' gia' presente in memoria e
pronto per voi. Se il vostro VxD non risente dell'ordine di
inizializzazione, specificate UNDEFINED_INIT_ORDER in questo parametro.
Begin_control_dispatch FIRSTVXDQuesta macro e la sua controparte, definiscono la device control procedure, ovvero la funzione che VMM chiama quando ci sono dei messaggi di controllo per il vostro VxD. Voi dovete specificare la prima meta' del nome della device control procedure, nel nostro esempio noi utilizziamo FIRSTVXD. La macro appendera' poi _Control al nome da noi fornito.Questo nome deve corrispondere a quello che avete specificato nel parametro CtrlProc della macro Declare_virtual_device . La device control procedure si trova sempre in un segmento locked (VxD_LOCKED_CODE_SEG). La precedente device control procedure non fa nulla di particolare. Siete voi a dover specificare a quali messagi di controllo il vostro VxD e' interessato a processere e le funzioni che li gestiranno. A questo scopo usate la macro Control_Dispatch .
End_control_dispatch FIRSTVXD
Control_Dispatchmessaggio, funzionePer esempio, se il vostro VxD processa solo il messaggio Device_Init, la vostra control procedure dovrebbe apparire in questo modo:
Begin_Control_Dispatch FIRSTVXDOnDeviceInit is the name of the function that will handle Device_Init message. You can name your function anything you like.
Control_Dispatch Device_Init, OnDeviceInit
End_Control_DispatchFIRSTVXD
ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32firstvxd.asm
-coff Specifica il formato
oggetto COFF
-c
Assembla solamente. Non chiamare il linker per linkare il file
oggetto.
-Cx Preserva
il case delle labels public ed extern.
-D<text> Definisce una macro
di testo. Per esempio, -DBLD_COFF definisce una macro di testo "BLD_COFF"
che sara' utilizzata in una assemblaggio condizionale. Se siete interessati,
potete cercare BLD_COFF nei file di include e vedere da voi quale
effetto essa ha sul processo di assemblaggio. Cio' detto, nella
command line sopra sono definite tre macro di testo: BLD_COFF, IS_32 e
MASM6. Se siete familiari col C, questo processo e' analogo a
:
#define BLD_COFFlink -vxd -def:firstvxd.def firstvxd.obj
#define IS_32
#define MASM6
-vxd
Specifica che vogliamo realizzare un VxD da un
file oggetto
-def:<.DEF file> Specifica il nome del file di definizione del modulo del
VxD
Trovo piu' conveniente utilizzare un makefile ma, se non vi piace l'approccio del makefile, potete sempre creare un file batch per automatizzare il processo di assembling.
NAME=firstvxd
$(NAME).vxd:$(NAME).obj
link -vxd -def:$(NAME).def
$(NAME).obj
$(NAME).obj:$(NAME).asm
ml -coff -c -Cx -DMASM6
-DBLD_COFF -DIS_32 $(NAME).asm
Traduzione italiana a cura di: Kill3xx - kaneda27@hotmail.com
Torna alla pagina principale |