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...

Tutoriais e Programação

Linguagem C - Uma janela de verdade

Sex

28

Nov

2008


10:57

(7 votos, média 4.43 de 5) 


C

A vontade de ver as coisas funcionarem depressinha é grande. Ficar digerindo teoria é o tipo da coisa chata. O melhor é ver o resultado e depois esmiuçar a teoria - aí a coisa não fica no ar. Então, já que estamos programando para Windows, que tal encarar uma janela de verdade?

Criando o projeto

Os passos para criar um projeto você já conhece: |Project|Create|, dar nome ao projeto (eu dei o nome de "janela"), selecionar o diretório de trabalho (continuei usando o /lcc/projects/teste/), selecionar "Windows application" e clicar em [Create]. Confirme o diretório com [Yes], aceite o wizard com [Yes]. Selecione "Single window" e clique em [Ok], [Next], [Finish] e novamente em [OK]. Deixe tudo como está e clique em [Next], [Next] e [Finish] para finalmente chegar na janela de edição.

Deu para perceber que aceitamos todas as configurações apresentadas pelo wedit - só tivemos o trabalho de assinalar "Windows application". Dando uma olhada no código fonte gerado automaticamente, dá para perceber que é um pouco mais elaborado do que o código fonte da caixa de diálogo. Algumas coisas já conhecemos, em compensação tem umas outras que... bem, serão vistas mais para frente.

Compile e rode o programa. O que se vê é uma janela de fundo branco, com uma barra de título na parte superior, uma barra de status na parte inferior e um menu - este é o esqueleto de um aplicativo windows (mas não esquecido dentro do armário wink )

Analisando o código fonte

Como sempre, vamos começar com a função WinMain. Sabemos que ela não pode faltar, pois é a porta de entrada do nosso aplicativo:

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) { MSG msg; HANDLE hAccelTable; hInst = hInstance; if (!InitApplication()) return 0; hAccelTable = LoadAccelerators(hInst,MAKEINTRESOURCE(IDACCEL)); if ((hwndMain = CreatejanelaWndClassWnd()) == (HWND)0) return 0; CreateSBar(hwndMain,"Ready",1); ShowWindow(hwndMain,SW_SHOW); while (GetMessage(&msg,NULL,0,0)) { if (!TranslateAccelerator(msg.hwnd,hAccelTable,&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }

O esquema básico é o mesmo dos nossos projetos de caixas de diálogo, mas esta função é mais parruda: iniciamos o aplicativo chamando a função InitApplication (onde será registrada a classe da janela, etc), carregamos os aceleradores de teclado (as teclas de atalho), criamos a janela, a barra de status, mostramos a janela na tela e entramos num loop de mensagens - o mesmo que dizer que entramos na central de comunicação do nosso programa. A única mensagem que pode nos "expulsar" da central de comunicação é a WM_QUIT.

Como sempre, um aplicativo windows nada mais é do que receber e enviar mensagens entre o sistema operacional e a central de comunicação do nosso programa. Vamos analisar o código por partes, começando com a primeira função chamada, a InitApplication.

A inicialização

Este procedimento começa manifestando a estrutura wc, do tipo WNDCLASS (esta estrutura já está manifestada no arquivo cabeçalho windows.h que inclui o win.h). Não é a primeira vez que falamos em estruturas, aliás, um conceito tão importante que será o assunto do próximo tutorial. Se quiser se antecipar, dê uma lida em Estruturas e Uniões e depois volte para este texto.

Vamos dar uma olhada na função estática InitApplication, que não precisa de parâmetros (void) e que retorna um valor lógico ou BOOL (1=verdadeiro ou 0=falso):

static BOOL InitApplication(void) { WNDCLASS wc; memset(&wc,0,sizeof(WNDCLASS)); wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS ; wc.lpfnWndProc = (WNDPROC)MainWndProc; wc.hInstance = hInst; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszClassName = "janelaWndClass"; wc.lpszMenuName = MAKEINTRESOURCE(IDMAINMENU); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); if (!RegisterClass(&wc)) return 0; return 1; }

O estilo da janela (wc.style) é uma combinação de constantes inteiras (CS_HREDRAW, CS_VREDRAW e CS_DBLCLKS) feita através do operador OR, representado por uma barra vertical.

O operador OR, um operador lógico cuja tradução é OU, é o modo padrão da linguagem C manipular bits. Quais bits? Neste caso, dos valores correspondentes às constantes. Experimente o seguinte: clique com o botão direito do mouse sobre CS_HREDRAW e escolha |Show definition of CS_HREDRAW|. Na janelinha pop-up é possível verificar que o valor desta constante é 2. Faça o mesmo com as outras duas. A declaração wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS; pode ser traduzida para wc.style = 2 OR 1 OR 8; ou wc.style = 2 | 1 | 8;. Aliás, HREDRAW pode ser traduzido para "Redesenhar a Horizontal", VREDRAW para "Redesenhar a Vertical" e DBLCLKS para "Cliques Duplos".

Note que os valores das constantes são potências de 2, ou seja, 2 é igual a 2 elevado a 1, 1 é igual a 2 elevado a 0 e 8 é igual a 2 elevado a 3. Existe uma regrinha fácil de ser guardada: toda potência de 2 possui apenas um único bit com valor 1; todos os outros bits estão zerados. Outra dica: a operação lógica OR é a comparação de dois bits. Apenas se os dois bits forem 0, o resultado será 0; caso contrário, será 1.

Conhecendo os valores e sabendo como funciona o OR, fica fácil perceber que 2 OR 1 OR 8, expresso em binário, será 0010 OR 0001 OR 1000, o que é igual a 1011, que correspode a 11 na notação decimal. Na verdade, cada bit desta "tripinha" de 4 bits, onde os bits são numerados da direita para a esquerda a partir de 0, funciona como uma flag: bit 0 ligado = janela redesenha na vertical; bit 1 ligado = janela redesenha na vertical; bit 3 ligado = janela aceita duplos cliques.

Logo após, ao invés de usar o DefDialogProc (próprio para caixas de diálogo), atribui-se o MainWndProc. O restante não difere da definição da estrutura WNDCLASS vista nas caixas de diálogo.

A tabela de teclas de atalho

Os aceleradores, dispostos em uma tabela, são as de teclas de atalho que permitem o acesso rápido a itens de menu sem o uso do mouse. Encontram-se nos recursos e podem ser editados.

Clique em |Design|New/Open|, a seguir escolha janelares.h, clique em [Open] e escolha "Accelerator" com um duplo clique. Você obterá uma janela de teclas aceleradoras indicando IDM_EXIT, com o valor 300 e com um valor de tecla ("Key value") de 81. 81 corresponde ao valor ASCII da letra "q".

Dê um duplo clique na linha do "Key value" 81 para obter a janela "Changing an accelerator key". Você pode mudar, adicionar ou eliminar teclas aceleradoras. Fique à vontade... só que, como já compilamos nosso programa, as alterações nos recursos terão de ser feitas "na unha", o que é assunto para mais tarde.

Isto foi só um adendo. Vamos voltar à vaca fria...

A central de mensagens

Após carregar os aceleradores, é criada a barra de status no rodapé da janela e, finalmente, a janela é mostrada. Até então, enquanto estava sendo "enfeitada", a janela permaneceu "escondida". Isto impediu que uma janela inacabada ficasse sassaricando na tela, mareando o usuário, e economizou muito tempo de execução.

Na verdade, a parte mais importante é o loop de mensagens que vem logo depois da chamada à função da API ShowWindow (mostre a janela):

... ShowWindow(hwndMain,SW_SHOW); while (GetMessage(&msg,NULL,0,0)) { if (!TranslateAccelerator(msg.hwnd,hAccelTable,&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } ...

Este loop, administrado por um comando de controle de fluxo while, chama a função da API GetMessage (pegar mensagem). Se a função retornar verdadeiro (TRUE), chamamos novamente a API para traduzir teclas aceleradoras (TranslateAccelerator). Esta função converterá uma sequência de teclas digitadas numa mensagem WM_COMMAND, como se tivesse sido enviada por um item de menu, se encontrar uma correspondência entre as teclas digitadas e a tabela de aceleradores que foi carregada algumas linhas antes.

Se TranslateAccelerator não encontrar nenhuma correspondência, a linha de execução passa para a função da API TranslateMessage (traduzir mensagem). Esta função controla as teclas digitadas e realiza o trabalho penoso de depurar a digitação do usuário (controlar sequências repetidas, intervalos, etc).

Finalmente é chamada a função da API DispatchMessage (despachar mensagem), que envia a mensagem para o procedimento indicado na classe janela, o MainWndProc.

É isso aí. Ficamos dando voltas e mais voltas neste loop até que, num dado momento seja gerada a mensagem WM_QUIT (terminar). Esta mensagem faz com que GetMessage retorne FALSE, interrompendo o loop e terminando o programa.

Informações adicionais