COMMENT CRÉER UN BUREAU VIRTUEL
Tutoriel réalisé par Faiseur
Niveau: [ Intermédiaire ]
Niveau: [ Intermédiaire ]
SUJETS TRAITÉS DANS CE
TUTORIEL
- Il introduit au concept de procédures
"callback"
- il introduit au concept de handle des fenêtres Windows
-
Introduction à quatre API: ShowWindow, GetClassName,
EnumWindows, lstrcmp
NOTES
CONCERNANT CE TUTORIEL
La première fois qu’une API est
décrite dans ce tutoriel celle-ci pointe sur son lien direct MSDN
Microsoft. N'hésitez pas à vous documenter sur la MSDN pour mieux
comprendre le fonctionnement des API utilisées.
Définition de
certains termes employés:
Handle : une valeur
permettant à Windows d’identifier, dans notre cas, une fenêtre.
Callback
: Il s'agit d'une fonction - ou procédure - qui sera automatiquement
appelée quand un événement particulier se produit.
DESCRIPTION
DU PROGRAMME
Les applications permettant de créer des
bureaux virtuels ont eu un certain succès il y a quelques années. D'un
premier abord il peut sembler compliqué de mettre en place un tel
système mais la création de deux bureaux virtuels uniquement, par
exemple, est plutôt simple en se servant de quelques API. De plus ce
type de tutoriel est à ma connaissance inconnu en assembleur, ce qui m'a
motivé à en proposer un.
Notre démo, dont le code source est
fournit en fichier joint plus bas, va permettre à l’utilisateur de
changer virtuellement de bureau. Chaque bureau virtuel disposera de ses
propres paramètres ; c’est-à-dire qu’il intégrera des fenêtres de
navigateurs que l’autre bureau n’affichera pas. Il en sera de même pour
les dossiers ouverts par l’utilisateur, qui seront propres à chaque
bureau.
Code:
SqueletteGUI.asm
- Code:
comment *
Exemple de bureau virtuel (implémenté dans Horizon Toolbar) par Faiseur
*
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\masm32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\comctl32.lib
include Proc/Bureauvirtuel.asm
.const
IDD_DIALOG1 equ 1000
IDC_BTN1 equ 1001
IDC_BUREAU1 equ 1002
IDC_BUREAU2 equ 1003
.data?
hInstance dd ?
.code
DlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
Switch uMsg
Case WM_COMMAND
mov edx,wParam
movzx eax,dx
shr edx,16
.if eax==IDC_BTN1
invoke SendMessage,hWin,WM_CLOSE,NULL,NULL
.elseif eax==IDC_BUREAU1
.if virtualdesktop == 2
mov countf,1
invoke EnumWindows,addr EnumExplorerProc2,0
fn SetDlgItemText,hWin,1002,"Bureau virtuel 1 actif"
fn SetDlgItemText,hWin,1003,"Bureau virtuel 2"
mov virtualdesktop,1
.endif
.elseif eax==IDC_BUREAU2
.if virtualdesktop == 1
mov countf,1
invoke EnumWindows,addr EnumExplorerProc,0
fn SetDlgItemText,hWin,1003,"Bureau virtuel 2 actif"
fn SetDlgItemText,hWin,1002,"Bureau virtuel 1"
mov virtualdesktop,2
.endif
.endif
Case WM_CLOSE
invoke EndDialog,hWin,0
default
return FALSE
Endsw
return TRUE
DlgProc endp
start:
mov hInstance,FUNC(GetModuleHandle,0)
invoke InitCommonControls
invoke DialogBoxParam,hInstance,IDD_DIALOG1,NULL,addr DlgProc,NULL
exit
end start
Bureauvirtuel.asm
- Code:
.data
hfolder dd 20 dup(0) ; fenêtres du bureau virtuel 2
gfolder dd 20 dup(0) ; fenêtres du bureau virtuel 1
countf dd 1
virtualdesktop dd 1
.code
EnumExplorerProc proc hwnd:DWORD,lParam:DWORD
LOCAL szWinBuf[261]:BYTE
invoke GetClassName,hwnd,addr szWinBuf,sizeof szWinBuf
fn lstrcmp,addr szWinBuf,"CabinetWClass"
test eax,eax
je @F
fn lstrcmp,addr szWinBuf,"MozillaUIWindowClass"
test eax,eax
je @F
fn lstrcmp,addr szWinBuf,"IEFrame"
test eax,eax
je @F
return TRUE
@@:
mov eax,hwnd
.if eax != gfolder[0] && eax != gfolder[4] && eax != gfolder[8] && eax != gfolder[12] && eax != gfolder[16] && eax != gfolder[20] && eax != gfolder[24] && eax != gfolder[28] && eax != gfolder[32] \
&& eax != gfolder[36] && eax != gfolder[40] && eax != gfolder[44] && eax != gfolder[48] && eax != gfolder[52] && eax != gfolder[56] && eax != gfolder[60] && eax != gfolder[64] && eax != gfolder[68]
.if countf < 19
.if countf ==1
m2m hfolder[0],hwnd
.elseif countf ==2
m2m hfolder[4],hwnd
.elseif countf ==3
m2m hfolder[8],hwnd
.elseif countf ==4
m2m hfolder[12],hwnd
.elseif countf ==5
m2m hfolder[16],hwnd
.elseif countf ==6
m2m hfolder[20],hwnd
.elseif countf ==7
m2m hfolder[24],hwnd
.elseif countf ==8
m2m hfolder[28],hwnd
.elseif countf ==9
m2m hfolder[32],hwnd
.elseif countf ==10
m2m hfolder[36],hwnd
.elseif countf ==11
m2m hfolder[40],hwnd
.elseif countf ==12
m2m hfolder[44],hwnd
.elseif countf ==13
m2m hfolder[48],hwnd
.elseif countf ==14
m2m hfolder[52],hwnd
.elseif countf ==15
m2m hfolder[56],hwnd
.elseif countf ==16
m2m hfolder[60],hwnd
.elseif countf ==17
m2m hfolder[64],hwnd
.elseif countf ==18
m2m hfolder[68],hwnd
.endif
inc countf
invoke ShowWindow,hwnd,SW_HIDE
return TRUE
.endif
.endif
invoke ShowWindow,hwnd,SW_SHOW
return TRUE
EnumExplorerProc endp
EnumExplorerProc2 proc hwnd:DWORD,lParam:DWORD
LOCAL szWinBuf[261]:BYTE
invoke GetClassName,hwnd,addr szWinBuf,sizeof szWinBuf
fn lstrcmp,addr szWinBuf,"CabinetWClass"
test eax,eax
je @F
fn lstrcmp,addr szWinBuf,"MozillaUIWindowClass"
test eax,eax
je @F
fn lstrcmp,addr szWinBuf,"IEFrame"
test eax,eax
je @F
return TRUE
@@:
mov eax,hwnd
.if eax != hfolder[0] && eax != hfolder[4] && eax != hfolder[8] && eax != hfolder[12] && eax != hfolder[16] && eax != hfolder[20] && eax != hfolder[24] && eax != hfolder[28] && eax != hfolder[32] \
&& eax != hfolder[36] && eax != hfolder[40] && eax != hfolder[44] && eax != hfolder[48] && eax != hfolder[52] && eax != hfolder[56] && eax != hfolder[60] && eax != hfolder[64] && eax != hfolder[68]
.if countf < 19
.if countf ==1
m2m gfolder[0],hwnd
.elseif countf ==2
m2m gfolder[4],hwnd
.elseif countf ==3
m2m gfolder[8],hwnd
.elseif countf ==4
m2m gfolder[12],hwnd
.elseif countf ==5
m2m gfolder[16],hwnd
.elseif countf ==6
m2m gfolder[20],hwnd
.elseif countf ==7
m2m gfolder[24],hwnd
.elseif countf ==8
m2m gfolder[28],hwnd
.elseif countf ==9
m2m gfolder[32],hwnd
.elseif countf ==10
m2m gfolder[36],hwnd
.elseif countf ==11
m2m gfolder[40],hwnd
.elseif countf ==12
m2m gfolder[44],hwnd
.elseif countf ==13
m2m gfolder[48],hwnd
.elseif countf ==14
m2m gfolder[52],hwnd
.elseif countf ==15
m2m gfolder[56],hwnd
.elseif countf ==16
m2m gfolder[60],hwnd
.elseif countf ==17
m2m gfolder[64],hwnd
.elseif countf ==18
m2m gfolder[68],hwnd
.endif
inc countf
invoke ShowWindow,hwnd,SW_HIDE
return TRUE
.endif
.endif
invoke ShowWindow,hwnd,SW_SHOW
return TRUE
EnumExplorerProc2 endp
Voici
l'intelligence du programme:
1. Lorsque l'utilisateur veut
passer au bureau virtuel 2:
- Réinitialiser puis mémoriser les
identifiants des fenêtres ouvertes par le bureau virtuel 1 (il s'agit du
bureau ouvert par défaut par l'utilisateur)
- Cacher toutes les
fenêtres des navigateurs et de l'explorer du bureau 1
- Afficher
les fenêtres (les handle) appartenant au bureau 2
2. Lorsque
l'utilisateur veut passer au bureau virtuel 1:
- Réinitialiser
puis mémoriser les identifiants (handle) des fenêtres ouvertes par le
bureau virtuel 2
- Cacher toutes les fenêtres des navigateurs et
de l'explorer du bureau 2
- Afficher les fenêtres (les handle)
appartenant au bureau 1
ANALYSE DU CODE
Passage
du bureau 1 au bureau 2:
Lorsque l’utilisateur veut afficher le
bureau virtuel 2 il commence par appuyer sur le bouton correspondant
IDC_BUREAU2
- Code:
elseif eax==IDC_BUREAU2
On vérifie si nous sommes
bien sur le bureau virtuel 1. Si c'est le cas nous passons à la suite…
- Code:
.if virtualdesktop == 1
Initialisation du compteur des
handle.Ce compteur sera utilisé dans les procédures callback
EnumExplorerProc et EnumExplorerProc2.
- Code:
mov countf,1
Ceci le cœur de tout
le programme. Le passage d’un bureau virtuel à l’autre se fait avec
cette fonction Api que nous allons détailler plus bas.
- Code:
invoke EnumWindows,addr EnumExplorerProc,0
On
termine en modifiant le texte des deux boutons et en signifiant à
l'application que nous sommes passés au bureau virtuel 2
- Code:
invoke SetDlgItemText,hWin,1003,"Bureau virtuel 2 actif"
invoke SetDlgItemText,hWin,1002,"Bureau virtuel 1"
mov virtualdesktop,2
.endif
INTRODUCTION
AUX PROCEDURES CALLBACK
On utilise l'API EnumWindows pour récupérer l'handle (identifiant
unique) de toutes les fenêtres ouvertes sur le bureau de l'utilisateur.
Cette
API nous envoie à une procédure callback qui correspond, dans le
programme, à "EnumExplorerProc" "ou EnumExplorerProc2".
"EnumExplorerProc"
"ou EnumExplorerProc2" sont appelées chaque fois qu'un événement est
trouvé par l'API. "EnumExplorerProc" "et EnumExplorerProc2" vont alors
traiter cet événement puis rendre la main à l’API jusqu’au prochain
événement.
"EnumExplorerProc" s'occupe de la procédure nécessaire
pour afficher le bureau virtuel 2.
"EnumExplorerProc2" s'occupe
de la procédure nécessaire pour afficher le bureau virtuel 1.
Dans
notre cas l'API EnumWindows appellera nos procédures à chaque fois
qu'une fenêtre sera trouvée. Lorsque cette API donne la main à
"EnumExplorerProc" "ou EnumExplorerProc2" elle leur envoie, en
paramètre, une précieuse information: l'identifiant unique de la fenêtre
(handle). C'est avec cette information que nous pourrons tout faire:
cacher les fenêtres ou les faire réapparaître.
Voici l'appel
de cette fonction dans notre programme:
Pour passer du bureau
virtuel 1 au bureau virtuel 2…
- Code:
invoke EnumWindows,addr EnumExplorerProc2,0
Pour
passer du bureau virtuel 2 au bureau virtuel 1…
- Code:
invoke EnumWindows,addr EnumExplorerProc,0
ANANYLSE
DE LA PROCEDURE CALLBACK « EnumExplorerProc »
Au
début de nos procédures callback nous trouvons :
- Code:
invoke GetClassName,hwnd,addr szWinBuf,sizeof szWinBuf
GetClassName permet de retrouver le nom de la
classe d'une fenêtre trouvée par l'API EnumWindows. Dans notre cas on
vérifie par comparaison de nom (l'API lstrcmp permet de comparer deux chaînes de
caractères afin de vérifier si elles sont équivalentes) qu'il s'agit
soit:
d'une fenêtre de l'explorer (elles ont pour nom
"CabinetWClass")...
- Code:
invoke lstrcmp,addr szWinBuf,"CabinetWClass"
d'une
fenêtre de Firefox (elles ont pour nom "MozillaUIWindowClass")...
- Code:
invoke lstrcmp,addr szWinBuf,"MozillaUIWindowClass"
d'une
fenêtre d'Internet Explorer (elles ont pour nom "IEFrame")...
- Code:
invoke lstrcmp,addr szWinBuf,"IEFrame"
Si une de ces fenêtres sont
trouvées on passe à la suite de la procédure callback, sinon on rend la
main à EnumWindows.
On vérifie ici si le handle de la fenêtre
trouvée est déjà utilisé dans le bureau 2
- Code:
.if eax != hfolder[0] && eax != hfolder[4] etc
Complément:
"&&" est une manière d'écrire la condition "ET" logique
Si
c’est le cas on affiche la fenêtre trouvée du bureau 2 avec l’API ShowWindow :
- Code:
invoke ShowWindow,hwnd,SW_SHOW
Si ce n’est pas le cas
on mémorise la fenêtre comme appartenant au bureau virtuel 1
- Code:
m2m gfolder[xx],hwnd
Puis
on la rend invisible pour l’utilisateur
- Code:
invoke ShowWindow,hwnd,SW_HIDE
Au final on rend la
main à l’API EnumWindows
- Code:
return TRUE
EN RÉSUMÉ
-
Cette procédure callback aura donc été appelée le nombre de fois
correspondant au nombre de fenêtres trouvées par l'API EnumWindows.
-
Nous aurons caché les fenêtres de l'explorer et des navigateurs du
bureau 1
- Nous aurons mémorisé l'identifiant (handle) des
fenêtres de l'explorer et des navigateurs du bureau 1
- Nous
aurons ouvert, à partir de leurs identifiants, les fenêtres de
l'explorer et des navigateurs du bureau 2
POUR
REVENIR AU BUREAU VIRTUEL 1 ?
Il sera nécessaire
d’inverser le sens du test des handles lorsque nous voulons retrouver le
bureau virtuel 1. C’est pourquoi une deuxième procédure callback est
utilisée, « EnumExplorerProc2 », pour revenir à l’état initial. La
différence se fait à cet endroit :
- Code:
.if eax != gfolder[0] && eax != gfolder[4] etc
Au
lieu de vérifier si une fenêtre trouvée correspond à une fenêtre du
bureau virtuel 2, nous vérifions si la fenêtre trouvée correspond à une
fenêtre du bureau virtuel 1. Si ce n’est pas le cas nous la mémorisons
en tant que fenêtre du bureau virtuel 2 :
- Code:
m2m hfolder[xx],hwnd
Ainsi, gfolder récupère les identifiants (handle) des fenêtres du bureau virtuel 1, hfolder les identifiants (handle) des fenêtres du bureau virtuel 2.
AMELIORATIONS
POSSIBLES
On peut évidemment inclure une compatibilité
avec les fenêtres d'autres navigateurs bien que les deux cités plus haut
sont utilisés dans la grande majorité des cas. Inclure la fermeture des
fenêtres d'autres programmes en cours, dont nous ne connaissons pas
forcément l'utilisation, n'est évidemment pas conseillé pour diverses
raisons. C'est pour cela que le bureau virtuel a ses limites mais offre
une bonne versatilité en usage courant. Proposer d'autres bureaux
virtuels serait également un plus.
CONCERNANT LA
COMPATIBILITE DU CODE SOURCE
Les sources sont en
syntaxe Masm/JWasm. Mais si vous utilisez Masm (ml.exe) une erreur va
apparaître lors de la compilation du code source.
La ligne ".if eax != gfolder[0] && eax != gfolder[4] ..." est trop longue. Deux possilibités:
1. Utilisez comme moi JWasm, qui n'est pas aussi limité par la longueur du code sur une ligne
2. Séparer cette ligne en plusieurs parties avec la commande "\"
La ligne ".if eax != gfolder[0] && eax != gfolder[4] ..." est trop longue. Deux possilibités:
1. Utilisez comme moi JWasm, qui n'est pas aussi limité par la longueur du code sur une ligne
2. Séparer cette ligne en plusieurs parties avec la commande "\"
Juillet 2010 - Faiseur
0 commentaires:
Enregistrer un commentaire