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) 


Declaração

Como só vamos precisar desta estrutura no procedimento da função gerenteJanela, vamos declará-la como variável LOCAL com o nome de ej (de estrutura janela - ou qualquer outro da sua escolha). A diretiva LOCAL aloca memória da pilha para esta variável e precisa estar situada imediatamente após a diretiva PROC. A sintaxe é LOCAL :. Vamos usar LOCAL ej:WNDCLASSEX, que pede ao MASM para alocar uma quantidade de memória de pilha correspondente ao tamanho da estrutura WNDCLASSEX para a variável de nome ej. A vantagem é que podemos referenciar ej no nosso código sem nos preocuparmos com o realinhamento da pilha, o que é mordomia pura. Uma desvantagem é que variáveis locais não podem ser usadas fora das funções onde foram criadas e que serão imediatamente destruídas quando se retorna ao chamador. Outra desvantagem é que variáveis locais não podem ser inicializadas automaticamente porque elas são apenas memória de pilha alocada dinamicamente na entrada da função. Precisamos atribuir seus valores manualmente após a diretiva LOCAL.

Entre os membros da estrutura encontramos o que deve conter o ponteiro para o nome da classe que desejamos criar. Para preencher este requisito, precisamos inicializar uma variável na seção .DATA que contenha o nome da classe.

.386
.MODEL FLAT,STDCALL
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

gerenteJanela proto :DWORD, :DWORD, :DWORD, :DWORD

.DATA
NomeClasse db "JanelaNua",0


.DATA?
mInstancia DWORD ?
linhaComando DWORD ?

.CODE
inicio:
invoke GetModuleHandle, NULL
mov mInstancia, eax
invoke GetCommandLine
mov linhaComando, eax
invoke gerenteJanela, mInstancia, NULL, linhaComando, SW_SHOWDEFAULT
invoke ExitProcess,0
end inicio

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
gerenteJanela endp

A função

A partir deste ponto listarei apenas a porção da função gerenteJanela. O primeiro membro da estrutura é o cbSize, que deve conter o tamanho da estrutura. Podemos obter este valor usando o operador SIZEOF. Como estilo da janela usaremos CS_HREDRAW OR CS_VREDRAW. O terceiro membro, lpfnWndProc, é o mais importante de todos. O significado de lpfn é "long pointer to function", ou seja, ponteiro longo para função. No win32 não existem ponteiros "near" (perto) ou "far" (distante); devido ao modelo de memória FLAT, existem apenas ponteiros. Mas isto, novamente, é sucata da época do win16. Cada classe janela precisa estar associada a uma função que gerencie o comportamento das janelas criadas a partir desta classe. Esta função, que chamaremos de gerenteMensagem, é tão importante que será discutida em separado. Por enquanto vamos atribuir valores aos primeiros membros da estrutura usando o mnemônico MOV:

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
mov ej.cbSize, SIZEOF WNDCLASSEX
mov ej.style, CS_HREDRAW or CS_VREDRAW
mov ej.lpfnWndProc, OFFSET gerenteMensagem
mov ej.cbClsExtra, NULL
mov ej.cbWndExtra, NULL

gerenteJanela endp

O ej.hInstance, o próximo membro da estrutura, deve conter o manipulador da instância do programa. Podemos usar a variável global mInstancia ou o parâmetro mInst recebido pela função gerenteJanela pois ambos apontam para o mesmo endereço, ou seja, contém o mesmo valor. Como não é possível transferir diretamente o valor de uma posição de memória para outra posição de memória e tanto ej.hInstance quanto mInst são posições de memória, será preciso usar o auxílio de um registrador. O mais fácil é utilizar o registrador da pilha: usar o mnemônico push para colocar o valor na pilha e o mnemônico pop para transferí-lo da pilha para ej.hInstance.

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
mov ej.cbSize, SIZEOF WNDCLASSEX
mov ej.style, CS_HREDRAW or CS_VREDRAW
mov ej.lpfnWndProc, OFFSET gerenteMensagem
mov ej.cbClsExtra, NULL
mov ej.cbWndExtra, NULL
push mInst
pop ej.hInstance

gerenteJanela endp

Ícone e Cursor

Para obter o manipulador do ícone e do cursor basta fazer uma chamada para LoadIcon e LoadCursor. A função LoadIcon carrega o recurso do ícone especificado a partir do executável associado a uma instância do aplicativo.

HICON LoadIcon(
HINSTANCE hInstance,
LPCTSTR lpIconName // string com o nome do ícone ou identificador do recurso do ícone
);

A função LoadCursor funciona como a anterior, apenas direcionada para o cursor.

HCURSOR LoadCursor(
HINSTANCE hInstance,
LPCTSTR lpCursorName // string com o nome do cursor ou identificador do recurso do cursor
);

Nas duas funções, HINSTANCE identifica uma instância do módulo cujo arquivo executável contém o ícone ou cursor que deve ser carregado. Como ainda não programamos os recursos do nosso aplicativo (vamos ver isto em tutoriais posteriores), nosso executável está "vazio" de recursos e HINSTANCE pode ser NULL. Neste caso serão usados os recursos do Windows e podemos usar os parâmetros default (veja mais detalhes na referência da API). Lembre-se de que o valor de retorno destas funções vai sempre para o registrador EAX.

Inicialização

A ordem de inicialização dos membros da estrutura WNDCLASSEX não é importante. Como estamos trabalhando o ícone do programa, aproveitaremos a chamada a LoadIcon e inicializaremos hIcon e hIconSm numa tacada.

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
mov ej.cbSize, SIZEOF WNDCLASSEX
mov ej.style, CS_HREDRAW or CS_VREDRAW
mov ej.lpfnWndProc, OFFSET gerenteMensagem
mov ej.cbClsExtra, NULL
mov ej.cbWndExtra, NULL
push mInst
pop ej.hInstance
invoke LoadIcon, NULL, IDI_WINLOGO
mov ej.hIcon, eax
mov ej.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov ej.hCursor, eax

gerenteJanela endp

A referência da API para WNDCLASSEX nos diz que, se usarmos uma cor para o membro hbrBackground, o valor da cor precisa ser um dos valores das cores padrão do sistema, acrescido de 1. Escolhemos a cor padrão COLOR_WINDOW, portanto usaremos COLOR_WINDOW+1.

Como não projetamos os recursos, também não temos um menu para o nosso aplicativo - a string com o nome do menu, por enquanto, será NULL.

E, finalmente, o nome da nossa classe de janela já foi definida na seção .DATA e o ponteiro para a string que contém o nome é OFFSET NomeClasse.

gerenteJanela proc mInst:DWORD, mInstAnt:DWORD, linhaCmd:DWORD, Mostra:DWORD
LOCAL ej:WNDCLASSEX
mov ej.cbSize, SIZEOF WNDCLASSEX
mov ej.style, CS_HREDRAW or CS_VREDRAW
mov ej.lpfnWndProc, OFFSET gerenteMensagem
mov ej.cbClsExtra, NULL
mov ej.cbWndExtra, NULL
push mInst
pop ej.hInstance
invoke LoadIcon, NULL, IDI_WINLOGO
mov ej.hIcon, eax
mov ej.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov ej.hCursor, eax
mov ej.hbrBackground, COLOR_WINDOW+1
mov ej.lpszMenuName, NULL
mov ej.lpszClassName, OFFSET NomeClasse

gerenteJanela endp

Foi extenso, porém não foi complicado. Nossa estrutura WNDCLASSEX está com todos os valores inicializados, ou seja, nossa classe de janela está definida. Mantendo a comparação inicial, a "planta da casa" está pronta. Agora podemos registrá-la.

Esta função é responsável pelo gerenciamento das mensagens provenientes de todas as janelas criadas a partir da classe associada. O Windows enviará mensagens à função para notificá-la de eventos importantes (entradas de teclado, cliques do mouse, etc) relativos às janelas pelas quais é responsável e a função deve responder adequadamente cada mensagem recebida.

Informações adicionais