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.


Mais sobre flags e operações binárias

Aproveitando o embalo, é bom saber um pouco mais sobre flags (bandeiras) e como tratá-las. Na programação C é muito importante saber lidar com bits e, como já disse acima, flags são os bits em "tripas" de bits, onde cada bit representa uma informação específica.

Um número inteiro nos disponibiliza até 32 bits, ou seja, 32 flags. Também já falei sobre a regrinha do bit único: números que sejam potências da base 2 possuem apenas 1 bit ligado. Assim, numa flag de 32 bits, os números que "ligam" as bandeiras únicas são 1, 2, 4, 8, 16, 32, 64, 128, 256... até 2 elevado à potência 31 (que corresponde a 2.147.483.648). Na representação binária temos:

0000 0000 0000 0000 0000 0000 0000 0001         1               2 elevado a 0
0000 0000 0000 0000 0000 0000 0000 0010         2               2 elevado a 1
0000 0000 0000 0000 0000 0000 0000 0100         4               2 elevado a 2
0000 0000 0000 0000 0000 0000 0000 1000         8               2 elevado a 3
0000 0000 0000 0000 0000 0000 0001 0000        16               2 elevado a 4
...
1000 0000 0000 0000 0000 0000 0000 0000        2.147.483.648    2 elevado a 31

Observe que as potências da base 2 indicam exatamente a posição do bit ligado se os numerarmos da direita para a esquerda a partir de 0. Por exemplo, 16 corresponde a 2 elevado a 4, portanto o bit 4 (o quinto bit da direita para a esquerda) estará ligado. No caso das flags não é propriamente o número que nos interessa, mas sim o(s) bit(s) ligados.

Ligando flags

Digamos que possuímos uma variável de nome minhasFlags, manifestada como número short (16 bits). Se quisermos ligar o oitavo bit, nem é preciso fazer muita ginástica: basta atribuir minhasFlags=128 para ligarmos o bit desejado (2 elevado a 7 é igual a 128).

Se quisermos ligar mais de um bit, por exemplo o oitavo e o quarto, uma das maneiras é somar os valores para cada bit. Como 2 elevado a 7 é igual a 128 e 2 elevado a 3 é 8, a soma é 128+8=136 que, em binário, corresponde a 1000 1000. Na mosca!

A operação binária OR faz a mesma coisa, porém de forma mais elegante. Lembrando, o operador de OR é | e a operação OR mantém TODOS os bits ligados dos dois operandos:

1000 0000 OR 0000 1000 = 1000 1000      (em binário)

                      ou

 1000 0000 | 0000 1000 = 1000 1000      (em binário)

                      ou

               128 | 8 = 136            (em decimal)

Para ligar estes bits em minhasFlags usamos:

minhasFlags = 136;

           ou

minhasFlags = 0;
minhasFlags |= 128;
minhasFlags |= 8;

Testando flags

Digamos que não saibamos qual é o valor da variável minhasFlags, ou seja, desconhecemos os bits que estão ligados. Se quisermos determinar quais são as flags ativas, usamos a operação lógica binária AND (cuja tradução é E).

A operação AND mantém apenas os bits que estiverem ligados nos dois operandos. O operador de AND é &. Se quisermos testar se o oitavo bit de minhasFlags está ligado, basta fazer um AND com minhasFlags e 128. Veja abaixo:

minhasFlags armazena o valor 136 que, em binário,
   corresponde a 0000 0000 1000 1000
Queremos saber se o oitavo bit está ligado, ou seja,
   0000 0000 1000 0000 (ou 128 decimal)

        0000 0000 1000 1000      (136)
AND     0000 0000 1000 0000      (128)
        -------------------
        0000 0000 1000 0000      (128)

136 & 128 = 128 nos indica que o oitavo bit está ligado. O mesmo pode ser feito com o quarto bit pois, 136 & 8 = 8. Se testarmos o terceiro bit, por exemplo, obtemos 136 & 4 = 0. O resultado nulo nos indica que o terceiro bit está desligado. Veja como programar:

if (minhasFlags & 128) { ... } ou oitavo_bit = 128; if (minhasFlags & oitavo_bit) { ... }

Desligando bits

Desligar bits exige uma operação engraçada, chamada de complemento. O complemento simplesmente inverte os bits: todos os ligados são desligados e todos os desligados são ligados. Assim, 0000 1000 é transformado em 1111 0111. A operação de complemento transformou 8 (0000 1000) em 247 (1111 0111)!

Mas como não estamos nem um pouquinho preocupados com o valor decimal da nossa tripinha de bits, o que vale é o raciocínio "bital": se trocarmos todos os bits e depois fizermos um AND, qual será o resultado? Acompanhe abaixo:

Queremos desligar o bit	3     ->       0000 0000 0000 1000
Fazemos o complemento         ->       1111 1111 1111 0111
Agora um AND com minhasFlags  ->       0000 0000 1000 1000
                                       -------------------
E o resultado é               ->       0000 0000 1000 0000

Ahá!!! Apenas o quarto bit (o de número 3) foi desligado - todos os outros permaneceram inalterados. Agora só falta explicar que o operador para a operação de complemento é ~. Uma declaração em C que desligue o quarto bit será:

tiraBit = 8; // (0000 0000 0000 1000) complTira ~= tiraBit; // (1111 1111 1111 0111) minhasFlags &= complTira; // (0000 0000 1000 1000) // (1111 1111 1111 0111) // --------------------- // (0000 0000 1000 0000) ou diretamente tiraBit = 8; minhasFlags &= ~tiraBit;

Observações da vó

Deu para entender porque existe a expressão "escovador de bits"?

Imagino que agora todo mundo esteja meio intoxicado e não é bem esta a proposta deste tutorial. Além do mais, não devemos perder de vista que o objetivo é programar uma janela que sirva para algum propósito - não é uma boa idéia ficar só no esqueleto. Mas, como sou abusada, dá-lhe mais um pouco de teoria: o assunto do próximo tutorial são as Estruturas e as Uniões. Se você ainda não leu o texto, conforme sugerido acima, anime-se! Se você já leu e, principalmente, se entendeu... tá legal, pule o tutorial.

mfx brokerкупить набор кистейооо полигон харьковблендеры отзывыновости погодыmfx brokertopodin отзывы

Informações adicionais