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

Janelas (masm)

Dom

7

Jan

2007


10:34

(12 votos, média 4.83 de 5) 


Solicitando e enviando mensagens

O gerente da janela solicita o envio de um "malote" com uma mensagem através da função GetMessage:

BOOL GetMessage(
LPMSG lpMsg, // endereço da estrutura com a mensagem
HWND hWnd, // manipulador da janela
UINT wMsgFilterMin, // primeira messagem
UINT wMsgFilterMax // última messagem
);

Existem algumas mensagens que chegam à "agência de correio" com características especiais. São como cartas registradas ou SEDEX: não podem ser entregues via caixa postal ou malote. Se usarmos o manipulador da janela (no exemplo, mJanela) como parâmetro hWnd da função GetMessage, apenas as "cartas normais" serão entregues. Se quisermos obter todas as mensagens, as "normais" E as "especiais", passamos este parâmetro como NULL.

Se não quisermos selecionar nossa correspondência (não queremos filtrar mensagens), passamos os parâmetros wMsgFilterMin e wMsgFilterMax como zero e a "agência" entregará TODAS as mensagens.

Se a função mandar um "malote" ao gerente de mensagens contendo a mensagem WM_QUIT, o valor de retorno será zero; caso contrário, o valor de retorno será diferente de zero. Se ocorrer um erro, o valor de retorno será -1 (por exemplo, quando o manipulador da janela não for válido).

Como já foi dito anteriormente, o Windows controla todos os eventos referentes a todas as janelas, inclusive os relativos à janela que acabamos de criar: a janela foi ativada, o cursor do mouse passou sobre ela, clique do mouse, tecla pressionada, janela minimizada, maximizada... tudo e mais alguma coisa gera uma mensagem. Dê uma olhada na referência da API do Windows e procure por WM_ (vem de Windows Message). Existe uma quantidade enorme de tipos de mensagens.

Caso a mensagem seja referente a uma tecla que foi pressionada, ela vem com um código de varredura do teclado. É muito chato trabalhar com estes códigos - mais fácil é trabalhar com o ASCII da tecla. Existe uma função que transforma uma mensagem WM_KEYDOWN (código de varredura) em uma mensagem WM_CHAR (código ASCII). É a função TranslateMessage:

BOOL TranslateMessage(
CONST MSG *lpMsg // endereço da estrutura com a mensagem (o "malote")
);

Para o gerente da janela mandar a mensagem ao gerente de mensagens, usamos a função DispatchMessage:

LONG DispatchMessage(
CONST MSG *lpmsg // endereço da estrutura com a mensagem (o "malote")
);

Loop infinito

Bem, então vamos ao trabalho. Queremos que nosso gerente de janela, depois de tudo que já fez, fique o tempo todo solicitando, conferindo e mandando suas mensagens para o gerente de mensagens. Para isto faremos uso de macros de pseudo alto nível do MASM: .WHILE, .BREAK e .IF

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
LOCAL mJanela:HWND
LOCAL malote:MSG
...

invoke RegisterClassEx, ADDR ej
invoke CreateWindowEx, NULL, ADDR NomeClasse, ADDR TituloJanela,
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, mInst, NULL
mov mJanela,eax
invoke ShowWindow, mJanela, SW_SHOWNORMAL
invoke UpdateWindow, mJanela
.WHILE TRUE
invoke GetMessage, ADDR malote, NULL, 0, 0
.BREAK .IF (eax < 1)
invoke TranslateMessage, ADDR malote
invoke DispatchMessage, ADDR malote
.ENDW

ret
gerenteJanela endp

Trocando em miúdos, este trecho de código diz o seguinte:

enquanto (.WHILE) verdadeiro (TRUE)
chame MandeMessagem, pelo malote, mensagens normais e especiais, sem filtro, sem filtro
páre (.BREAK) se (.IF) o valor de retorno for menor que 1
chame TraduzaMensagem, do malote
chame DespacheMensagem, do malote
fim do enquanto (.ENDW)

Este loop infinito só é interrompido quando o valor de retorno de GetMessage for 0 ou -1 (ou seja, menor que 1). Quando é 0, a mensagem recebida foi WM_QUIT e o programa deve ser encerrado. O valor -1 indica erro e o melhor é... também terminar ao invés de dar pau.

Saindo do loop, o gerente da janela ainda deve realizar uma tarefa antes de devolver o controle ao corpo principal do código: deve armazenar o código de saída em eax para devolvê-lo ao Windows. Até o presente momento, o Windows não faz uso deste valor de retorno, mas é melhor agir com segurança e jogar conforme a regra:

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
...

.WHILE TRUE
invoke GetMessage, ADDR malote, NULL, 0, 0
.BREAK .IF (eax < 1)
invoke TranslateMessage, ADDR malote
invoke DispatchMessage, ADDR malote
.ENDW
mov eax, malote.wParam
ret
gerenteJanela endp

Agora o gerente da janela está com todo o seu esquema de trabalho definido. Uma de suas funções é enviar as mensagens recebidas para o gerente de mensagens para que sejam processadas. Então, vamos para a última função...

Processando mensagens recebidas

Agora é a vez do nosso window procedure - o gerente de mensagens. Você pode dar a ele o nome que quiser - não precisa chamá-lo de WindowProc:

LRESULT CALLBACK WindowProc(
HWND hwnd, // manipulador da janela
UINT uMsg, // identificador da mensagem
WPARAM wParam,
LPARAM lParam
);

O primeiro parâmetro, hWnd, é o manipulador da janela para a qual a mensagem é destinada. uMsg é a mensagem. Observe que uMsg NÃO é uma estrutura MSG, é apenas um número identificador. O Windows define centenas de mensagens, a maioria sem maior interesse para o nosso programa, e irá enviar uma mensagem apropriada apenas quando ocorrer um fato relevante para a nossa janela. wParam e lParam são apenas parâmetros extras usados por algumas mensagens que enviam dados acessórios.

No nosso exemplo, vamos chamar o WindowProc de gerenteMensagem:

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
...
gerenteMensagem endp

O gerente de mensagens precisa checar cada mensagem recebida do Windows para verificar se é de interesse. Se for de interesse, deve dar a resposta adequada; se não for, PRECISA chamar DefWindowProc, passando todos os parâmetros que recebeu, para que o processamento default seja efetuado. Esta DefWindowProc é uma função da API que processa as todas as mensagens que não são do interesse do seu programa.

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret

gerenteMensagem endp

Encerrando o programa

A única mensagem que SEMPRE PRECISA ser respondida é a WM_DESTROY. Esta mensagem é enviada toda vez a janela for fechada - é o sinal de final de expediente. No momento em que esta mensagem é recebida, a janela já foi removida da tela. Esta é apenas uma notificação de que a janela foi destruída e que os "gerentes" devem encerrar o expediente e se liberar do controle do Windows. Ainda há tempo de fazer alguma "ordem no escritório" mas, quando se chegou neste ponto, não há outra opção a não ser terminar. Se você quiser ter uma chance de impedir que o usuário feche a janela, você deve processar a mensagem WM_CLOSE.

gerenteMensagem proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY
invoke PostQuitMessage, NULL
xor eax,eax
ret
.ENDIF

invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
gerenteMensagem endp

Bem, voltando à WM_DESTROY... após a eventual "ordem no escritório", o gerente de mensagens precisa chamar PostQuitMessage (a qual remeterá uma mensagem WM_QUIT), zerar o valor do registrador EAX com xor eax,eax ou mov eax,0 e retornar (ret) para o gerente da janela. O gerente da janela vai chamar GetMessage pela última vez, obtendo a mensagem WM_QUIT que acabou de ser enviada pelo gerente de mensagens. GetMessage retorna o valor zero em eax quando recebe WM_QUIT o que, por seu lado, encerra o loop infinito. O gerente da janela se despede enviando o malote.wParam para o Windows e o fluxo do programa volta para o corpo principal para executar a saída do processo chamando ExitProcess.

Informações adicionais