Adicionando um menu e um ícone no “Hello, world” para Windows

Além do código fonte em C, existe uma maneira de inserir “recursos” nos seus programas através de um “compilador de recursos” (resource compiler). A ideia é colocar tudo o que sua aplicação vai usar linkado ao seu programa: Ícones, menus e algumas outras coisas… Para tanto, temos que criar um arquivo de recursos, com extensão .rc. No nosso caso, quero disponibilizar apenas um ícone default para o hello e um menu simples. Isso é feito assim:

/* winapp.rc */
#include "winapp-resources.h"

WINAPP_ICON ICON "winapp.ico"

WINAPP_MENU MENU
BEGIN
  POPUP "&File"
  BEGIN
    MENUITEM "E&xit", ID_FILE_EXIT
  END
END

E, em winapp.resources.h eu definirei apenas a constante para ID_FILE_EXIT:

/* winapp-resources.h */
#ifndef WINAPP_RESOURCES_INCLUDED
#define WINAPP_RESOURCES_INCLUDED

/* Qualquer valor arbitrário serve, contanto que seja único! */
#define ID_FILE_EXIT 1000

#endif

winapp-0O arquivo winapp.ico deixo à sua escolha. Eu escolhi esse aqui do lado. Poderia ser qualquer um outro… Não sei se reparou, mas o programa original, sem um ícone customizado, também tem um ícone… Ele é definido pela constante IDI_APPLICATION no registro da classe da janela (o que levanta uma questão interessante, mas a farei depois!)… O arquivo de recursos acima define o recurso WINAPP_ICON (pode ser qualquer outro nome, desde que seja único!) como do tipo ICON e fornece o nome do arquivo.

Da mesma forma, ele define um menu que chamei de WINAPP_MENU, do tipo MENU… Quando o arquivo de recursos é compilado com o comando abaixo:

$ i586-mingw32msvc-windres winapp.rc winapp-resources.o

O arquivo objeto winapp-resources.o é criado e será linkado com o winapp.o, inserindo esses recursos no nosso executável. Tudo o que precisamos fazer é modificar a classe de nossa janela principal, adicionando o menu e o ícone do recurso… Isso é feito modificando-se o membro de dados hIcon e adicionando o membro lpszMenuName na estrutura WNDCLASS no código do artigo anterior:

#include <windows.h>
#include "winapp-resources.h"
...
  WNDCLASS wc = {
    ...
    .hIcon = LoadIcon(NULL, "WINAPP_ICON"),
    .lpszMenuName = "WINAPP_MENU"
    ...
  }
...

Ao recompilar a aplicação, como abaixo (primeiro compilei o recurso com o winres, depois o código em C, com o gcc e, por último, “linkei” tudo usando, também, o gcc), obtemos uma janela que segue, quando executada:

$ i586-mingw32msvc-windres winapp.rc winapp-resources.o
$ i586-mingw32msvc-cc -O3 -c -o winapp.o winapp.c
$ i586-mingw32msvc-cc -s -o winapp.exe winapp.o winapp-resources.o
Com menu e ícones novos!
Com menu e ícones novos!

Repare que se você clicar em “Exit” nada acontecerá… Isso é porque precisamos tratar a mensagem que será entregue ao procedimento de janela, adicionando o tratador da mensagem WM_COMMAND em WindowMessagesHandler():

...
  case WM_COMMAND:
    if (LOWORD(wParam) == ID_FILE_EXIT)
      PostMessage(hWnd, WM_CLOSE, 0, 0);
    break;
...

Ou seja, se WM_COMMAND for atrelado ao recurso ID_FILE_EXIT (o item “Exit” do menu), então mandamos a mensagem WM_CLOSE para nossa própria janela e retornamos… A mensagem WM_CLOSE será colocada na fila de mensagens da janela e tratada, mais tarde, pela função DefWindowProc() que, por sua vez, colocará a mensagem WM_DESTROY na file e assim por diante…

PS: A questão interessante que citei antes é: “Como diabos o Windows sabe o ícone da aplicação sem executá-la?”. Note que o ícone aparecerá no file manager ou em seu desktop se você copiar o arquivo ou o atalho para lá… Deixo para você pesquisar a respeito! :)

Anúncios