Tutoriais e Programação
Linguagem C - Caçando informações
Qui 27 Nov 2008 14:02 |
- Detalhes
- Categoria: Linguagem C
- Atualização: Quinta, 27 Novembro 2008 20:21
- Autor: vovó Vicki
- Acessos: 15547
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:
- 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).
- 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:
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
É 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:
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:
A função de nome PegaTexto exige como parâmetro um manipulador (handle) e retorna um inteiro (int). Agora vamos por partes:
- 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".
- 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]:
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:
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...
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 //:
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:
Atualize o programa dlg2.c, salve-o, compile o código e execute o programa. Tá melhor, tá não?
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
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.