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 - Caçando informações

Qui

27

Nov

2008


14:02

(11 votos, média 4.00 de 5) 


C

A esta altura do campeonato já deu para perceber que o grande lance na programação Windows está diretamente relacionado à troca de mensagens entre o sistemão (o sistema operacional Windows) e a central de mensagens do nosso programa. Conforme prometido no final do módulo anterior, o Diálogo Personalizado, vamos rastrear as informações fornecidas pelo usuário. Se você não leu o tutorial anterior, sugiro que o faça, pois este é apenas a continuação do assunto. Além do mais vamos expandir o programa dlg2.c, também criado no módulo anterior.

Definindo os objetivos

Se o usuário clicar no botão [OK], queremos saber o teor do texto digitado. Esta operação tem dois aspectos:

  1. Saber se o botão [OK] foi clicado (se o usuário clicar em [Cancela] ou fechar a janela, é sinal de que não quer revelar o conteúdo digitado).
  2. Obter o texto do campo de edição.

Interceptando um clique no botão [OK]

Lá vamos nós para a central de mensagens do programa. Vou mostrá-la novamente para não dar margem a dúvidas:

static BOOL CALLBACK DialogFunc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: InitializeApp(hwndDlg,wParam,lParam); return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hwndDlg,1); return 1; case IDCANCEL: EndDialog(hwndDlg,0); return 1; } break; case WM_CLOSE: EndDialog(hwndDlg,0); return TRUE; } return FALSE; }

O ponto para interceptar um clique no botão [OK] já está definido: é quando nossa central de mensagens receber uma mensagem do tipo WM_COMMAND trazendo no parâmetro wParam o valor IDOK. O que precisamos, além de fechar a caixa de diálogo com EndDialog, é obter o texto que foi digitado (ou não) no campo de edição. Chegou finalmente a hora de botar a mão na massa - vamos escrever uma função que realize o trabalho desejado.

Caçando o texto

Sabemos que o texto que queremos se encontra no campo de edição de apelido ID_EDICAO. Para capturá-lo, mais uma vez existe uma função da API, a GetDlgItemText. Acontece que esta função extrai o texto da janela-filha ID_EDICAO e transfere os caracteres para uma área de memória. Áreas de memória reservadas para armazenarem dados são chamadas de buffer, cuja tradução literal é pára-choque. Só Deus sabe porque escolheram este termo; pra mim, não tem nada a ver, preferia algo como cesta ou balaio wink

É claro que o balaio... perdão, o buffer precisa ter um tamanho suficientemente grande para conter todos os caracteres que pretendemos transferir. Além disso, é uma exclusividade da casa e não precisa cair no domínio público, ou seja, outros programas ou módulos não precisam saber da sua existência. Chamamos isto de buffer estático.

Por uma questão de ordem, colocamos a definição desta variável estática no início do programa, logo depois dos includes e da declaração do protótipo da função DialogFunc (aliás, ainda não expliquei a razão de protótipos de funções. Aguarde mais um pouco que a gente chega lá). Então, a coisa fica assim se chamarmos a variável estática de balaio e determinarmos que possa conter até 1024 caracteres:

... #include "testedlgres.h" static BOOL CALLBACK DialogFunc( HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); static char balaio[1024]; ...

Também, por uma questão de ordem, deixamos que a primeira função do programa seja a da entrada, a WinMain, seguida pela função de inicialização, a InitializeApp. Bem, então logo após, podemos escrever a seguinte função:

int PegaTexto(HWND hwnd) { memset(balaio,0,sizeof(balaio)); if (GetDlgItemText(hwnd, ID_EDICAO, balaio, sizeof(balaio))) { return 1; } return 0; }

A função de nome PegaTexto exige como parâmetro um manipulador (handle) e retorna um inteiro (int). Agora vamos por partes:

  1. A função memset preenche a área de memória reservada para a variável balaio com 1024 zeros. A função sizeof dá o tamanho do balaio (1024 caracteres). O que queremos é enviado através dos parâmetros e o resultado é que o buffer é zerado - o mesmo que dizer "o balaio é esvaziado".
  2. Segue-se uma diretiva if (cuja tradução é se). Então, SE GetDlgItemText conseguir transferir o conteúdo do campo de edição para o balaio, retorne 1 (o mesmo que TRUE); caso contrário, retorne 0 (o mesmo que FALSE).

Falta destrinchar a função GetDlgItemText. Esta função precisa dos seguintes parâmetros:

  • hwnd: o manipulador da janela-mãe, nossa caixa de diálogo.
  • ID_EDICAO: o apelido do campo de edição, a janela-filha da qual se quer extrair o texto.
  • balaio: o buffer onde devem ser colocados os caracteres.
  • sizeof(balaio): o tamanho do buffer, ou seja, o número máximo de caracteres que podem ser transferidos.

Tudo bem, mas o que é que o if faz com a função GetDlgItemText? SE o que? É que GetDlgItemText retorna o número de caracteres transferidos. Se for 1 ou mais caracteres, é o mesmo que dizer if TRUE, a condição é preenchida e o valor de retorno é 1; caso contrário o valor de retorno é 0. Tudo em riba, só falta chamar nossa função quando o usuário clicar o botão [OK]:

static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: InitializeApp(hwndDlg,wParam,lParam); return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: PegaTexto(hwndDlg); EndDialog(hwndDlg,1); return 1; ...

Conferindo o resultado

Se você fez as alterações propostas até agora, teve a pachorra de salvar o arquivo, fazer a compilação e conseguiu rodar o programa, não notou diferença nenhuma. Simplesmente é preciso acreditar que o texto do campo ID_EDICAO foi transferido para o balaio - não tem como ver. Como isto é muito chato, vamos espiar a área de memória usando a função da API MessageBox que cria uma caixa de mensagem. É uma saída de preguiçoso porque, usando esta função, não será preciso registrar uma classe janela, definir mais um procedimento, etc e tal.

A MessageBox precisa como parâmetros o manipulador da janela-mãe (já deu para perceber que o sistemão adora handles), o buffer onde se encontra o texto que deve ser mostrado, uma string com o título da caixa de mensagem e o botão (ou botões) que acompanham a caixa. Aproveitando a interceptação do botão [OK], logo depois da função PegaTexto, entre o texto das linhas 5, 6 e 8:

... case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (PegaTexto(hwndDlg)) { MessageBox(hwndDlg,balaio,"Texto digitado",MB_OK); EndDialog(hwndDlg,1); } return 1; ...

Salve com |File/Save| o programa dlg2.c, compile e rode. Tchan, tchan, tchan, tchan - funciona! Agora experimente clicar em [OK] com o campo de edição vazio. Você vai perceber que não acontece nada - nem caixa de mensagem aparece, nem a caixa de diálogo é fechada. Isto é porque a função PegaTexto retorna 0 (ou falso), fazendo com que a linha de execução no caso do IDOK não entre na área do if, que o EndDialog não seja executado e que o valor de retorno seja 1 (ou verdadeiro).

Mas que programa mais mal educado! Imagine a situação do usuário que clica em [OK] e fica sem saber porque nada acontece. Vai achar que o programa pendurou ou, se entender de programação, vai dizer que nosso programinha é uma droga. Durma com um barulho desses!

Melhorando a interface

Para corrigir este comportamento inaceitável do nosso programa existem duas alternativas: ou chamamos uma nova caixa de mensagem para avisar o usuário que o campo de edição não pode estar vazio, ou desabilitamos o botão [OK], que só será habilitado quando houver texto no campo de edição. A primeira alternativa não oferece grandes novidades. A segunda é um novo desafio de programação. Então, vamos optar pela segunda.

O botão [OK] deve ter as seguintes características: no início do programa, estar desabilitado; se for digitado texto no campo de edição, deve ser habilitado; se o texto do campo de edição for apagado, deve ser desabilitado novamente. Quem é que está dando as cartas? É o campo de edição, e ele é apenas mais uma das janelas-filhas. Toca correr para nossa central de mensagens...

... case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (PegaTexto(hwndDlg)) { MessageBox(hwndDlg,balaio,"Texto digitado",MB_OK); EndDialog(hwndDlg,1); } return 1; case IDCANCEL: EndDialog(hwndDlg,0); return 1; case ID_EDICAO: return ConfereTexto(hwndDlg,wParam); } break; ...

Foram adicionadas a linhas 13, 14 e 15. Aqui cabe uma explicação. Quando comecei a escrever o texto para habilitar/desabilitar o botão [OK], o código começou a ficar mais extenso do que eu imaginava e a aparência do switch ficou horrível. Por isso, decidi criar uma função à parte, para deixar o código mais elegante e limpo. Vale como sugestão: toda vez que o código começar a virar uma maçaroca, crie uma função. A leitura do código fica mais fácil.

Bem, a função ConfereTexto tem esta cara. Observe os comentários (o que é uma boa prática de programação) cujas linhas são iniciadas com //:

int ConfereTexto(HWND hDlg,WPARAM wParam) { HWND hIdOk = GetDlgItem(hDlg,IDOK); switch (HIWORD(wParam)) { case EN_CHANGE: if (GetDlgItemText(hDlg,ID_EDICAO,balaio, sizeof(balaio))) { // Tem texto no campo de edição. Habilitar o botão IDOK EnableWindow(hIdOk,1); } else // sem texto, disabilitar o botão IDOK EnableWindow(hIdOk,0); break; } return 1; }

Na declaração switch estamos usando o HIWORD do wParam. É preciso explicar que wParam é um double word (também grafado como dword), ou seja, tem 32 bits. Um word é a metade de um dword e tem 16 bits. wParam carrega duas informações diferentes, uma nos 16 bits mais significantes (HIWORD) e outra nos 16 bits menos significantes (LOWORD). Nos bits do LOWORD encontramos a ID do controle que enviou a mensagem (o remetente) e nos bits do HIWORD encontramos a sub-mensagem de WM_COMMAND que o controle está enviando.

No nosso caso, LOWORD indicará ID_EDICAO e HIWORD indicará EN_CHANGE. Todas as mensagens oriundas de campos de edição possuem o prefixo EN, derivado de Edit Field Notification, e a tradução de CHANGE é mudar. Então, a cada mudança no campo de edição, uma mensagem EN_CHANGE será enviada através do LOWORD - botou uma letra, uma mensagem é enviada; tirou uma letra, a mesma coisa. É exatamente isto que vamos interceptar, ou seja, caso EN_CHANGE, então... então chamamos GetDlgItemText para verificar quantos caracteres há no campo de edição. Se houver mais do que 0 (zero) caracteres, habilitamos o botão. Se não houver caracteres, desabilitamos o botão.

Pra variar, precisamos do manipulador (handle) do botão IDOK para poder alterar suas propriedades. O manipulador foi obtido logo no início da função quando chamamos a GetDlgItem e guardamos o valor de retorno na variável hIdOk (que é do tipo HWND). Chamamos a função EnableWindow com os parâmetros hIdOk (o handle do botão IDOK) e um inteiro: 1 (TRUE) para habilitar o botão e 0 (FALSE) para desabilitá-lo.

Só falta um detalhe: o programa precisa começar com o botão IDOK desabilitado. Neste caso, isto deve ser feito antes da janela ser apresentada na tela, o que nos remete à função InitializaApp. Como já somos cobras em sanduiches de funções, mandamos ver o seguinte:

static int InitializeApp( HWND hDlg,WPARAM wParam, LPARAM lParam) { // Por foco no campo de edição SetFocus(GetDlgItem(hDlg,ID_EDICAO)); // Desabilitar o botão IDOK EnableWindow(GetDlgItem(hDlg,IDOK),0); return 1; }

Atualize o programa dlg2.c, salve-o, compile o código e execute o programa. Tá melhor, tá não?

Diálogo

Observações da vó

Sobre a interface com o usuário: a solução de desabilitar o botão OK até que foi boa, mas prefiro usar as caixas de mensagem que explicam exatamente o que está acontecendo. Um botão ou um item de menu desabilitados, na verdade, só impedem que sejam clicados e o usuário não é obrigado a saber porque isto está ocorrendo. Mensagens explícitas facilitam as coisas, mesmo quando óbvias (já me aconteceu de ter que abrir o código para verificar porque eu havia desabilitado determinado item de menu, pois não me lembrava mais depois de alguns meses wink

Bem, até agora nos preocupamos mais com o resultado da programação usando funções disponíveis. Está na hora de dar uma olhada nas principais bibliotecas que fornecem estas funções. Criamos nossas próprias funções e também fizemos uso de algumas variáveis. Não seria má idéia conhecer os tipos básicos, definições e declarações. Estes serão os assuntos do próximo tutorial.

биография Вадим Логофет как приготовить плов из курицы в мультиваркеофициальный никас сайт менюхарьков лобановскийангар быстровозводимыйкупить пудруполигон ооо

Informações adicionais