Temps d'Application et Functions du Shell



Nidala: Le Shell en général est un ensemble muni de commandes intégrées (COMMAND.COM pour DOS, BASH pour Linux, etc) et d'une syntaxe de programmation qui permet d'enregistrer des programmes shell ou scripts (batch).

Downloadez l'exemple.

Un peu de théorie

Le temps d'application est d'habitude appelé "appy time". C'est simplement le temps où le système VM (la Machine Virtuelle Principale) est assez stable pour permettre l'interaction entre les VxDs et les applications Ring-3 (les programmes quelconques), spécialement les 16 bits. Par exemple, pendant le temps appy, les VxDs peuvent charger et appeler des fonctions dans les DLLs 16 bits. Ce temps appy n'est pas disponible sous Windows 3.1x. Sous Windows 3.1, un VxD peut obtenir l'adresse de n'importe quelle fonction d'une DLL 16 bits et simuler un appel lointain vers cette adresse. Cependant, n'importe quelle tâche d'un programme Ring-3 sera interrompu. Donc les seules APIs que les VxDs peuvent appeler sont celles qui ont été interrompues en sécurité, c'est-à-dire avec Postmessage. Sous Windows 95, un VxD peut appeler presque n'importe quelle fonction dans n'importe quelle DLL 16 bits à l'aide du temps appy.
Plus simplement, si votre VxD est informé qu'on est maintenant dans le temps d'application (appy), il peut charger les DLLs 16 bits et appeler des fonctions qui leurs appartiennent. Comment un VxD sait-il si le temps appy est arrivé? Il doit détecter un des événements liés au temps appy grâce au VxD Shell. Quand le système VM est dans un état stable, le VxD Shell appellera la fonction de service que le VxD a spécifié lorsqu'il s'est fait inscrire pour obtenir un événement de temps appy. Le VxD Shell appellera votre fonction de service seulement une fois pour chaque enregistrement d'événement de temps appy. C'est exactement comme une demande d'emploi. Vous allez dans une agence de recrutement, enregistrer votre numéro de téléphone et nom chez eux. Puis vous rentrez à la maison. Quand un travail est disponible, l'agence vous téléphone en vous informant des bonnes nouvelles. Si ces nouvelles vous intéressent et que vous vous en occupez, on ne vous appellera jamais plus.
Ça peut prendre un peu de temps avant qu'un événement de temps appy ne soit disponible. Les événements de temps appy ne sont pas disponibles dans les circonstances suivantes :

Gestion d'un événement de temps appy.

Vous pouvez définir un événement de temps appy en appelant la fonction _Shell_CallAtAppyTime qui possède la syntaxe suivante :
VxDCall _SHELL_CallAtAppyTime, <<OFFSET32 pfnCallback>,  dwRefData, dwFlags, dwTimeout>
pfnCallBack Est l'adresse de retour de la fonction Call que vous souhaitez que le VxD Shell appelle lorsqu'un événement de temps appy se produit. La fonction recevra deux paramètres, dwRefData et dwFlags qui sont exactement identiques aux deux paramètres que vous avez passé à la fonction _Shell_CallAtAppyTime. Notez que le VxD Shell appellera votre fonction de retour avec une séquence d'appel C. Bref, vous devez définir votre fonction de retour de service comme ça :

BeginProc OnAppyTime, CCALL, PUBLIC
ArgVar dwRefData,DWORD   ; Déclare le nom et le type.
ArgVar dwFlags, DWORD
EnterProc

<Mettez votre code ici>

LeaveProc
Return
EndProc OnAppyTime

dwRefData Représente les données que vous souhaitez que votre VxD Shell doit repasser après votre fonction de rappel de service (le Call de votre programme qui nous envoie dans le VxD). Ça peut être tout ce que vous voulez.
dwFlags Event flags (les différents événements). Ce paramètre peut être une des valeurs qui suivent :
CAAFL_RING0  événement de type Ring-0.
CAAFL_TIMEOUT  est l'événement: Temps d'attente dépassé (temps limite dans dwTimeout). 
Utilisez-le simplement, dans l'attente qu'un événement de temps appy ne se produise pendant une période de temps définie, en utilisant le Flag CAAFL_TIMEOUT. Si vous voulez attendre indéfiniment l'événement de temps appy, remplacez ce Flag par un NULL. Je ne sais pas ce que fait le Flag CAAFL_RING0 en réalité.
dwTimeout est la période de temps que le VxD sera susceptible d'attendre qu'un événement de temps appy se produise. Je n'ai pas pu trouver de renseignements à propos de l'unité de temps employée avec cet argument.

Ce service est asynchrone, ce qui signifie qu'il retourne (revient à la suite du Call) immédiatement après avoir définit le contenu de votre fonction Call qui s'occupe des événement de temps appy.
Si cet appel de service (le retour de ce Call) est couronné de succès, il renvoie dans eax le handle du type d'événement de temps appy. Si c'est un échec, eax contiendra la valeur 0.
Vous pouvez annuler votre définition (registering) de temps appy en appelant la fonction _Shell_CancelAppyTimeEvent laquelle ne prend qu'un unique paramètre, qui est l'Handle de l'événement de temps appy retourné par la fonction _Shell_CallAtAppyTime.
Pour ne pas transgresser les règles, avant l'appel _Shell_CallAtAppyTime, vous devez vérifier le système pour voir si l'événement de temps appy est valide.
Par exemple, que se passe-t-il si vous êtes en train récupérer un événement de temps appy alors que le système chute (déraille)? Le rappel de service de votre VxD ne sera jamais appelé! (Le Call de notre programme et qui visite le VxD échoue) Vous pouvez questionner le système pour voir si l'événement de temps appy est valide et disponible en appelant
_Shell_QueryAppyTimeAvailable. Ce service ne prend aucun paramètre. Il retourne 0 dans eax si le temps appy n'est pas disponible, c'est-à-dire si le système s'est définitivement fermé ou que le server de messages ait obtenu des erreurs GP. Ce service ne vous informe pas qu'on est actuellement ou non au temps appy : il vous dit seulement qu'il peut y avoir des événements de temps appy. Bref, si vous voulez vous en sortir, appelez d'abord _Shell_QueryAppyTimeAvailable et s'il renvoie une valeur non-nulle dans eax, vous pouvez continuer à appeler _Shell_CallAtAppyTime.

Services de temps d'application du Shell

Quand le temps appy est atteint, il y a plusieurs services du Shell que vous pouvez appeler : Ces six services sont à notre disposition donc les VxDs peuvent appeler des fonctions 16 bits dans des DLLs ou ExE 16 bits, comme ceux de la doc WinHelp. Cependant, puisque nous travaillons dans un mode 32 bits (et 64 bit bientôt), je n'entrerai pas dans leurs détails. Si vous êtes intéressés, vous pouvez lire la documentation Windows 95/98 DDK.
Il existe d'autre Service du Shell exclusivement liées au temps appy qui sont plus utiles je pense: _Shell_ShellExecute et _SHELL_BroadcastSystemMessage. Avec _SHELL_BroadcastSystemMessage, vous pouvez envoyer un message à toutes les fenêtres de plus haut niveau et tous les VxDs dans un appel! Si le temps appy est disponible, vous pouvez envoyer des messages à toutes les fenêtres et VxDs. Si le temps appy n'est pas disponible, vous pouvez envoyer des messages seulement aux VxDs.
_SHELL_ShellExecute est l'homologue Ring-0 de la fonction ShellExecute en Ring-3. En réalité il appelle ShellExecute (Ring-3) pour faire le boulot. Avec ce service du Shell, vous pouvez executer / ouvrir / imprimer n'importe quel fichier. _SHELL_ShellExecute présente la syntaxe suivante :
VxDCall _SHELL_ShellExecute, <OFFSET32 ShexPacket>
Elle ne prend qu'un seulement paramètre, l'adresse (Flat) d'une structure SHEXPACKET. Elle renvoie la valeur de ShellExecute dans eax. On va examiner la structure SHEXPACKET en détail..
 
shex_dwTotalSize est la taille en octets de la structure SHEXPACKET (plus le paramètre facultatif, rgchBaggagergchBaggage bientôt.
shex_dwSize est la taille en octets de la structure de SHEXPACKET, ne comptant pas le paramètre facultatif rgchBaggage. Combiné avec shex_dwTotalSize ci-dessus, le VxD Shell peut calculer la taille de rgchBaggage qui est d'une longueur arbitraire.
shex_ibOp est l'opération qu'on souhaite exécuter. Si vous spécifiez 0, cela signifie que vous voulez ouvrir le fichier. Si c'est un fichier exécutable, il sera lancé. Si vous voulez exécuter d'autres opérations, vous devez spécifier le nom de l'opération quelque part dans rgchBaggage et ce paramètre doit contenir la taille en octets du début de cette structure SHEXPACKET au premier caractère de la chaîne de caractères ASCII qui spécifie le nom de l'opération que vous voulez exécuter. La taille de SHEXPACKET est sur 32 octets. Si la chaîne de caractères représentant l'opération suit immédiatement la structure SHEXPACKET, la valeur dans shex_ibOp DOIT être sur 32. Quant à la liste des opérations vous pouvez exécuter, vérifier ShellExecute. Il y a trois opérations définies, "ouvrir", "imprimer" et "explorer".
shex_ibFile est la distance relative du début de cette structure à la chaîne de caractères ASCII qui est le nom du fichier qu'on veut envoyer à ShellExecute, dans shex_ibOp.
shex_ibParams sont les paramètres facultatifs que vous voulez passer au fichier indiqué dans shex_ibFile. Si le fichier est un document ou que vous ne voulez lui passer aucun paramètre, employer un 0. Si vous voulez passer de quelconques paramètres au fichier, mettre ces paramètres (sous forme de chaîne de caractères) quelque part après cette structure et mettre la distance relative du début de cette structure à la chaîne de caractères dans ce paramètre. Bref, c'est comme shex_ibOp et shex_ibFile.
shex_ibDir est le répertoire de travail. mettez 0 si vous voulez employer le répertoire Windows, sinon vous pouvez mettre votre répertoire préféré du genre 'C:\temps' quelque part après cette structure et mettre la distance relative entre le début de cette structure et la chaîne de caractères de renseignements de ce paramètre.
shex_dwReserved comme le nom l'indique, il est réservé. Ne mettez pas de bordel avec ça.
shex_nCmdShow Comment on doit afficher la fenêtre de l'application (du programme). C'est une des valeurs que vous passez normalement à ShowWindow, c'est-à-dire. Une des valeur du genre SW_XXXX. Cherchez ces valeurs dans Windows.inc.

Tous les membres sont des DWORD. Maintenant qu'est-ce que ce membre rgchBaggage que j'avais promis de décrire ? Il est un peu difficile à expliquer. rgchBaggage est un membre de structure SHEXPACKET mais il ne peut pas être inclus dans la définition de structure même parce que sa taille est arbitraire. Vérifiez Shell.inc, vous verrez que rgchBaggage n'est pas définie dans SHEXPACKET. Cependant la documentation Windows 9x DDK affirme que c'est un membre de SHEXPACKET.
Mais qu'est-ce que rgchBaggage ? C'est simplement un tableau de plusieurs chaînes de caractères ASCII qui sont à la suite de la structure SHEXPACKET. Dans ce tableau, vous pouvez mettre le nom de l'opération que vous voulez exécuter sur le fichier, le nom du fichier, les paramètres que vous voulez passer au fichier et le répertoire de travail. Le VxD Shell obtient l'adresse de la chaîne de caractères dans rgchBaggage en ajoutant la distance relative entre la structure SHEXPACKET au premier octet de la chaîne de caractères à l'offset de SHEXPACKET. Par exemple, si la structure SHEXPACKET commence en 0060000h et la chaîne de caractères suit immédiatement cette structure, alors la distance entre la structure et la chaîne est la taille de la structure elle-même, sur 32 octets (20h). Ainsi le VxD Shell sait que la Chaîne est placée à l'adresse 0060020h. Ce paramètre sert de position relative pour la chaîne (par rapport au début de la structure = point d'origine)

Exemple

Ce sera un exemple très simple juste pour vous montrer comment 'registerer' (détecter) un événement de temps appy et emploier _Shell_ShellExecute. Le VxD sera dynamique et sera chargé par une application win32 simple. Quand l'utilisateur presse le bouton "run CALCULATOR", le programme win32 appelle DeviceIoControl pour demander au VxD de 'registerer' un événement de temps appy et ainsi lancer 'calc.exe' qui est placé dans le répertoire ..\Windows.
 
;---------------------------------------------------------------------------------
;                                   VxD Source Code
;---------------------------------------------------------------------------------
.386p
include \masm\include\vmm.inc
include \masm\include\vwin32.inc
include \masm\include\shell.inc

VxDName TEXTEQU <VXDEXEC>
ControlName TEXTEQU <VXDEXEC_Control>
VxDMajorVersion TEXTEQU <1>
VxDMinorVersion TEXTEQU <0>

VxD_STATIC_DATA_SEG
VxD_STATIC_DATA_ENDS

VXD_LOCKED_CODE_SEG
;----------------------------------------------------------------------------
; Souvenez-vous : le nom du VxD DOIT ÊTRE en majuscule sinon il ne se chargera/déchargera pas.
;----------------------------------------------------------------------------
DECLARE_VIRTUAL_DEVICE %VxDName,%VxDMajorVersion,%VxDMinorVersion, %ControlName,UNDEFINED_DEVICE_ID,UNDEFINED_INIT_ORDER

Begin_control_dispatch %VxDName
        Control_Dispatch W32_DEVICEIOCONTROL, OnDeviceIoControl
End_control_dispatch %VxDName
 

BeginProc OnDeviceIoControl
 assume esi:ptr DIOCParams
 .if [esi].dwIoControlCode==1
      VxDCall _SHELL_CallAtAppyTime,<<OFFSET32 OnAppyTime>,0,0,0>
 .endif
 xor eax,eax
 ret
EndProc OnDeviceIoControl
VXD_LOCKED_CODE_ENDS

VXD_PAGEABLE_CODE_SEG
BeginProc OnAppyTime, CCALL
 ArgVar RefData,DWORD
 ArgVar TheFlag,DWORD
 EnterProc
 mov File.shex_dwTotalSize,sizeof SHEXPACKET
 add File.shex_dwTotalSize,sizeof EXEName
 mov File.shex_dwSize,sizeof SHEXPACKET
 mov File.shex_ibOp,0
 mov File.shex_ibFile,sizeof SHEXPACKET
 mov File.shex_ibParams,0
 mov File.shex_ibDir,0
 mov File.shex_dwReserved,0
 mov File.shex_nCmdShow,1
 VxDCall _SHELL_ShellExecute, <OFFSET32 File>
 LeaveProc
 Return
EndProc OnAppyTime
VXD_PAGEABLE_CODE_ENDS

VXD_PAGEABLE_DATA_SEG
 File SHEXPACKET <>
 EXEName db "calc.exe",0
VXD_PAGEABLE_DATA_ENDS

end

Analyse

Le VxD attend les messages DeviceIoControl, service no 1. Quand il reçoit un tel message, on prend en compte l'événement de temps appy.
VxDCall _SHELL_CallAtAppyTime,<<OFFSET32 OnAppyTime>,0,0,0>
Il passe l'adresse (Flat) de la fonction OnAppyTime à _Shell_CallAtAppyTime pour que le VxD Shell l'appelle quand un événement de temps appy se produit. Nous n'employons pas de données de référence et n'avons aucun besoin du temps mort donc les trois paramètres après l'adresse OnAppyTime sont mis à zéros.
Quand un événement de temps appy arrive, le VxD Shell appelle la fonction OnAppyTime.
BeginProc OnAppyTime, CCALL
Nous déclarons notre fonction avec BeginProc. Puisque le VxD Shell appellera OnAppyTime avec une séquence d'appel C, nous avons besoin de spécifier l'attribut CCALL.
ArgVar RefData,DWORD
ArgVar TheFlag,DWORD
EnterProc
...
LeaveProc
Return
Puisque le VxD Shell appellera OnAppyTime avec deux paramètres, nous devons définir une pile en conséquence. La macro ArgVar sert au réglage des bornes (la taille) de pile. Sa syntaxe est la suivante :
ArgVar  varname, size, used
varname est le nom du paramètre. Vous pouvez employer n'importe quel nom. size est, bien sûr, la taille du paramètre en octets. Vous pouvez utiliser BYTE, WORD, DWORD ou 1,2,4. used est habituellement omis.
Immédiatement après la macros ArgVar , nous avons besoin d'employer les macros EnterProc et LeaveProc pour marquer le début et la fin des instructions de la procédure, pour que les variables locales et les paramètres puissent être acceptés correctement. Utilisez la macro Return pour retourner au Call appelant.
 mov File.shex_dwTotalSize,sizeof SHEXPACKET
 add File.shex_dwTotalSize,sizeof EXEName
 mov File.shex_dwSize,sizeof SHEXPACKET
 mov File.shex_ibOp,0
 mov File.shex_ibFile,sizeof SHEXPACKET
 mov File.shex_ibParams,0
 mov File.shex_ibDir,0
 mov File.shex_dwReserved,0
 mov File.shex_nCmdShow,1
 VxDCall _SHELL_ShellExecute, <OFFSET32 File>
Les instructions à l'intérieur de la procédure sont simples : elles initialisent la structure SHEXPACKET et appellent le service _Shell_ShellExecute. Notez que shex_dwTotalSize contient la taille ajoutée de la structure SHEXPACKET elle-même et la taille de la chaîne de caractère qui la suit. C'est un cas simple. Si la chaîne de caractères ne suit pas immédiatement cette structure, vous devez calculer la distance entre le premier octet de la structure et le dernier octet de la chaîne de caractères par vous-même. shex_ibFile contient la taille de la structure elle-même parce que le nom du programme suit immédiatement la structure. shex_ibDir est mis à 0, ce qui signifie que nous voulons employer le répertoire Windows en tant que répertoire de travail. Notez que ça ne signifie pas que le programme doit être dans ce répertoire. Le programme peut être placé n'importe où tant que Windows peut le retrouver. shex_nCmdShow est mis à 1 ce qui est équivalant à la valeur SW_SHOWNORMAL.
File SHEXPACKET <>
EXEName db "calc.exe",0
Nous définissons une structure SHEXPACKET suivie immédiatement par le nom du programme que nous voulons exécuter.

[Iczelion's Win32 Assembly Homepage]


Traduit par Morgatte