Bája el ejemplo aquí.
El manejo de la memoria bajo Win32 desde el punto de vista de las aplicaciones es un poco simple y directo. Cada proceso usa un espacio de 4 GB de dirrecciones de memoria. El modelo de memoria usado se llama modelo de memoria plana [flat]. En este modelo, todos los segmentos de registro (o selectores) direccionan a la misma localidad de memoria y el desplazamiento [offset] es de 32-bit. Tambien las aplicaciones pueden acceder a la memoria en cualquier punto en su espacio de direcciones sin necesidad de cambiar el valor de los selectores. Esto simplifica mucho el manejo de la memoria. No hay mas puntos "near" (cerca) o "far" (lejos).
Bajo Win16, hay dos categorías principales de funciones de la API de memoria: Global y Local. Las de tipo Global tienen que ver con la memoria situada en diferentes segmentos, por eso hay funciones para memoria "far" (lejana). Lasfunciones de la API de tipo Local tienen que ver con un motículo [heap] de memoria local del proceso así que son las funciones de memoria "near" (cercana). Bajo Win32, estos dos tipos son uno y le mismo tipo. tendrás el mismo resultado si llamas a GlobalAlloc o LocalAlloc.
Los pasos para ubicar y usar la memoria son los siguientes:
Puedes sustituir "Global" por "Local" en LocalAlloc, LocalLock,etc.
El método de arriba puede simplificarse radicalmente usando el flag GMEM_FIXED en la llamada a GlobalAlloc. Si usas esta bandera [flag], El valor de retorno de Global/LocalAlloc será el puntero al bloque de memoria reservado, no el manejador (handle). No tienes que llamar a Global/LocalLock y puedes pasar el puntero a Global/LocalFree sin llamar primero a Global/LocalUnlock. Pero en este tutorial, usaré el modo "tradicional" ya que te lo puedes encontrar cuando leas el código de otros programas.
La E/S de
archivos bajo Win32 tiene una semblanza más notable que la de bajo DOS.
Los pasos a seguir son los mismos. Sólo tienes que cambiar las interrupciones
por llamadas a la API y ya está. Los pasos requeridos son los siguientes:
.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 ; ID del control de edición
.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 ?
; manejador (handle) del control de edición
hFile HANDLE
?
; manejador de archivo
hMemory
HANDLE ?
; manejador del bloque de memoria reservada
pMemory
DWORD ?
; puntero al bloque de memoria reservada
SizeReadWrite
DWORD ?
; numero de bytes actualmente para leer o escribir
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
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
;========================================================
;
Inicializacion de los miembros de la estructura 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
En la sección WM_CREATE, creamos el control de edición. Date cuenta que los parámetros que especifican x, y, anchura, altura del control son todos ceros ya que reajustaremos el tamaño del control despues para cubrir toda el area cliente de la ventana padre.
En este caso, no tenemos que llamar a ShowWindow para hacer que el control de edición aparezca en la pantalla porque hemos incluido el estilo WS_VISIBLE. Puedes usar este truco también en la ventana padre.
;==================================================
;
Inicializa los miembros de la estructura 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
Despues de crear el control de edición, tendremos que inicializar los miembros de ofn. Como queremos reciclar ofn en la caja de diálogo para guardar, pondremos solo los miembros *comunes* que son usados por ambos GetOpenFileName y GetSaveFileName.
La sección WM_CREATE es un lugar amplio para poner las incializaciones únicas (que se inicializan una sola vez).
.ELSEIF uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0ffffh
invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
Recibimos el mensaje WM_SIZE cuando el tamaño de nuestro área cliente en la ventana principal cambia. Tambien lo recibimos cuando la ventana es creada por primera vez. Para poder recibir el mensaje, el estilo de ventana debe incluir los estilos CS_VREDRAW y CS_HREDRAW. Usamos esta oportunidad para reajustar el tamaño de nuestro control de edición al mismo tamaño de nuestro área cliente de la ventana padre. Primero tenemos que saber la anchura y altura del área cliente de la ventana padre. Obtenemos esta información de lParam. La palabra alta [high word] de lParam contiene la altura y la palabra baja [low word] de lParam la anchura del area cliente. Entonces usamos la información para reajustar el tamaño del control de edición llamando a la función MoveWindow, que además de cambiar la posición de la ventana, permite cambiar su tamaño.
.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
Cuando el usuario selecciona el elemento de menú File/Open, rellenamos el miembro Flags de la estructura ofn y llamamos a la función GetOpenFileName para mostrar la caja de diálogo de abrir archivo.
.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
Después que el usuario ha seleccionado el archivo que desea abrir, llamamos a CreateFile para abrir el archivo. Hemos especificado que la función intentará abrir el archivo para lectura y escritura. Después de abrir el archivo, la función devuelve el manejador (handle) al archivo abierto que almacenamos en una variable global para futuros usos. Esta función es como sigue:
CreateFile
proto lpFileName:DWORD,\
dwDesiredAccess:DWORD,\
dwShareMode:DWORD,\
lpSecurityAttributes:DWORD,\
dwCreationDistribution:DWORD\,
dwFlagsAndAttributes:DWORD\,
hTemplateFile:DWORD
dwDesiredAccess especifica qué operación quieres que se haga en el archivo.
Cuando se abre el archivo, reservamos un bloque de memoria para usar con las funciones ReadFile y WriteFile. Especificamos el flag GMEM_MOVEABLE para dejar a Windows mover el bloque de memoria para consolidar la memoria. La bandera [flag] GMEM_ZEROINIT le dice a GlobalAlloc que rellene el nuevo bloque de memoria reservado con ceros.
Cuando GlobalAlloc vuelve satisfactoriamente, eax contiene el manejador (handle) al bloque de memoria reservado. Le pasamos este manejador (handle) a la función GlobalLock que nos devuelve un puntero al bloque de memoria.
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
Cuando el bloque de memoria esta listo para ser usado, llamamos a la función ReadFile para leer los datos del archivo. Cuando el archivo es abierto o creado por primera vez, el puntero del archivo esta en el deplazamiento [offset] 0. Así que en este caso, empezamos a leer el primer byte del archivo. El primer parámetro de ReadFile es el manejador (handle) del archivo a leer, el segundo es el puntero al bloque de memoria para contener los datos, el siguiente es el numero de bytes a leer del archivo, el cuarto parámetro es la direccion de la variable de tamaño DWORD que rellenaremos con el número de bytes realmente leídos del archivo.
Despues de rellenar el bloque de memoria con los datos, ponemos los datos en el control de edición mandando el mensaje WM_SETTEXT al control de edición con lParam conteniendo el puntero al bloque de memoria. Despues de esta llamada, el control de edición muestra los datos en el área cliente.
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
En este punto, no necesitamos tener el archivo abierto por más tiempo ya que nuestra intención es grabar los datos modificados en el control de edición en otro archivo, no el archivo original. Asi que cerramos el archivo llamando a CloseHandle con el manejador (handle) como su parámetro. Seguido desbloquearemos el bloque de memoria y lo liberamos. Actualmente no tienes que liberar la memoria en este punto, Puedes usar el bloque de memoria durante el proceso de grabación después. Pero como demostración, yo he elegido liberarla aquí.
invoke SetFocus,hwndEdit
Cuando la caja de diálogo "abrir archivo" es mostrada en la pantalla, el foco de entrada se centra en ella. Así que después de cerrar el diálogo de "abrir archivo", tendremos que mover el foco de entrada otra vez al control de edición.
Esto termina la operacion de lectura de archivo. En este punto, el usuario puede editar el contenido del control de edición. Y cuando quiera salvar los datos a otro archivo, deberá seleccionar File/Save en el menú que mostrará la caja de diálogo de salvar archivo. La creación de la caja de diálogo de salvar archivo no es muy diferente de la de abrir archivo. Efectivamente, se diferencian sólo en el nombre de la función, GetOpenFileName y GetSaveFileName. Puedes reciclar la mayoría de los miembros de la estructura ofn excepto el miembro Flags.
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
En nuestro caso, queremos crear un nuevo archivo, así que OFN_FILEMUSTEXIST y OFN_PATHMUSTEXIST deben ser dejados fuera, sino la caja de diálogo no nos dejara crear un archivo que no exista ya.
El parámetro dwCreationDistribution de la función CreateFile deberá ser cambiada a CREATE_NEW ya que queremos crear un nuevo archivo.
El resto del código es idéntico a todas las secciones de "abrir archivo" excepto las siguientes líneas:
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
Mandamos el
mensaje WM_GETTEXT al control de edición para copiar los datos del bloque
de memoria, el valor devuelto en eax es la longitud de los datos dentro del
buffer. Después de que los datos estén en el bloque de memoria,
los escribimos en un nuevo archivo.
[Iczelion's Win32 Assembly HomePage]
n u M I T_o r's Programming Page
Este tutorial, original de Iczelion, ha sido traducido por JoTaKe, y revisado por: n u M I T_o r