Downloadez l'exemple ici.
Les fichiers d'entrée-sortie sous Win32 ressemblent étonnement à ceux sous DOS. Les étapes nécessaires sont les mêmes. Vous avez seulement besoin de changer les appels aux API et c'est fait. Les étapes exigées sont les suivantes :
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
MEMSIZE equ 65535
EditID equ 1 ; n° ID de la zone d'édition.
.data
ClassName db "Win32ASMEditClass",0
AppName db "Win32 ASM Edit",0
EditClass db "edit",0
MenuName db "FirstMenu",0
ofn
OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ?
; L'Handle de la zone d'édition.
hFile HANDLE ?
; Handle du fichier.
hMemory HANDLE ?
;Handle du bloc de mémoire réservé.
pMemory DWORD ?
;Pointer du bloc de mémoire réservé
SizeReadWrite DWORD ?
; Nombre d'octets lu ou écrit réalité. (dans le bloque mémoire on peut très bien ne pas tout utiliser).
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
mov CommandLine,eax
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain
proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain
endp
WndProc
proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_CREATE
invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
0,0,0,hWnd,EditID,\
hInstance,NULL
mov hwndEdit,eax
invoke SetFocus,hwndEdit
;====================================================
;
Initialisation des members de la structure OPENFILENAME
;====================================================
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hWndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
.ELSEIF uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0ffffh
invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
.ELSEIF uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
invoke SetFocus,hwndEdit
.elseif ax==IDM_SAVE
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetSaveFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
invoke SetFocus,hwndEdit
.else
invoke DestroyWindow, hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor
eax,eax
ret
WndProc
endp
end
start
Dans la section WM_CREATE, nous créons un contrôle d'édition (ou une zone d'édition, de saisie). Notez que les paramètres x, y, width, height qui sont les dimensions du contrôle sont tous mis à zéros puisque nous redéfinirons leurs tailles plus tard pour que son secteur client soit contenu en entier dans la fenêtre parente.
Remarquez que dans ce cas, nous ne devons pas appeler ShowWindow pour faire apparaître la zone d'édition à l'écran, parce que nous incluons le style WS_VISIBLE. Vous pouvez aussi utiliser ce tour de passe passe pour la fenêtre parente.
;=====================================================
;
Initialisation des membres de la structure OPENFILENAME.
;=====================================================
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hWndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
Après la création du contrôle d'édition, prenons le temps d'initialiser les membres d'ofn. Parce que nous souhaitons aussi réutiliser ofn pour la boîte de dialogue de sauvegarde, nous remplissons uniquement les membres *communs* employés par GetOpenFileName et GetSaveFileName.
La section WM_CREATE est un formidable endroit pour pouvoir faire vite fait l'initialisation.
.ELSEIF uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0ffffh
invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
Nous recevons des messages WM_SIZE lorsque la taille du secteur client de notre fenêtre principale change. Nous recevons aussi ce message quand la fenêtre est créée en premier. Pour pouvoir recevoir ce message, les styles de classe de fenêtre doivent inclure les styles CS_VREDRAW et CS_HREDRAW. Nous profitons de cette occasion pour redimensionner notre contrôle d'édition à la taille du le secteur client de notre fenêtre parentale. D'abord nous devons connaître la largeur actuelle et la hauteur du secteur client de la fenêtre parente. Nous obtenons ces renseignements de lParam. Le mot de poids fort d'lParam contient la hauteur alors que le mot de poids faible d'lParam contient la largeur du secteur client. Nous employons alors ces deux informations pour redimentionner la zone d'édition en appelant la fonction MoveWindow. En plus du changement de la taille de la fenêtre, nous pouvons aussi changer sa position.
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
Quand l'utilisateur choisit le sous-menu File/Open, nous remplaçons les Flags de la structure ofn et appelons la fonction GetOpenFileName pour afficher la boîte de dialogue qui sert à ouvrir des fichiers.
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
Après que l'utilisateur ait choisit un fichier pour l'ouvrir, nous appelons CreateFile pour ouvrir ce fichier. Nous indiquons à cette fonction qu'elle doit essayer d'ouvrir le fichier pour le lire ou bien écrire dedans. Après que le fichier soit ouvert, la fonction renvoie l'handle de ce fichier que nous stockons dans une variable Globale pour une future utilisation. Cette fonction a la syntaxe suivante :
CreateFile proto lpFileName:DWORD,\
dwDesiredAccess:DWORD,\
dwShareMode:DWORD,\
lpSecurityAttributes:DWORD,\
dwCreationDistribution:DWORD\,
dwFlagsAndAttributes:DWORD\,
hTemplateFile:DWORD
dwDesiredAccess spécifie quelle est l'opération que vous voulez exécuter sur le fichier.
Quand le fichier est ouvert, nous allouons(réservons) un bloc de mémoire pour l'utiliser par les fonctions WriteFile ou ReadFile. Nous spécifions le flag GMEM_MOVEABLE pour que ce soit Windows qui s'occupe de déplacer le bloc de mémoire dans le but de consolider la mémoire (défragmentation locale). Le flag GMEM_ZEROINIT dit à GlobalAlloc de remplir le bloc de mémoire nouvellement réservé avec des zéros.
Quand les retours de GlobalAlloc sont menés avec succès, eax contiennent l'handle du bloc de mémoire alloué. Nous passons cet handle à la fonction GlobalLock qui renvoie ensuite le pointer au bloc de mémoire.
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
Quand le bloc de mémoire est prêt pour l'utilisation, nous appelons la fonction ReadFile pour lire les données du fichier. Quand un fichier est d'abord ouvert ou créé, le fichier n'est pas. Ainsi dans ce cas, nous commençons à lire le premier octet dans le fichier. Le premier paramètre de ReadFile est l'handle du fichier à lire, le deuxième est le pointer du bloc de mémoire qui content les données, ensuite c'est le nombre d'octets à lire dans le fichier, le quatrième paramètre est l'adresse de la variable (de taille DWORD) qui contiendra le nombre d'octets réellement lus dans le fichier.
Après que nous ayons remplis le bloc de mémoire des données, nous transvasons ces données vers le contrôle d'édition (la zone de saisie) en envoyant le message WM_SETTEXT au contrôle d'édition, avec lParam qui contient le pointer du bloc de mémoire. Après cet appel, le contrôle d'édition affiche les données dans son secteur client.
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
À ce point, nous n'avons aucun besoin de maintenir le fichier ouvert plus longtemps puisque notre but est d'écrire dans un autre fichier (que le fichier original)les données visibles dans contrôle d'édition. Donc nous fermons le fichier en appelant CloseHandle avec l'Handle du fichier comme son paramètre. Ensuite nous ouvrons le bloc de mémoire et le libérons. En réalité vous ne devez pas libérer la mémoire dès maintenant, vous pourrez réutiliser plus tard le bloc de mémoire pendant l'opération de sauvegarde. Mais dans le but de notre démonstration, je tiens à la libérer ici.
invoke SetFocus,hwndEdit
Quand la DialogBox (qui sert à ouvrir des fichiers) s'affiche à l'écran, le centre d'entrée passe de la fenêtre principale à la DialogBox en question pour que ce soit elle qui soit active. Ainsi quand on referme un fichier de la DialogBox (déjà ouvert), nous devons rendre le centre d'entrée à la zone de saisie (au contrôle d'édition), qui se trouve en arrière.
Ceci fini l'opération de lecture sur le fichier. À ce point, l'utilisateur peut écrire sur le contrôle d'édition. Et quand il veut sauvegarder les données dans un autre fichier, il doit choisir le sous-menu File/Save qui affiche une DialogBox pour les sauvegardes. La création de la boîte de dialogue de sauvegarde n'est pas très différente de la boîte de dialogue d'ouverture de fichiers. En fait, elles diffèrent seulement entre le nom des fonctions, GetOpenFileName et GetSaveFileName. Vous pouvez aussi réutiliser la plupart des membres de la structure ofn, sauf le membre Flag.
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
Dans notre cas, nous voulons créer un nouveau fichier, donc OFN_FILEMUSTEXIST et OFN_PATHMUSTEXIST doivent être omis sinon la boîte de dialogue ne nous laissera pas créer un fichier qui n'existe pas déjà.
Le paramètre dwCreationDistribution de la fonction CreateFile doit être changé en CREATE_NEW puisque nous voulons créer un nouveau fichier.
Le reste du code est identique à celui dans la partie 'fichiers à ouvrir' sauf la chose suivante :
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
Nous envoyons le message WM_GETTEXT au contrôle d'édition pour celui-ci recopier les données (le texte) qu'il contient dans le bloc de mémoire que nous avions alloués, la valeur de retour dans eax est la longueur des données à l'intérieur du buffer. Après que les données soient dans le bloc de mémoire, nous les écrivons dans un nouveau fichier.