Affichage simple dans la console

Voir le sujet précédent Voir le sujet suivant Aller en bas

Affichage simple dans la console

Message par Admin le Ven 2 Déc - 17:29

Jusqu'à présent nous avons affiché les messages dans une simple fenêtre. Mais nous allons maintenant afficher les messages dans la console (que nous avons fait apparaitre en mettant l'option /console dans les paramètres du Linker).
Dans ce programme, nous appelons une sous procédure dans laquelle nous mettrons les appels à effectuer. Remarque : nous passons l'adresse du message dans le registre rcx et non par un push comme dans les exemples précédents.
Bien sûr, elle commence par aligner la pile et réserver la place pour les 4 paramètres standards. Puis il nous faut récupérer le handle de la console de sortie standard (et il y a aussi une console d'entrée standard et une console de sortie des erreurs). Pour cela nous appelons la fonction GetStdHandle avec la constante STD_OUTPUT_HANDLE en paramètre. Nous testons le code erreur et si c'est bon nous conservons le handle retournée dans une zone mémoire (en 64 bits, comme nous disposons de registres supplémentaires, nous pourrions aussi utiliser un des registres par exemple r15). Un handle est en fait un pointeur vers une structure interne de l'API Windows et qui permet de l'utiliser dans d'autres fonctions.
Avec ce handle, nous allons écrire dans la console par la fonction ... WriteConsoleA qui nécessite 5 paramètres (dont le 5ième ne sert à rien !!!) ce qui nous oblige à manipuler la pile comme indiqué dans un des sujets précédents. Mais il faut aussi passer dans un des paramètres la longueur du message. Donc si le message est fixe, nous pouvons avoir calculé sa longueur par une instruction comme ILGMSG equ $ - szMsg placée derrière le message à afficher. Ici nous préférons utiliser une fonction de l'API comme exemple llstrlen. La fonction retourne la longueur dans le registre rax que nous passons dans le registre r8 pour l'appel de la fonction WriteConsoleA. Le handle de la console est passé par le registre rcx.
Après l'appel nous testons le code retour après avoir libérer la place de l'alignement et du push et nous terminons la sous procédure.
Dans la procèdure Main, nous ajoutons l'affichage d'un message de bonne fin par MessageBoxA dont l'interet est d'attendre un clic de l'utilisateur sur le bouton OK. Sinon le programme se terminerait immédiatement après l'affichage dans la console et nous ne verrions rien !!
Code:

;programme  64 bits
;affichage message dans la console
;=====================================================
;Constantes Api windows
;=====================================================
NULL equ 0h
FALSE equ 0h
TRUE equ 1h
MB_OK       equ 0h
MB_ICONERROR  equ 010h
STD_INPUT_HANDLE  equ -10
STD_OUTPUT_HANDLE equ -11
STD_ERROR_HANDLE  equ -12

global Main
extern ExitProcess,MessageBoxA,GetStdHandle,WriteConsoleA,lstrlen,wsprintfA,GetLastError
;===========================================================
;données initialisées
;===========================================================
section .data
szTitre:  db 'Affichage', 0
szTitreErreur:  db 'Erreur', 0
szMsgFin:    db 'Fin programme!', 0
szMsgConsole:    db 'Message Console!!',10,13, 0
szMsgErreur db "Erreur  N° %d  à la ligne %d ",0
;===========================================================
;données non initialisées
;===========================================================
section .bss
hCons  resq 1
szZoneRep   resb 100
section .text
Main:
    sub rsp, 8h     ; alignement de la pile avant tout appel de procèdure
 sub rsp,20h     ; réservation place des 4 parametres pour ne pas l'oublier
 

 lea rcx,[szMsgConsole]    ; message a afficher dans la console
 call affconsolewin
    mov rcx, 0      ; handle  fenêtre
    mov rdx,szZoneRep  ; adresse du message
    mov r8,szMsgFin  ; adresse du titre de la fenêtre
    mov r9, MB_OK     ;  type du message : Erreur
    call MessageBoxA
 ; on ne dépile pas les paramétres ( add rsp 20h) pour
 ; que la pile reste alignée avant l'appel de la fonction ExitProcess
    mov  rcx,0    ; code retour
    call ExitProcess
;========================================
;affichage console
;========================================
;rcx contient le message à afficher
affconsolewin:
    sub rsp,8h          ; alignement pile
 sub rsp, 20h        ; ajuste RSP pour la sauvegarde des paramètres  20h
    mov r12,rcx   ; save message
 mov rcx,STD_OUTPUT_HANDLE
    call    GetStdHandle       ; acquisition du handle de la sortie standard
 mov r9,__LINE__ - 1
 cmp eax,NULL
 je .aff_erreurs
    mov     [hCons], rax    ; stockage
 ;calcul de la longueur du message
 mov rcx,r12 ; recup de l'adresse du message à  afficher
 call   lstrlen
 mov r9,__LINE__ - 1
 cmp eax,NULL
 je .aff_erreurs
 add rsp,20h   ; on rend la place des parametres précédent
 sub rsp, 8h   ; donc on enleve déjà 8 octets pour preparer la pile
 push    0     ; avant le push du parametre qui enleve 8 octets sur la pile
 sub rsp, 20h   ; puis on enleve les 32 octets pour la place des 4 parametres
 
 mov     rcx, [hCons]  ;handle console
 mov     rdx,r12
 mov     r8,rax   ; longueur calculee avant
 mov     r9,NULL
    call    WriteConsoleA
 add   rsp, 10h ; dépile le paramètre et son alignement
 mov r9,__LINE__ - 2
 cmp eax,NULL
 je .aff_erreurs
    mov rax,[hCons]  ; on retourne le handle de la console si ok
 jmp  .aff_fin
.aff_erreurs:
 call afferreur
 mov rax,0     ; on retourne zero si erreur
.aff_fin:
    add rsp,20h   ;nettoie la pile (avec xx = 32d )
 add rsp,8h    ; pour revenir au départ
 ret  
;=================================================================
; affichage du message d'erreur
;=================================================================
afferreur:  
    sub rsp,8h
    sub rsp,20h
   call GetLastError  ;récupèration du code erreur dans le registre rax
   mov rcx,szZoneRep  ; zone de retour du formatage
   mov rdx,szMsgErreur ; libellé du message
   mov r8,rax      ; code erreur
   call wsprintfA  ; on devrait tester aussi le code retour
   mov rcx, 0      ; handle  fenêtre
    mov rdx,szZoneRep  ; adresse du message
    mov r8,szTitreErreur  ; adresse du titre de la fenêtre
    mov r9, MB_OK|MB_ICONERROR      ;  type du message : Erreur
    call MessageBoxA
.fin:  
  add rsp,28h
  ret    

Nos petits programmes prennent de l'ampleur et nous remarquons que nous tapons les mêmes données de l'un à l'autre. Donc nous allons mettre les définitions des constantes Windows dans un fichier qui sera inséré par la directive include et les sous procédures utilitaires comme afferreur dans un source assembleur que nous allons compiler à part. Puis l'objet de ces routines sera lié dans le linker en modifiant la ligne de commende comme ceci :
Code:

<Repertoire>\Golink nompgm.obj    routineswin64.obj  /console Kernel32.dll User32.dll Gdi32.dll /entry:Main
avatar
Admin
Admin

Messages : 38
Date d'inscription : 28/11/2016

Voir le profil de l'utilisateur http://assembleur64.forumactif.com

Revenir en haut Aller en bas

Voir le sujet précédent Voir le sujet suivant Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum