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 - Projeto Espião III

Dom

30

Nov

2008


21:20

(1 voto de 5.00) 


C

Se você seguiu os tutoriais anteriores, então seu programa espião.exe já deve estar funcionando. Mas está meio tímido, porque não fala com o usuário - só mostra o menu e, quando solicitado, a árvore com as janelas abertas no momento da solicitação. Calado demais, este nosso espião. Neste tutorial vamos fazer com que entregue e ouro wink

Quem é quem no pedaço

Nosso projeto inicial incluía informações sobre as janelas na barra de status. Uma das formas de pedir informações é clicando num dos itens da árvore. Mas, como saber quando o usuário clica um item?

O controle de árvore, assim como muitos outros controles, envia mensagens do tipo WM_NOTIFY para nossa central de mensagens toda vez que um evento acontece, ou seja, com qualquer coisinha o controle grita. É justamente esta mensagem que precisamos interceptar com o auxílio do caso WM_NOTIFY (linhas 11 e 12):

LRESULT CALLBACK MainWndProc( HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { static HWND hwndTree; RECT rc, rcStatus; switch (msg) { case WM_CREATE: hwndTree = janelaTree(hwnd,IDJANELATREE); break; case WM_NOTIFY: return TrataWmNotify(hwnd,wParam,lParam); ...

Fiéis ao estilo "top-down", chamamos a função TrataWmNotify, a qual deverá fazer o serviço final de espionagem: obter informações precisas sobre a janela indicada pelo usuário.

Catando gritos

O código da função é um tantinho mais complicado que os anteriores, mas não é nada que assuste. Veja abaixo:

LRESULT TrataWmNotify(HWND hwnd, WPARAM wParam, LPARAM lParam) { NMHDR *apontaGrito; TV_HITTESTINFO testaClique; HWND hTree = GetDlgItem(hwnd,IDJANELATREE); HTREEITEM hti; HWND hwndAlvo; apontaGrito = (NMHDR *)lParam; switch (apontaGrito->code) { case NM_CLICK: memset(&testaClique,0,sizeof(TV_HITTESTINFO)); GetCursorPos(&testaClique.pt); MapWindowPoints(HWND_DESKTOP,hTree,&testaClique.pt,1); hti = TreeView_HitTest(hTree,&testaClique); if (hti == (HTREEITEM)0) break; hwndAlvo = PegaItemInfo(hTree,hti); PoeTextoStatus(hwnd,hwndAlvo); break; } return DefWindowProc(hwnd,WM_NOTIFY,wParam,lParam); }

De todos os gritos que o controle de árvore possa dar, o único que nos interessa é o NM_CLICK. Como este controle é muito nervoso (por ser muito complexo), a quantidade de gritos que ele é capaz de dar é muito grande. O tipo de grito dado é sempre armazenado no parâmetro lParam da mensagem WM_NOTIFY. Este parâmetro nada mais é do que um ponteiro indicando o endereço de memória de uma estrutura chamada de NMHDR:

typedef struct tagNMHDR { HWND hwndFrom; UINT idFrom; UINT code; } NMHDR;

O campo HWND hwndFrom contém o manipulador da janela que está gritando (ou enviando a mensagem, como queira), o campo UINT idFrom contém o identificador do controle e o campo UINT code contém o código do grito. Existem oito tipos de grito:

  • NM_CLICK: o controle foi clicado com o botão esquerdo do mouse
  • NM_DBLCLK: o controle recebeu um duplo clique com o botão esquerdo do mouse
  • NM_KILLFOCUS: o controle perdeu o foco de entrada
  • NM_OUTOFMEMORY: o controle não pode completar uma operação por falta de memória (dá-lhe gynko biloba smile )
  • NM_RCLICK: o controle recebeu um clique com o botão direito do mouse
  • NM_RDCLICK: o controle recebeu um duplo clique com o botão direito do mouse
  • NM_RETURN: o controle tem o foco e o usuário digitou a tecla Enter
  • NM_SETFOCUS: o controle recebeu o foco

Selecionando gritos

Como já disse, o único grito que nos interessa é o NM_CLICK, mas não todos os NM_CLICKs. Se o usuário clicar sobre um item, sim; se clicar na janela do controle, mas não sobre um item, não! Então, vamos por partes:

  1. Sabendo que vamos precisar de um ponteiro que nos indique o endereço da estrutura NMHDR, manisfestamos a variável *apontaGrito do tipo NMHDR. Mais abaixo, definimos apontaGrito como o ponteiro para a estrutura do tipo MNHDR do parâmetro lParam (apontaGrito = (NMHDR *)lParam;).
  2. Tendo como acessar a estrutura NMHDR, usamos um switch apontando para o campo code da estrutura para interceptar o caso NM_CLICK (lembre-se que ponteiros usam -> para referenciar um campo, e não um ponto, por isso switch (apontaGrito->code) ).
  3. No caso do switch interceptar um NM_CLICK, a primeira providência é limpar a área de memória reservada para a estrutura testaClique, do tipo TV_HITTESTINFO, que foi previamente manifestada.

Antes de prosseguir, vamos dar uma olhada na definição da estrutura TV_HITTESTINFO, usada para determinar a localização de um ponto relativo a um controle de árvore:

typedef struct _TVHITTESTINFO { POINT pt; UINT flags; HTREEITEM hItem; } TV_HITTESTINFO, FAR *LPTV_HITTESTINFO;

POINT pt contém as coordenadas-cliente do ponto testado, UINT flags recebe as informações do resultado do ponto que foi testado e HTREEITEM hItem contém o manipulador (handle) do item que ocupa o ponto. Agora podemos continuar com a análise:

  1. Chamando a função da API GetCursorPos, colocamos as coordenadas do ponto clicado na estrutura testaClique. Como precisamos passar como parâmetro o endereço do campo pt da estrutura, a chamada é feita com &testaClique.pt.
  2. A função MapWindowPoints transforma (mapeia) um conjunto de pontos de um espaço de coordenadas relativos a uma janela para um espaço de coordenadas relativos a outra janela. Se o primeiro parâmetro for NULL ou HWND_DESKTOP, os pontos serão mapeados como coordenadas de tela. O segundo parâmetro (htree) indica a janela para os quais os pontos serão mapeados. O terceiro parâmetro é o endereço onde se encontram os pontos que devem ser mapeados e o último indica quantos conjuntos de pontos existem (no caso, temos apenas 1 conjunto de coordenadas).
  3. Mapeadas as coordenadas de tela do clique, usamos a macro TreeView_HitTest que determina a localização do ponto especificado em relação à área cliente de um controle de árvore. Esta macro retorna o item ao qual o ponto pertence ou NULL se o ponto não estiver em um dos itens. Este valor de retorno é armazenado na variável hti, do tipo HTREEITEM, anteriormente manifestada.
  4. Se o valor de hti for 0 (zero), é sinal que o clique foi dado fora da área de um item da árvore. Neste caso interrompe-se o caso com break. Se hti contiver um item da árvore, então o grito do controle nos interessa.
  5. Com um NM_CLICK válido, chamamos duas funções, a PegaItemInfo e a PoeTextoStatus, das quais falaremos adiante.
  6. Finalmente passamos todas as mensagens para o procedimento padrão da janela chamando o procedimento DefWindowProc com todos os parâmetros iniciais. Isto é uma atitude não invasiva, altamente aconselhável porque o controle pode precisar das mensagens e porque queremos manter os distúrbios no menor nível possível para não afetar negativamente o ambiente.

Informações adicionais