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) 


Nível Avançado No tutorial anterior aprendemos como pôr uma janelinha na tela. Só que era uma janela do tipo message box, que não permite adicionar certas funcionalidades como um menu por exemplo. Neste tutorial vamos aprender a criar uma janela "de verdade". Além disso, vamos explorar a "mecânica" da coisa.

Projeto

Como sempre, nosso primeiro passo é planejar o programa. O projeto até que é pequeno, mas, em compensação... o tutorial é um mastodonte!

  1. O sistema operacional é o Windows de 32 bits.
  2. Não precisamos mais do que o conjunto de instruções do processador 386.
  3. Vamos criar apenas uma janela "nua", com um mínimo de funcionalidade (apenas pode ser fechada).

Vamos por partes que a coisa há de ficar clara. Um dos aspectos mais importantes é entender o sistema de comunicação do Windows. Antes de começar pra valer este tutorial que deve ficar um pouco longo, abra o QEditor do MASM32 e digite (ou copie e cole) o esqueleto de um programa que possa ser finalizado com ExitProcess:

.386
.MODEL FLAT,STDCALL
option casemap:none

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

.CODE
inicio:
invoke ExitProcess,0
end inicio

A teoria das janelas

A Interface Gráfica dos programas Windows, conhecida como GUI (Graphical User Interface), depende essencialmente de funções da API. O uso desta interface padrão beneficia usuários e programadores. Para os usuários, as GUIs dos programas Windows são todas parecidas facilitando a navegação. Para os programadores, os códigos da GUI estão testados e prontos para uso. A desvantagem para os programadores é a complexidade crescente envolvida. Para criar ou manipular qualquer objeto GUI, como janelas, menus ou ícones, os programadores precisam seguir regras rígidas definidas pelo sistema Windows.

Logo abaixo estão os passos necessários para se criar uma janela no desktop:

  1. Obter um manipulador de instância - HANDLE (obrigatório).
  2. Pegar as instruções da linha de comando (dispensável, a não ser que o programa necessite de parâmetros iniciais fornecidos pelo usuário).
  3. Criar e Registrar a classe da janela (requerido, a não ser que se use tipos de janelas predefinidos, por exemplo MessageBox ou uma Dialog Box).
  4. Criar a janela (Obrigatório).
  5. Mostrar a janela no desktop.
  6. Atualizar a área cliente da janela.
  7. Entrar num loop infinito checando as mensagens do Windows.
  8. Processar as mensagens da janela - o gerente de mensagens.
  9. Encerrar o programa se o usuário fechar a janela.

Handle

Como usuário do Windows você sabe que pode rodar várias instâncias de um mesmo programa. Se você abrir a calculadora do Windows duas vezes, obterá duas janelas distintas, cada uma delas rodando uma instância do programa. Se você fizer cálculos na primeira janela, a segunda não é afetada. Portanto, cada uma das instâncias, aos olhos do sistema, é um aplicativo independente. Como é que o Windows consegue individualizar cada uma das instâncias? Através de um identificador ou manipulador de instância. Este manipulador é apenas um número que identifica a instância para o sistema. Por exemplo, "a janela da instância 1 precisa ser atualizada", "a janela da instância 2 foi ativada", "a janela da instância 2 foi minimizada", etc, permite que o sistema efetue as tarefas nas janelas corretas. Se não existisse o número de identificação, bem... a bagunça seria inevitável. E se duas instâncias possuírem o mesmo número... o Windows vai dar pau.

Existe uma função da API que nos permite obter um manipulador de instância que não conflite com outros já existentes (as duas janelas que abrimos para a calculadora não são as únicas que estão abertas). Esta função é a GetModuleHandle, que faz parte da kernel32.lib.

info Aproveite e familiarize-se um pouco mais com o MASM32: clique no item de menu [Tools / API Library List] para ativar uma ferramenta muito útil que nosso amigo hutch colocou à nossa disposição - a "API to Library list". Esta janelinha é o mapa da mina que relaciona uma grande quantidade de funções e as bibliotecas correspondentes. Digite "getmoduleh" e você já estará na linha que corresponde à função procurada: GetModuleHandle lib ==> kernel32.lib

Nós (e o sistema) vamos precisar do manipulador de instância quando quisermos atualizar ou alterar alguma coisa na(s) janela(s) do programa. É um número que será usado numa porção de funções diferentes, portanto, precisamos preparar um lugar onde vamos guardar este identificador e que possa ser acessado de qualquer ponto do programa . Um endereço de memória que guarda um valor e que pode ser acessado de qualquer ponto do programa é chamado de VARIÁVEL GLOBAL. Ao invés de lembrar do endereço, podemos dar um nome a ele. Por exemplo, você pode ir para a casa do "Zé" ou para a "rua dos bits, número 135" que dá na mesma, pois o Zé mora neste endereço.

Já que precisamos deixar a critério do sistema o número que será designado como manipulador de instância para o programa, o valor da variável que conterá o manipulador da instância só pode ser obtido em tempo de execução, portanto, não podemos (e não devemos!) inicializar esta variável. A seção para variáveis não inicializadas, conforme já foi visto no tutorial "Por onde começar", é a seção .DATA?. Nesta seção damos nomes às variáveis, indicamos seu tipo e informamos que não são inicializadas através de um ponto de interrogação (?).

A função GetModuleHandle

Qual é o tipo da variável que precisamos declarar? A referência da API nos diz que a função GetModuleHandle retorna um manipulador de módulo (módulo é igual a instância no win32) para o módulo especificado se o arquivo tiver sido mapeado no espaço de endereços do processo chamador e que o tipo do valor de retorno é HMODULE - apenas um dos muitos nomes que o Windows dá a DWORD.
HMODULE GetModuleHandle(
LPCTSTR lpModuleName // endereço do nome do módulo para o qual se solicita um manipulador
);
  • Parâmetro lpModuleName: Aponta para uma string terminada em zero com o nome de um módulo Win32 (uma .DLL ou arquivo .EXE). Se a extensão do nome do arquivo for omitida, a extensão default é .DLL. A string do nome do arquivo por ter um caracter finalizador ponto (.) para indicar que o nome do módulo não possui extensão. A string não precisa especificar um caminho (path). O nome é comparado (sem considerar maiúsculas e minúsculas) aos nomes dos módulos que já estejam mapeados no espaço de endereços do processo chamador.
    Se o parâmetro for NULL, GetModuleHandle retorna um manipulador do arquivo usado para criar o processo da chamada.

Então vamos digitar um pouco. Certifique-se de que os arquivos kernel32.inc e kernel32.lib constam na lista de include e includelib. Uma vez familiarizados com a função, podemos chamá-la com o parâmetro NULL. É tudo o que queremos: um manipulador de instância para o nosso programa.

.386
.MODEL FLAT,STDCALL
option casemap:none

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

.DATA?
mInstancia DWORD ?


.CODE
inicio:
invoke GetModuleHandle, NULL
mov mInstancia, eax

invoke ExitProcess,0
end inicio

A diretiva invoke você já conhece do tutorial "O Folgado" e a função GetModuleHandle já foi mais do que explicada. A linha seguinte é que são elas: usa o mnemônico MOV com os operandos mInstancia e eax. Vamos por partes.

info Um mnemônico é um nome reservado de uma família de códigos operacionais que realizam tarefas semelhantes no processador. MOV pede ao processador para MOVer ou copiar um valor de um local para outro. Dê uma olhada no texto acessório "Códigos Operacionais" para entender melhor.

EAX é um registrador de uso geral, ou seja, é um tipo especial de memória DENTRO do processador e que serve para o armazenamento temporário de dados (a informação pode ser colocada num determinado instante e de lá ser retirada quando isso se fizer necessário). Existem vários registradores dentro do processador - para maiores detalhes leia o texto acessório "Registradores".

Em todo caso, esta nova linha pode ser traduzida da seguinte maneira: MOVa o valor que se encontra no registrador EAX para a posição de memória referente à nossa variável mInstancia. Quando esta linha for executada, a variável global mInstancia é inicializada.

O motivo pelo qual transferimos o valor de EAX para mInstancia é que o valor de retorno das funções da API são armazenados no registrador EAX. Neste caso, assim que se volta da função GetModuleHandle, o registrador EAX contém o valor do manipulador de instância solicitado.

Informações adicionais