A Aldeia Numaboa ancestral ainda está disponível para visitação. É a versão mais antiga da Aldeia que eu não quis simplesmente descartar depois de mais de 10 milhões de pageviews. Como diz a Sirley, nossa cozinheira e filósofa de plantão: "Misericórdia, ai que dó!"

Se você tiver curiosidade, o endereço é numaboa.net.br.

Leia mais...

Informática Numaboa - Tutoriais e Programação

Dialog Box (masm)

Ter

21

Abr

2009


14:30

(3 votos, média 3.67 de 5) 


Nível intermediário

Uma dialog box como janela principal. Facilitando a vida com o uso de classes já definidas e gerenciamento automático. Adicionando componentes e economizando programação.

Uma caixa de diálogo nada mais é do que uma janela normal que foi desenhada para trabalhar com controles do tipo caixas de edição e botões. Os controles, por sua vez, são apenas janelas-filha. A classe de janelas dialog box foi criada para diminuir o trabalho dos programadores e oferece uma porção de facilidades tipo "prato pronto". O pessoal da microsoft chamou esta janela "especial" de CAIXA de diálogo (dialog box), mas eu prefiro o termo JANELA de diálogo. Neste tutorial usaremos uma janela de diálogo como janela principal.

Um pouco de teoria

O Windows possui um "gerente de janelas de diálogo" que administra a maior parte da lógica do teclado, como acionar o botão default quando a tecla [Enter] for pressionada ou mudar o foco de entrada quando a tecla [Tab] for pressionada. As janelas de diálogo são utilizadas principalmente como dispositivos de entrada e saída. Como o gerenciamento fica por conta do Windows, elas funcionam como um espécie de caixa preta: não precisamos saber como uma janela de diálogo funciona internamente, basta saber como interagir com ela. Caso você não saiba, este é um dos princípios da programação orientada a objetos, denominado ocultação de informações por encapsulamento. Se você acredita no gerente de diálogos... basta usar a dialog box.

Vamos às mordomias. Normalmente, se pusermos controles (janelas-filha) numa janela normal, precisamos produzir estas janelas-filha como subclasses da janela-mãe, ou seja, precisamos derivar as propriedades da janela-mãe. Além disto, toda a lógica do teclado fica por nossa conta. É muita mão de obra. É muito mais fácil e cômodo deixar o gerente do sistema trabalhar pra gente.

Janelas de diálogo são definidas como um recurso, da mesma maneira que menus. Podemos escrever um modelo com as características e os controles da janela de diálogo e depois compilar o código fonte do recurso com um editor de recursos. É claro que podemos produzir o código fonte "na unha", mas é um processo muito chato. Usando um editor de recursos podemos construir nossa janela de diálogo num ambiente visual - clicar e arrastar botões é mais fácil do que calcular suas coordenadas. Sugestões de editores de recurso você encontra no tutorial "Usando Recursos".

Existem dois tipos de janelas de diálogo: modal e não modal. Uma janela de diálogo não modal permite que o foco seja transferido para outra janela; janelas modais tratam o foco de forma especial. Existem dois subtipos da janela de diálogo modal: modal de aplicativo e modal de sistema. Uma modal de aplicativo não permite que se mude o foco para outra janela do mesmo aplicativo, mas permite que se mude o foco para outra janela de OUTRO aplicativo. Uma modal de sistema nunca permite a mudança de foco - ela precisa ser fechada antes.

Os recursos da janela de diálogo

O código fonte do arquivo de recursos do nosso exemplo contém o modelo de janela de diálogo e um pequeno menu:

#include "resource.h" #define IDC_EDIT 3000 #define IDC_BUTTON 3001 #define IDC_EXIT 3002 #define MenuDaqui 3003 #define IDM_pegar 32000 #define IDM_limpar 32001 #define IDM_sair 32002 DLGNumaBoa DIALOG 10, 10, 205, 48 STYLE DS_MODALFRAME | 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | WS_3DLOOK CAPTION "Testando o Gerente" MENU MenuDaqui BEGIN EDITTEXT IDC_EDIT, 15,17,111,13, ES_AUTOHSCROLL | ES_LEFT DEFPUSHBUTTON "Diga &Oi", IDC_BUTTON, 141,10,52,13 PUSHBUTTON "&Sair", IDC_EXIT, 141,26,52,13 END MenuDaqui MENU BEGIN POPUP "Testar Controles" BEGIN MENUITEM "Pegar Texto", mnID_pegar MENUITEM "Limpar Texto", mnID_limpar MENUITEM SEPARATOR MENUITEM "&Sair", mnID_sair END END

Foi usado um #include referenciando o arquivo resource.h. Este arquivo contém as definições necessárias para dar suporte às constantes nominadas como WS_SYSMENU, WS_VISIBLE, etc. Em relação ao modelo da janela de diálogo:

  • O nome da classe: DLGNumaBoa. Palavra chave que identifica o tipo da janela: DIALOG. Medidas em unidades dialog box (não são pixels): 10,10,205,48
  • Palavra chave para indicar o estilo: STYLE. Composição com OR (|) de todos os estilos desejados: DS_MODALFRAME | ...
  • Texto da barra de título: CAPTION "Testando o Gerente".
  • O menu da janela, definido no mesmo arquivo de recursos: MENU MenuDaqui
  • Início da definição dos controles: BEGIN.
  • Caixa de Edição: EDITTEXT Identificador, Medidas, Opções
  • Botão default: DEFPUSHBUTTON Texto no botão, Identificador, Medidas
  • Botão: PUSHBUTTON Texto no Botão, Identificador, Medidas
  • Fim da definição de controles: END

Não se esqueça de que o arquivo de recursos precisa ser compilado para poder ser incorporado ao executável! Note que o modelo da janela de diálogo possui um menu, o MenuDaqui, que também precisa ser definido no mesmo arquivo de recursos. Se tiver dúvidas quanto ao modelo do menu, reveja o tutorial "Criando menus em Assembly".


Criando a janela dialog box

Quando criamos a janela "tradicional", logo após obtermos o manipulador da instância, chamamos o gerente da Janela (o procedimento gerenteJanela) que, entre outras tarefas, se encarrega de criar a janela principal. Acontece que sabemos que a dialog box possui um gerente próprio, que fica de plantão para nos atender. Basta requisitar seus serviços através de CreateDialogParam (se quisermos uma janela não modal) ou de DialogBoxParam (se quisermos uma janela modal). No caso, como queremos uma janela modal, só precisamos substituir a linha invoke gerenteJanela do modelo usado até agora por invoke DialogBoxParam:

HWND CreateDialogParam( HINSTANCE hInstance, LPCTSTR lpTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam ); INT_PTR DialogBoxParam( HINSTANCE hInstance, LPCTSTR lpTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam ); ... .DATA NomeClasse db "DLGNumaBoa",0 // precisa ser o nome dado no recurso .CODE inicio: invoke GetModuleHandle, NULL mov mInstancia, eax invoke DialogBoxParam, mInstancia, ADDR NomeClasse, NULL, ADDR gerenteMensagem, NULL invoke ExitProcess,0 end inicio

Gerenciando as mensagens recebidas

Como não fomos nós que definimos a classe DLGNumaBoa, e sim o gerente de diálogos, precisamos apresentar-lhe nosso gerente de mensagens - que ele não conhece porque é "nosso funcionário". Nós indicamos gerenteMensagem na função DialogBoxParam e precisamos "entregar a ficha" do gerente de mensagens ao gerente de diálogos. Fazemos isto entregando o protótipo da função:

... includelib \masm32\lib\kernel32.lib gerenteMensagem proto :DWORD, :DWORD, :DWORD, :DWORD ...

Determinando o controle com o foco inicial

Assim que a janela de diálogo for mostrada na tela, queremos que a caixa de edição tenha o foco. Nosso objetivo pode ser alcançado se interceptarmos a mensagem WM_INITDIALOG, que é a "prima próxima" da mensagem WM_CREATE da janela "comum". Como pretendemos alterar uma das propriedades da caixa de edição, é preciso fornecer ao gerente de mensagens o identificador (ID) da mesma. Este identificador, como todos os dos outros controles da janela de diálogo, é o definido no arquivo de recursos. Para obter o manipulador da caixa de edição basta chamar a função GetDlgItem com o identificador do controle e, para dirigir o foco para um controle, basta chamar a função SetFocus com o manipulador do controle:

HWND GetDlgItem( HWND hDlg, // manipulador da janela de diálogo int nIDDlgItem // identificador do controle ); ... .CONST IDC_EDIT equ 3000 IDC_BUTTON equ 3001 IDC_EXIT equ 3002 IDM_pegar equ 32000 IDM_limpar equ 32001 IDM_sair equ 32002 ... gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_INITDIALOG invoke GetDlgItem, hWnd, IDC_EDIT invoke SetFocus, eax .ELSEIF ...

Gerenciando os itens do menu

A mensagem WM_COMMAND é enviada quando o usuário seleciona um item de comando do menu, quando um controle envia uma mensagem de notificação para a janela-mãe ou quando uma tecla aceleradora é traduzida. O formato da mensagem WM_COMMAND é:

WM_COMMAND wNotifyCode = HIWORD(wParam); // código de notificação wID = LOWORD(wParam); // identificador do item, controle ou acelerador hwndCtl = (HWND) lParam; // manipulador do controle

O parâmetro wParam é um valor de 32 bits, ou seja, possui 4 bytes de 8 bits. Os dois bytes mais à esquerda formam o word mais significativo (high word) e os dois à direita formam o word menos significativo (low word). Tratando os words em separado, o parâmetro wParam contém dois valores: o código de notificação e o identificador do item, controle ou tecla aceleradora. Veja abaixo para entender melhor ou dê uma olhada no texto de apoio "O que são registradores" e "Assembly e Registradores".

| 31 a 24          23 a 16     |     15 a 8      |      7 a 0      |
|                              |    byte mais    |    byte menos   |
|                              |  significativo  |   significativo |
| .. word mais significativo ..|                                   |
|   (código de notificação)    | .... word menos significativo ... |
| (identificador do controle)  |                                   |
|                                                                  |
| .......................... DWORD ............................... |
  • wNotifyCode: word mais significativo de wParam. Contém o código de notificação se a mensagem for de um controle. É 1 se a mensagem for de um acelerador e 0 (zero) se a mensagem for de um menu.
  • wID: word menos significativo de wParam. Contém o identificador do item de menu, controle ou acelerador.
  • hwndCtl: valor de lParam. Identifica o controle remetente se a mensagem for de um controle. Caso contrário, o valor é NULL.

De posse destas informações, sabemos que uma mensagem veio de um item de menu somente quando lParam de uma mensagem WM_COMMAND for igual a NULL. Vai daí que... precisamos interceptar a mensagem WM_COMMAND, testar se o parâmetro lParam é NULL e testar cada um dos itens do menu. O identificador do item do menu se encontra no word menos significativo de EAX, ocupando os primeiros 16 bits, por isso usamos AX (e não EAX).

... gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_INITDIALOG invoke GetDlgItem, hWnd, IDC_EDIT invoke SetFocus, eax .ELSEIF uMsg == WM_COMMAND mov eax, wParam .IF lParam == NULL .IF ax == IDM_pegar ...
Item do menu "Pegar Texto"

Quando o item selecionado for "Pegar Texto", cujo ID é IDM_pegar, queremos que o texto que esteja na caixa de edição seja mostrado numa caixa de mensagem. Para obter o texto da caixa de edição utilizamos a função GetDlgItemText, para mostrar o texto usamos a função MessageBox. A última você já conhece do tutorial "Message Box".

UINT GetDlgItemText( HWND hDlg, // manipulador da janela de diálogo int nIDDlgItem, // identificador do controle LPTSTR lpString, // endereço do buffer para o texto int nMaxCount // tamanho máximo da string ); int MessageBox( HWND hWnd, // manipulador da janela proprietária LPCTSTR lpText, // endereço do texto da message box LPCTSTR lpCaption, // endereço do título da message box UINT uType // estilo da message box );

Note que vamos precisar de um buffer para armazenar o texto da caixa de edição, portanto vamos declará-lo na seção de dados não inicializados. Também vamos precisar do título da message box. Usaremos TituloJanela e modificamos apenas o texto:

... .DATA NomeClasse db "DLGNumaBoa",0 TituloJanela db "O que o gerente fez",0 .DATA? mInstancia HTINSTANCE ? buffer db 512 dip(?) ... gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_INITDIALOG invoke GetDlgItem, hWnd, IDC_EDIT invoke SetFocus, eax .ELSEIF uMsg == WM_COMMAND mov eax, wParam .IF lParam == NULL .IF ax == IDM_pegar invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512 invoke MessageBox, hWnd, ADDR buffer, ADDR TituloJanela, MB_OK ... .ENDIF
Item do menu "Limpar Texto"

Quando o item selecionado for "Limpar Texto", cujo ID é IDM_limpar, queremos apenas eliminar qualquer texto que possa estar na caixa de edição. Se trocarmos o texto existente por NULL, limpamos a caixa. Usamos a função SetDlgItemText:

BOOL SetDlgItemText( HWND hDlg, // manipulador da janela de diálogo int nIDDlgItem, // identificador do controle LPCTSTR lpString // texto a ser incluído ); .IF lParam == NULL .IF ax == IDM_pegar invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512 invoke MessageBox, hWnd, ADDR buffer, ADDR TituloJanela, MB_OK .ELSEIF ax == IDM_pegar invoke SetDlgItemText, hWnd, IDC_EDIT, NULL ... .ENDIF
Item do menu "Sair"

O identificador do item de menu "Sair" é IDM_sair. Quando for selecionado, queremos que uma caixa de mensagem diga "tchauzinho" e a janela de diálogo seja fechada. Precisamos inicializar a string do "tchauzinho" e utilizar a função EndDialog que destrói uma janela de diálogo modal, ou seja, dispensa o gerente de diálogo.

BOOL EndDialog( HWND hDlg, // manipulador da janela de diálogo int nResult // valor de retorno ); ... .DATA NomeClasse db "DLGNumaBoa",0 TituloJanela db "O que o gerente fez",0 Adeus db "Tchauzinho",0 ... .IF lParam == NULL .IF ax == IDM_pegar invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512 invoke MessageBox, hWnd, ADDR buffer, ADDR TituloJanela, MB_OK .ELSEIF ax == IDM_pegar invoke SetDlgItemText, hWnd, IDC_EDIT, NULL .ELSEIF ax == IDM_sair invoke MessageBox, hWnd, ADDR Adeus, ADDR TituloJanela, MB_OK invoke EndDialog, hWnd, NULL .ENDIF ...

Gerenciando o fechamento da janela

Já que o fechamento da janela é possível de ser obtido através do item de manu "Sair", podemos aproveitar o código para fechar a janela quando o usuário clica no canto superior direito da janela no botãozinho [x]. Este botãozinho envia uma mensagem do tipo WM_CLOSE:

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_INITDIALOG invoke GetDlgItem, hWnd, IDC_EDIT invoke SetFocus, eax .IF uMsg == WM_CLOSE invoke SendMessage, hWnd, WM_COMMAND, IDM_sair, 0 .ELSEIF uMsg == WM_COMMAND ...

Gerenciando os botões

Vimos acima que o parâmetro lParam identifica o remetente se a mensagem for de um controle. Caso contrário, o valor é NULL. Nossos botões são controles, então, as mensagens enviadas por eles terão lParam diferente de NULL. Acontece que precisamos do código de notificação dos botões para testar o evento (clique, duplo clique, etc) e este código se encontra no word mais significativo de wParam. Já vimos que, para obter o word menos significativo, basta transferir o valor de 32 bits para um registrador (EAX, por exemplo) e trabalhar com os primeiros 16 bits usando "meio" registrador (AX, por exemplo). Para acessar os 16 bits mais significativos precisamos usar um expediente: deslocar os bits da esquerda para a direita em 16 posições. A instrução que produz este deslocamento é a SHR (shift right - deslocamento para a direita). Usaremos o registrador EDX para efetuar o deslocamento e depois utilizaremos DX para obter o código de notificação (de 16 bits).

Quando o botão com "Diga Oi" for acionado, queremos que o texto da caixa de edição seja preenchido com "Viu só? O gerente funcionou" que, claro, precisa ser declarado e inicializado na seção .DATA. Quando o botão "Sair" for acionado, aparece a caixa de mensagem com "Tchauzinho" e a janela de diálogo é fechada.

... .DATA NomeClasse db "DLGNumaBoa",0 TituloJanela db "O que o gerente fez",0 Adeus db "Tchauzinho",0 StringTeste db "Viu só? O gerente funcionou",0 ... .ELSEIF uMsg == WM_COMMAND mov eax, wParam .IF lParam == NULL .IF ax == IDM_pegar invoke GetDlgItemText, hWnd, IDC_EDIT, ADDR buffer, 512 invoke MessageBox, hWnd, ADDR buffer, ADDR TituloJanela, MB_OK .ELSEIF ax == IDM_pegar invoke SetDlgItemText, hWnd, IDC_EDIT, NULL .ELSEIF ax == IDM_sair invoke MessageBox, hWnd, ADDR Adeus, ADDR TituloJanela, MB_OK invoke EndDialog, hWnd, NULL .ENDIF .ELSE mov edx, wParam shr edx, 16 .IF dx == BN_CLICKED // botão clicado? .IF ax == IDC_BUTTON invoke SetDlgItemText, hWnd, IDC_EDIT, ADDR StringTeste .ELSEIF ax == IDC_EXIT invoke SendMessage, hWnd, WM_COMMAND, IDM_sair, 0 .ENDIF .ENDIF .ENDIF ...

O valor de retorno do gerente de mensagens de uma janela de diálogo

Você viu na rotina do gerente de mensagens de uma janela "normal", quando nenhuma mensagem é interceptada, é preciso redirigir a mensagem para o procedimento padrão do sistema com invoke DefWindowProc, hWnd, uMsg, wParam, lParam. Numa janela de diálogo, em princípio, é preciso fazer a mesma coisa - chamar o procedimento padrão. A diferença é a seguinte: não substituímos o gerente de diálogos, apenas o contratamos. Ele está "de serviço" e não precisamos chamá-lo através de uma função. É suficiente indicar para ele se nós já processamos a mensagem ou não, e isso é possível fazer com TRUE (verdadeiro) ou FALSE (false). Também sabemos que o registrador oficial para valores de retorno é o EAX. Então EAX = TRUE significa que processamos a mensagem; EAX = FALSE significa que a mensagem não foi processada e o gerente precisa tomar as devidas providências.

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg == WM_INITDIALOG ... .ELSEIF uMsg == WM_CLOSE ... .ELSEIF uMsg == WM_COMMAND ... .ELSE mov eax, FALSE ret .ENDIF mov eax, TRUE ret gerenteMensagem endp

Finalmentes

Mais um tutorial do tamanho de um bonde! Mas é assim mesmo. Toda vez que se discute um assunto em detalhes, a conversa fica longa. Escreva e compile seu código. O resultado obtido deve ser este:

Diálogo

Como sempre, você pode fazer o download do tutorial ou visitar a seção de downloads / tutoriais / assembly numaboa para baixar a coleção completa.

Abraços a todos da vó Vicki vovo

игровой автомат resident резидент онлайн бесплатноnikasrestaurant.comресторан никас фото yeellafb consultkontur-yamal.ru сайты отзывов

Informações adicionais