Tutorial 11: Mas sobre las Caja de Diálogo [Dialog Box]

En este tutorial vamos a aprender mas sobre las cajas de diálogo [dialog box]. Especificamente, vamos a explorar la manera de como usar cajas de diálogo como nuestra entrada-salida de datos. Si leíste el tutorial anterior, este te va a resultar ligero. Encontrarás algunos cambios menores, es todo lo que necesitamos para poder usar cajas de diálogo adjuntas a nuestra ventana principal. En este tutorial también aprenderemos como usar cajas de diálogo comunes.

Bájate los ejemplos de cajas de diálogo aquí y aquí. Bajate el ejemplo de una cajas de diálogo común aquí.

 

Teoría:

Hay muy poco que decir sobre como usar las cajas de diálogo como entrada-salida de nuestro programa. Tu programa crea la página principal normalmente y cuando quieres mostrar la cajas de diálogo, llamas a CreateDialogParam o DialogBoxParam. Con la llamada a DialogBoxParam, no tendrás que hacer nada mas, sólo procesar los mensajes en el procedimiento de la cajas de diálogo. Con CreateDialogParam, tendrás que insertar la llamada a IsDialogMessage en el bucle de mensajes para dejar a la cajas de diálogo el control sobre la navegación del teclado en tu cajas de diálogo. Como los dos casos son diferentes, no pondré el codigo fuente aquí. Puedes bajarte los ejemplos y examinarlos tu mismo, aquí y aquí.

Comencemos con las cajas de diálogo comunes. Windows tiene preperadas unas cajas de diálogo predefinidas que pueden ser usadas por tus aplicaciones. Estas cajas de diálogo existen para proveer un interfaz estandard de usuario. Consisten en cajas de diálogo de archivo, impresión, color, fuente, y busqueda. Deberías usarlas lo máximo posible. Las cajas de diálogo residen en comdlg32.dll. Para usarlas, tendrás que enlazar [link] el archivo comdlg32.lib. Creas estas cajas de diálogo llamando a la función apropiada en la librería de las cajas de diálogo. Para el archivo de diálogo "Abrir" [Open], se emplea la función GetOpenFileName, para la caja de diálgo "Guardar" [Save] GetSaveFileName, para dibujar un diálogo es PrintDlg y ya está. Cada una de estas funciones toma como parámetro un puntero a la estructura. Deberás mirarlo en la referencia de la API de Win32. En este tutorial, demostraré como crear y usar un diálogo "Abrir archivo" [Open file].

Debajo está la el prototipo de la función GetOpenFileName:
 

GetOpenFileName proto lpofn:DWORD

Puedes ver que sólo recibe un parámetro, un puntero a la estructura OPENFILENAME. El valor devuelto es TRUE que significa que el usuario a seleccionado un archivo para abrir, de otra manera devolverá FALSE. Lo siguiente que veremos será la estructura OPENFILENAME.
 

OPENFILENAME  STRUCT

 lStructSize DWORD  ?
 hwndOwner HWND  ?
 hInstance HINSTANCE ?
 lpstrFilter LPCSTR  ?
 lpstrCustomFilter LPSTR  ?
 nMaxCustFilter DWORD  ?
 nFilterIndex DWORD  ?
 lpstrFile LPSTR  ?
 nMaxFile DWORD  ?
 lpstrFileTitle LPSTR  ?
 nMaxFileTitle DWORD  ?
 lpstrInitialDir LPCSTR  ?
 lpstrTitle LPCSTR  ?
 Flags  DWORD  ?
 nFileOffset WORD  ?
 nFileExtension WORD  ?
 lpstrDefExt LPCSTR  ?
 lCustData LPARAM  ?
 lpfnHook DWORD  ?
 lpTemplateName LPCSTR  ?

OPENFILENAME  ENDS

Vamos a ver el significado de los miembros mas utilizados de la estructura.
 

lStructSize El tamaño de la estructura OPENFILENAME, en bytes
hwndOwner El manejador (handle) de la caja de diálogo "open file".
hInstance Manejador (handle) de la instancia de la aplicación que crea la caja de diálogo "open file".
lpstrFilter Las cadenas de filtro en formato de pares de cadenas terninadas en null (0). La primera entre las dos es la de descripción. La segunda cadena es el patrón de filtro. por ejemplo: 
     FilterString   db "All Files (*.*)",0, "*.*",0 
                        db "Text Files (*.txt)",0,"*.txt",0,0 
Date cuenta que sólo el patrón en la  segunda cadena de este par es usado actualmente por Windows para filtrar los archivos. Tambien date cuenta de que tienes que poner un 0 extra al final de la cadena de filtro para advertir el final de ésta.
nFilterIndex Especifica que par de cadenas de filtro que serán usadas inicialmente cuando la caja de diálogo "open file" es mostrada por primera vez. El índice es de base 1, que es el primer par 1, el segundo par es 2 y así el resto. En el ejemplo de arriba, si especificamos nFilterIndex como 2, será usado el segundo patrón, "*.txt".
lpstrFile Puntero al buffer que contiene el nombre del archivo usado para inicializar el control de edición de nombre de archivo en la caja de diálogo. El buffer será como mínimo de 260 bytes de largo. Después de que el usuario seleccione el archivo a abrir, el nombre de archivo con toda la dirección (path) es almacenado en este buffer. Puedes extraer la información de aquí mas tarde.
nMaxFile El tamaño del buffer lpstrFile.
lpstrTitle Puntero al encabezamiento o título de la caja de diálogo "open file"
Flags Determina el estilo y características de la caja de diálogo.
nFileOffset después de que el usuario haya seleccionado el archivo a abrir, este miembro contiene el índice al primer caracter del nombre de archivo actual. Por ejemplo, si el nombre completo con el directorio (path) es "c:\windows\system\lz32.dll", este miembro contendrá el valor 18.
nFileExtension después de que el usuario seleccione el archivo a abrir, este miembro contiene el índice al primer caracter de la extensión del archivo

 

Ejemplo:

El siguiente programa muestra una caja de diálogo de abrir archivo cuando el usuario seleccione File-> Open del menu. Cuando el usuario seleccione un archivo en la caja de diálogo, el programa muestra una caja de mensaje mostrando el nombre completo, nombre de archivo, y extensión del archivo seleccionado.

.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_EXIT equ 2
MAXSIZE equ 260
OUTPUTSIZE equ 512

.data
ClassName db "SimpleWinClass",0
AppName  db "Our Main Window",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)
OurTitle db "-=Our First Open File Dialog Box=-: Choose the file to open",0
FullPathName db "The Full Filename with Path is: ",0
FullName  db "The Filename is: ",0
ExtensionName db "The Extension is: ",0
OutputString db OUTPUTSIZE dup(0)
CrLf db 0Dh,0Ah,0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?

.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:DWORD
    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 hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .IF uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax,wParam
        .if ax==IDM_OPEN
            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
            mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                OFN_EXPLORER or OFN_HIDEREADONLY
            mov  ofn.lpstrTitle, OFFSET OurTitle
            invoke GetOpenFileName, ADDR ofn
            .if eax==TRUE
                invoke lstrcat,offset OutputString,OFFSET FullPathName
                invoke lstrcat,offset OutputString,ofn.lpstrFile
                invoke lstrcat,offset OutputString,offset CrLf
                invoke lstrcat,offset OutputString,offset FullName
                mov  eax,ofn.lpstrFile
                push ebx
                xor  ebx,ebx
                mov  bx,ofn.nFileOffset
                add  eax,ebx
                pop  ebx
                invoke lstrcat,offset OutputString,eax
                invoke lstrcat,offset OutputString,offset CrLf
                invoke lstrcat,offset OutputString,offset ExtensionName
                mov  eax,ofn.lpstrFile
                push ebx
                xor ebx,ebx
                mov  bx,ofn.nFileExtension
                add eax,ebx
                pop ebx
                invoke lstrcat,offset OutputString,eax
                invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
                invoke RtlZeroMemory,offset OutputString,OUTPUTSIZE
            .endif
        .else
            invoke DestroyWindow, hWnd
        .endif
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp
 end start


Análisis:

            mov ofn.lStructSize,SIZEOF ofn
            push hWnd
            pop  ofn.hwndOwner
            push hInstance
            pop  ofn.hInstance

Rellenamos en la rutina los miembros de la estructura ofn.

            mov  ofn.lpstrFilter, OFFSET FilterString

Este FilterString es el filtro para el nombre de archivo que especificamos como sigue:

FilterString db "All Files",0,"*.*",0
                   db "Text Files",0,"*.txt",0,0

Date cuenta que las cuatro cadenas terminan en 0. La primera cadena es la descripción de la siguiente cadena. El actual patrón es la cadena par, en este caso, "*.*" y "*.txt". Actualmente podemos especificar aquí cualquier patrón que queramos. DEBEMOS poner un cero extra después de la última cadena de patrón para denotar el final de la cadena de filtro. No olvides esto sino tu caja de diálogo funcionará de forma extraña.

            mov  ofn.lpstrFile, OFFSET buffer
            mov  ofn.nMaxFile,MAXSIZE

Especificamos dónde la caja de diálogo pondrá el nombre del archivo que ha seleccionado el usuario. Date cuenta que tendremos que especificar su tamaño en el miembro nMaxFile. Podemos extraer más tarde el nombre del archivo de este buffer.

            mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                OFN_EXPLORER or OFN_HIDEREADONLY

Flags especifica  las características de la caja de diálogo.

Las banderas [flags] OFN_FILEMUSTEXIST  y OFN_PATHMUSTEXIST demandan que el nombre de archivo y dirección (path) que el usuario pone en el control de edición (edit control) DEBEN existir.
La bandera OFN_LONGNAMES dice a la caja de diálogo que muestre nombres largos de archivo.
La bandera OFN_EXPLORER especifica que la apariencia de la caja de diálogo debe ser parecida a la del explorador.
La bandera OFN_HIDEREADONLY oculta el cuadro de selección de solo-lectura en la caja de diálogo.
Hay muchas banderas más que puedes usar. Consulta tu referencia de la API de Win32.

            mov  ofn.lpstrTitle, OFFSET OurTitle

Especifica el titulo o encabezado [caption] de la caja de diálogo.

            invoke GetOpenFileName, ADDR ofn

Llamada a la función GetOpenFileName. Pasando el puntero a la estructura ofn como parámetro.
En este momento, la caja de diálogo de abrir archivo es mostrada en la pantalla. La función no volverá hasta que el usuario seleccione un archivo para abrir o presione el boton de cancelar o cierre la caja de diálogo. Devolverá el valor TRUE en eax si el usuario selecciona un archivo para abrir. Sino devolverá FALSE en cualquier otro caso.

            .if eax==TRUE
                invoke lstrcat,offset OutputString,OFFSET FullPathName
                invoke lstrcat,offset OutputString,ofn.lpstrFile
                invoke lstrcat,offset OutputString,offset CrLf
                invoke lstrcat,offset OutputString,offset FullName

En el caso que el usuario seleccione un archivo para abrir, preparamos la cadena de salida que se mostrará en la caja de mensajes. Ubicamos un bloque de memoria en la variable OutputString y entonces usamos la función de la API, lstrcat, para entrelazar las cadenas siempre. Para poner las cadenas en varias líneas, debemos separar cada línea con el par de caracteres que alimentan el retorno de carro (13d o 0Dh) y el avance de línea (10d o 0Ah).

                mov  eax,ofn.lpstrFile
                push ebx
                xor  ebx,ebx
                mov  bx,ofn.nFileOffset
                add  eax,ebx
                pop  ebx
                invoke lstrcat,offset OutputString,eax

Las líneas de arriba requieren una explicación. nFileOffset contiene el índice dentro de ofn.lpstrFile. Pero no puedes añadirlo directamente porque nFileOffset es una variable de tamaño WORD y lpstrFile es de tamaño DWORD. Así que tendré que poner el valor de nFileOffset en la palabra baja [low word] de ebx y sumárselo al valor de lpstrFile.

                invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK

Mostramos la cadena en la caja de mensajes.

                invoke RtlZerolMemory,offset OutputString,OUTPUTSIZE

Debemos *limpiar* el OutputString antes de poder meterle cualquier otra cadena. Así que usamos la función RtlZeroMemory para hacer este trabajo.


Índice

Siguiente

[Iczelion's Win32 Assembly Homepage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por   t x a b o   y revisado por  n u M I T_o r

www.000webhost.com