Squelette d'un Driver de Matériel Virtuel
Ou squelette d'un VxD
(Virtual Device Driver Skeleton)

Maintenant que vous connaissez quelques bases à propos du VMM (Manager de Machines Virtuelles) et des VxDs (Drivers de Dispositifs (matériels) Virtuels), nous allons voir comment coder un VxD. Vous avez besoin du kit de Développement sur les Drivers de Dispositif de Windows 95/98 (Windows 95/98 Device Driver Development Kit). Il est essentiel que vous l'ayez. Windows 95 DDK est disponible seulement aux abonnés MSDN. Cependant, Windows 98 DDK est disponible gratuitement chez Microsoft. Vous pouvez aussi employer Windows 98 DDK pour développer des VxDs même s'il est orienté vers WDM. Vous pouvez télécharger Windows 98 DDK à partir de   http://www.microsoft.com/hwdev/ddk/install98ddk.htm?
Vous pouvez télécharger le pack complet, environ 30 MO ou vous pouvez télécharger seulement les parties qui vous intéressent. Si vous voulez ne pas télécharger le paquet entier, n'oubliez pas de télécharger la documentation de Windows 95 DDK incluse dans other.exe
Windows 98 DDK contient MASM version 6.11d. Vous devez vous mettre à jour à la dernière version. Pour savoir où télécharger la dernière version, vérifiez ma page principale.
Windows 9x DDK contient plusieurs fichiers 'include' (*.inc) essentiels lesquels ne sont pas inclus dans le pack MASM32.
Vous pouvez télécharger l'exemple de ce tutorial ici.

Format de fichier 'LE'

Les VxDs emploient les formats de fichiers de type 'exécutables linéaires' (LE). C'est le format de fichier conçu pour la version 2.0 OS/2. Ce format peut supporter à la fois les code 16bit et 32bits, ce qui est une des exigences des VxDs. Rappelez-vous des vieux jours des VxDs de Windows 3.x. En ce temps là, Windows bootait sous DOS, donc les VxDs devaient être capable de faire quelques initialisations en mode réel (une seule machine réelle) avant que Windows ne commute la machine en mode protégé (plusieurs machines virtuelles). Le mode réel de code 16 bits doit être dans le même fichier exécutable que le code de mode 32 bits protégé. Donc le format de fichier LE est le choix logique. Les Drivers de Windows NT n'ont pas besoin de traiter avec l'initialisation de mode réel, c'est pourquoi ils n'utilisent pas le format de fichier LE. A la place, ils emploient le format de fichier PE.

Le .CODE et les .DATA dans un fichier 'LE' sont stockés dans des segments lesquels ont des attributs différents pendant l'exécution. Voici ci-dessous les classes de segments disponibles.

Ça ne signifie pas que votre VxD doive forcément avoir TOUS ces segments. Vous pouvez choisir uniquement les segments que vous voulez employer dans votre VxD. Par exemple, si votre VxD n'a pas l'initialisation du mode réel, il ne doit pas présenter le segment RCODE.
La plupart du temps, vous utiliserez LCODE, PCODE et PDATA. En tant qu'auteur de VxDs, c'est à vous de juger et de choisir les segments appropriés pour vos .CODE/.DATA. En général, vous devez employer PCODE et PDATA autant que possible parce que le VMM peut paginer ces segments en mémoire si nécessaire. Vous devez employer LCODE pour stocker les manipulateurs d'interruptions matériel (hardware interrupt handlers) et les services qu'ils appellent.
N'employez pas directement ces classes de segment. Vous devez en fait déclarer des segments basés sur ces classes. Ces déclarations de segment sont stockées dans un fichier de définition (.def). La totalité du fichier de définition pour un VxD est ci-dessous :
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
La première déclaration est celle du nom du VxD. Le nom d'un VxD DOIT ÊTRE tout en majuscule. J'ai fais l'expérience avec des noms minuscules et le VxD a tout refusé d'exécuter sauf son propre chargement en mémoire.
L'étape suivante c'est la déclaration des segments. La déclaration consiste en trois parties : le nom du segment, la classe du segment et la propriété souhaitée pendant l'exécution de ce segment. Vous pouvez voir qu'il y a beaucoup de segments basés sur une même classe, par exemple, _LPTEXT, _LTEXT, _LDATA sont tous basées sur la classe de segment LCODE avec exactement les mêmes propriétés. Ces segments sont déclarés ainsi pour pouvoir comprendre plus facilement le code du programme écrit. Par exemple, LCODE peut contenir à la fois du code et des données. Alors que ce sera plus facile pour le programmeur s'il peut stocker les données dans le segment _LDATA et le code dans le segment _LTEXT. Et en fin de compte, ces deux segments seront combinés dans un unique segment, dans le fichier exécutable final.
Un VxD exporte un et seulement un symbole, son bloc descripteur de Matériel (DDB) ou (device descriptor block). Le DDB est en réalité une structure qui contient tout ce que le VMM a besoin de savoir sur un VxD. Vous DEVEZ exportez le DDB dans le fichier de définition de module.
La plupart du temps, vous pouvez employer le susdit fichier '.DEF' dans de nouveaux projets de VxDs. Vous devez seulement changer du nom du VxD dans la première et dernière ligne du fichier '.DEF'. Les déclarations de segment sont des overkills (des surplus non adaptés à lui) pour un projet de VxD en asm. Ils servent à l'écriture d'un VxD en C, cependant leur utilisation dans un projet asm reste correcte. Vous obtiendrez plusieurs messages d'avertissement mais votre VxD sera quand même assemblé. Vous pouvez vous débarrasser de ces messages d'avertissement en supprimant les déclarations de segment que vous n'employez pas dans votre projet.
vmm.inc contient beaucoup de macros pour la déclaration de segments dans votre fichier source.
 
_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

Chaque macro a son homologue de fin. Par exemple, si vous voulez déclarer le segment _LTEXT dans votre fichier source, vous le ferez ainsi :

VxD_LOCKED_CODE_SEG

<Placez votre code ici>

VxD_LOCKED_CODE_ENDS

Squelette d'un VxD

Maintenant que vous savez des connaissances sur les segments des fichier (LE), nous pouvons passer au fichier source. Une chose que vous pouvez voir à propos de la programmation d'un VxD est l'utilisation un peu lourde des macros. Vous trouverez des macros partout en programmant des VxDs. Ça prend une certaine attention pour les utiliser. Les macros nous sont fourni pour cacher certains détails de programmations plutôt ardues et dans certains cas, faire que le code source soit plus clair. Si vous êtes curieux, vous pouvez lire la définition de ces macros dans divers fichiers include (*.inc) tel que vmm.inc.
Voici le code source du squelette d'un VxD :
 
.386p
include vmm.inc

DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER

Begin_control_dispatch FIRSTVXD
End_control_dispatch FIRSTVXD

end


À première vue, le code source ne ressemble pas à celui d'un code source asm. C'est à cause de l'utilisation des macros. On va analyser ce code source et vous le comprendrez bientôt.

.386p
On dit à l'assembleur que nous voulons employer jeu d'instruction 80386, incluant les instructions privilégiées du CPU. On peut aussi employer .486p ou .586p.
include vmm.inc
On doit inclure vmm.inc dans chaque code source d'un VxD parce qu'il contient les définitions des macros qu'on utilise dans le fichier source. On peut en inclure d'autres pour inclure des fichiers si besoin.
DECLARE_VIRTUAL_DEVICE FIRSTVXD,1,0, FIRSTVXD_Control, UNDEFINED_DEVICE_ID, UNDEFINED_INIT_ORDER
Comme vu précédemment, le VMM apprend tout ce qu'il a besoin de savoir sur un VxD en regardant leBloc Descriptif du Device (matériel)(DDB). Le bloc descripteur du Device est une structure qui contient les informations essentielles à propos du VxD tel que son nom, son n°ID, son entrypoints de ses services VxD (s'ils existent) et cetera. Vous pouvez chercher cette structure dans VMM.inc. Il est déclaré en tant que VxD_Desc_Block. Vous exporterez cette structure dans le fichier .DEF. Il y a 22 membres dans cette structure, mais vous n'aurez généralement besoin d'en remplir que quelques-uns. Ainsi VMM.inc contient une macro qui initialisera et remplira les membres de la structure pour vous. Cette macro est DECLARE_VIRTUAL_DEVICE. Elle présente le format suivant :

Declare_Virtual_Device   Name, MajorVer, MinorVer, CtrlProc, DeviceID, InitOrder, V86Proc, PMProc, RefData

Une chose que vous dont vous pouvez vous rendre compte c'est que les labels du code source d'un VxD sont insensibles. Vous pouvez aussi bien utiliser des caractères minuscules que majuscules ou bien une combinaison des deux. On va examiner chaque paramètre de Declare_virtual_device.

Ensuite nous avons la macro Begin_Control_Dispatch.
Begin_control_dispatch FIRSTVXD
End_control_dispatch FIRSTVXD
Cette macro et son homologue de fermeture définissent la procédure de contrôle du Device laquelle est la fonction que le VMM appelle quand il y a des messages de contrôle pour votre VxD. Vous devez spécifier la première moitié du nom de la procédure de contrôle, dans notre exemple nous employons FIRSTVXD. La macro ajoutera _Control au nom que vous avez fourni. Ce nom doit correspondre à celui que vous avez spécifié dans le paramètre CtrlProc de la macro Declare_virtual_device. La procédure de contrôle du Device est toujours dans un segment de type locké (VxD_LOCKED_CODE_SEG). La susdite procédure de contrôle ne fait rien. Vous devez spécifier quels sont les messages de contrôle auxquels votre VxD s'intéresse pouvoir les traiter, c'est exactement le même procédé pour une simple procédure de fenêtre). Employez la macro Control_Dispatch à cette fin.
Control_Dispatch message, function
Par exemple, si votre VxD traite seulement le message Device_Init, alors votre procédure de contrôle de Device ressemblera à ceci :
Begin_Control_Dispatch  FIRSTVXD
  Control_Dispatch  Device_Init, OnDeviceInit
End_Control_DispatchFIRSTVXD
OnDeviceInit est le nom de la fonction qui traitera chaque message Device_Init. Vous pouvez nommer votre fonction comme ça vous chante.
Vous terminez le code source du VxD avec la directive end
Pour récapituler, au minimum, un VxD doit présenter un bloc de contrôle de Device et une procédure de contrôle Device.
- un 'bloc de contrôle de Device' pour détecter les messages reçus.
- une 'procédure de contrôle de Device' pour traiter le message et agir en fonction.
Déclarez un bloc de contrôle de Device avec la macro
Declare_Virtual_Device et une procédure de contrôle de Device avec la macro Begin_Control_Dispatch. Vous devez exporter le bloc de contrôle de Device en spécifiant son nom sous la directive EXPORTS dans le fichier .DEF.

Assemblage du VxD

Le processus d'assemblage est le même que celui employé pour l'assemblage d'une application win32 normale (un programme 32bit). Vous invoquez ml.exe sur le code asm source et liez ensuite le fichier objet avec link.exe. Les différences sont dans les commutateurs de ligne de commande employés par ml.exe et link.exe

 ml -coff -c -Cx -DMASM6 -DBLD_COFF -DIS_32  firstvxd.asm

-coff  spécifie un format d'objet de type COFF
-c   Assemble seulement. N'appelez pas le linker pour lier le fichier objet, puisque nous appellerons link.exe plus tard avec plus de paramètres.
-Cx  Préserve les chaînes de caractères publics, des labels externes.
-D<text> définit un texte macro. Par exemple,-DBLD_COFF définit un texte macro BLD_COFF lequel sera employé pour des assemblages conditionnels. Si ça vous intéresse, vous pouvez chercher BLD_COFF dans les fichiers 'include' et voir vous-même quel effet il a sur le processus d'assemblage. Ainsi dans les commutateurs de ligne de commande ci-dessus, trois textes macros sont définis : BLD_COFF, IS_32 et MASM6. Si vous êtes familiers avec le C, ce processus est identique :

#define BLD_COFF
#define IS_32
#define MASM6
link -vxd -def:firstvxd.def  firstvxd.obj

-vxd indique que nous voulons construire un VxD pour le fichier objet
-def:<.DEF File> indique le nom du fichier de définition du module du VxD

je trouve plus commode d'employer le makefile mais vous pouvez créer un fichier batch (*.bat) pour automatiser le processus d'assemblage si vous n'aimez pas l'approche du makefile. Voici mon makefile.

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


[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte