Tutorial de C para Windows
(para iniciantes)
Uma janela vazia

Esse tutorial é para você que já sabe programar em C, e apenas quer migrar para o Windows. Bons compiladores comerciais de C/C++ pra Windows são Visual C++ e Borland C++, mas há um compilador grátis na Internet, o LCC. 

Eu mesmo sou extremamente iniciante em programação para Windows, portanto, não garanto que este tutorial vá ensinar a melhor ou mais rápida maneira de programar para Windows, mas estou aberto a sugestões. 
 

Ao contrário do tutorial anterior, desta vez eu não vou começar com um exemplo, porque este ficaria meio grande demais e poderia assustar... Criar uma simples janela vazia envolve um mecanismo relativamente complexo, e é melhor que você primeiro entenda o mecanismo. 

Quando você quer criar uma janela no Windows, você tem que fazer tudo isso: 

  1. Criar a classe da sua janela, incluindo nela alguns dados iniciais.
  2. Registrar a classe da janela.
  3. Criar a janela em si, informando outros dados importantes para sua criação.
  4. Mostrar a janela na tela.
  5. Verificar se aconteceu alguma coisa envolvendo a sua janela.
  6. E tomar as providências necessárias se acontecer alguma coisa com sua janela...
Fala sério, uma simples janela não parecia ser tão complexa antes de você ler esses itens... =) 

Eu vou ir fazendo um exemplo aos poucos, assim fica mais fácil de entender. 
 

Antes de qualquer outra coisa, vamos fazer o "esqueleto" de qualquer programa para Windows. Nesse "esqueleto" eu já vou colocar declarações algumas variáveis e funcões (em azul), mas você não precisa nem ligar pra isso agora (finja que não está aí, você vai se sentir melhor...). 
 



/* EX03.C - uma simples janela vazia */ 
#include <windows.h> 

const char *NOME = "TantoFaz", 
           *TITULO = "Capítulo 2 - Abrindo janelas"; 

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL); 

int WINAPI WinMain(HINSTANCE hInstancia, HINSTANCE hInstanciaAnt, 
           char *LinhaCmd, int Visualizacao) 
{ 
  HWND hJanela; 
  MSG Mensagem; 
  WNDCLASS ClasseDaJanela; 
} 



 
Agora vamos realmente começar a implementar aqueles itens do início do capítulo: 
 

1. Criar a classe da sua janela, incluindo nela alguns dados iniciais.  

Ok, a primeira coisa que você deve fazer é declarar uma variável do tipo WNDCLASS (classe de janela). No nosso exemplo (acima), nós temos: 

WNDCLASS ClasseDaJanela;

Entao, ClasseDaJanela passa a ser um struct com os mesmos elementos de WNDCLASS (definido em win.h). Aí continua nosso exemplo, dessa vez definindo valores para esses elementos (os dados iniciais): 



#include <windows.h> 

const char *NOME = "TantoFaz",  
           *TITULO = "Capítulo 2 - Abrindo janelas"; 

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL); 

int WINAPI WinMain(HINSTANCE hInstancia, HINSTANCE hInstanciaAnt, 
           char *LinhaCmd, int Visualizacao) 
{ 
  HWND hJanela; 
  MSG Mensagem; 
  WNDCLASS ClasseDaJanela; 

  ClasseDaJanela.style = CS_HREDRAW | CS_VREDRAW; 
  ClasseDaJanela.lpfnWndProc = FuncaoDaJanela; 
  ClasseDaJanela.cbClsExtra = 0; 
  ClasseDaJanela.cbWndExtra = 0; 
  ClasseDaJanela.hInstance = hInstancia; 
  ClasseDaJanela.hIcon = LoadIcon(hInstancia, NULL); 
  ClasseDaJanela.hCursor = LoadCursor(NULL, IDC_ARROW); 
  ClasseDaJanela.hbrBackground = GetStockObject(WHITE_BRUSH); 
  ClasseDaJanela.lpszClassName = NOME; 
 



 

Agora acompanhe no quadro a descrição de cada elemento: 
 
 

Item  Descrição
style Estilo da janela. Nesse caso, o estilo é algo como redesenhar ou não a janela quando o usúario aumentar seu tamanho, se ela deve ou não ser fechada, etc. As definições de estilo podem ser encontradas no win.h, sempre começando com CS_. Para colocar vários estilos, separe-os com o o símbolo |.
lpfnWndProc  A função que deve ser chamada quando acontecer algo à janela. Perceba que essa função é declarada antes do WinMain, no início do programa (LRESULT CALLBACK FuncaoDaJanela .......)
cbClsExtra  ---
cbWndExtra  ---
hInstance  A instância do programa (a variável que identifica o seu programa). Basta passar a variável que foi passada para WinMain (no nosso caso, hInstancia)
hIcon O ícone do seu programa. Nosso exemplo utiliza um ícone padrão.
hCursor  O cursor de mouse que seu programa vai usar. Veja as definições começadas com IDC_, no win.h.
hbrBackground A cor de fundo da janela. As opções são: BLACK_BRUSH, DKGRAY_BRUSH, GRAY_BRUSH, HOLLOW_BRUSH, LTGRAY_BRUSH, NULL_BRUSH, WHITE_BRUSH.
lpszMenuName  ---
lpszClassName  Nome do seu programa (usado internamente pelo Windows).
 

Agora que já definimos os valores de cada elemento, temos que informar ao Windows que está tudo pronto, só falta portanto registrar a classe. 

2. Registrar a classe da janela. 

Depois que você fez a pior parte do trabalho, basta um comando para registrar a classe de janela: 

RegisterClass(&ClasseDaJanela); 

Isso basta para registrar a classe, mas pode ocorrer de você ter feito alguma coisa errada no passo anterior, e aí o Windows não vai conseguir registrar a classe... Nesse caso, RegisterClass vai retornar 0 (a mesma coisa que FALSE). Portanto você pode fazer com que o programa exiba uma mensagem de texto e saia, o que poderia ficar assim: 

if (!RegisterClass(&ClasseDaJanela)) 
{ 
  MessageBox(NULL, "Nao foi possivel abrir a janela.\nPressione OK para finalizar.", "Erro", MB_OK + MB_ICONERROR); 
  return 0; 
} 

Note que ainda usamos NULL no MessageBox, já que nossa janela ainda não foi criada... 

Observação1: Coloque o comando MessageBox em uma linha só. 
Observação2: Adicione o código acima ao resto do programa (antes da chave "}") 

Estamos prontos portanto para o próximo passo: 

3. Criar a janela em si, informando outros dados importantes para sua criação. 

Podemos dizer que esse terceiro passo é tão detalhado quanto o primeiro. Para criar a janela (depois de ter registrado a classe), você usa apenas uma função: CreateWindow, que cria a janela e retorna um "handle" para a janela. Mas essa função tem muitos parâmetros (11, para ser exato). Ta aí o exemplo: 

hJanela = CreateWindow(NOME, TITULO, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, hJanela, hInstancia, NULL); 

Inclua isso no programa também. 
Lembre-se que hJanela é declarado no nosso exemplo no início do WinMain (HWND hJanela;) 
Vou explicar o que significa cada parâmetro do CreateWindow: 

    1. Nome do seu programa que o Windows usa internamente. 
    2. Título da janela. 
    3. Tipo de janela. Há várias opções, que você pode encontrar no win.h, todas começadas com WS_. 
    4. Posição (coordenada X) do lado esquerdo da janela (CW_DEFAULT faz com que o Windows determine a posição). 
    5. Posição (coordenada Y) do canto superior da janela (pode usar CW_DEFAULT também). 
    6. A largura da janela (use CW_DEFAULT se quiser). 
    7. A altura da janela (veja acima). 
    8. A janela a qual a sua janela pertence. NULL significa que a sua janela não pertence a nenhuma outra. 
    9. "Handle" para o menu (não usamos menu, portanto: NULL) 
    10. A instância do seu programa. 
    11. Não nos interessa no momento.
É isso... Agora, quando você quiser fazer qualquer coisa com sua janela, você deve usar o "handle" hJanela. 

Próximo passo: 

4. Mostrar a janela na tela. 

Já essa parte é tão fácil quanto o passo 2: 

ShowWindow(hJanela, Visualizacao); 

Não preciso dizer que você deve incluir isso no programa... 
Acho que não necessita explicações. O primeiro parâmetro indica a janela que será mostrada na tela e o segundo parâmetro indica como a janela será visualizada (maximizada, minimizada, etc.). No nosso exemplo, eu apenas passo a variável Visualizacao, que é passada para o programa quando ele se inicia. Mas se quiser você pode usar SW_HIDE, SW_SHOWMAXIMIZED, etc. (veja win.h). 

O penúltimo passo (estamos quase terminando): 

5. Verificar se aconteceu alguma coisa envolvendo a sua janela. 

Você deve se lembrar do exemplo no início do tutorial, onde tínhamos:  

MSG Mensagem;

Pois é, é através dessa variável que o Windows vai enviar mensagens pra você, do tipo: "O usuário mandou fechar a janela", "O usuário movimentou o mouse", etc. Mas é claro que o Windows não se comunica com você dessa maneira... Quando você declara uma variável do tipo MSG, você na verdade está declarando uma struct com os seguintes elementos: 

hwnd - indica em que janela a ação foi executada. 
message - um número que indica o que aconteceu. 
wParam - um complemento da mensagem. No caso da mensagem WM_MOUSEMOVE, por exemplo, wParam especifica os estados dos botões do mouse e outras coisas que não vêm ao caso. 
lParam - outro complemento da mensagem. No caso da mensagem WM_MOUSEMOVE, por exemplo, lParam especifica as coordenadas do mouse (x e y). 
time - não vamos utilizar por enquanto. 
pt - também não vamos utilizar no momento. 
  
Aprendendo isso, você pode entender melhor o que está por vir. 
Mas o que vamos adicionar ao programa será o seguinte: 

while(GetMessage(&Mensagem, hJanela, 0, 0)) 
{ 
  DispatchMessage(&Mensagem); 
} 
return Mensagem.wParam;  

GetMessage vai retornar 0 (ou seja, FALSE) quando o Windows enviar uma mensagem WM_QUIT. Portanto, enquanto isso não acontecer, o programa executa DispatchMessage(&Mensagem);. Esse comando DispatchMessage apenas chama FuncaoDaJanela, passando todos os parâmetros necessários. E, quando GetMessage retornar 0, o programa será finalizado retornando um código de erro. No caso, wParam contém o código de erro que o Windows definiu, e recomendo que você o use. 

A esta altura, nosso programa deve estar assim: 



/* EX03.C - uma simples janela vazia */ 
#include <windows.h> 

const char *NOME = "TantoFaz", 
           *TITULO = "Capítulo 2 - Abrindo janelas"; 

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL); 

int WINAPI WinMain(HINSTANCE hInstancia, HINSTANCE hInstanciaAnt, 
           char *LinhaCmd, int Visualizacao) 
{ 
  HWND hJanela; 
  MSG Mensagem; 
  WNDCLASS ClasseDaJanela; 

  ClasseDaJanela.style = CS_HREDRAW | CS_VREDRAW; 
  ClasseDaJanela.lpfnWndProc = FuncaoDaJanela; 
  ClasseDaJanela.cbClsExtra = 0; 
  ClasseDaJanela.cbWndExtra = 0; 
  ClasseDaJanela.hInstance = hInstancia; 
  ClasseDaJanela.hIcon = LoadIcon(hInstancia, NULL); 
  ClasseDaJanela.hCursor = LoadCursor(NULL, IDC_ARROW); 
  ClasseDaJanela.hbrBackground = GetStockObject(WHITE_BRUSH); 
  ClasseDaJanela.lpszClassName = NOME; 

  if (!RegisterClass(&ClasseDaJanela)) 
  { 
    MessageBox(NULL, "Nao foi possivel abrir a janela.\nPressione OK para finalizar.", "Erro", MB_OK + MB_ICONERROR); 
    return 0; 
  }  

  hJanela = CreateWindow(NOME, TITULO, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, hJanela, hInstancia, NULL); 

  ShowWindow(hJanela, Visualizacao); 

  while(GetMessage(&Mensagem, hJanela, 0, 0)) 
  { 
    DispatchMessage(&Mensagem); 
  } 

  return Mensagem.wParam; 
} 


Ufa! Exemplo bem grandinho, no entanto, não é hora de compilar ainda... Precisamos ainda fazer a função que vai cuidar dos eventos da janela. A declaração de uma função desse tipo fica mais ou menos assim:

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, WPARAM ParamW, LPARAM ParamL);

Eu tenho que dizer uma coisa... O win.h é cheio de definições e typedefs. Por exemplo, se você quer dizer long, você pode dizer LRESULT, LPARAM, LONG, entre outros... são a mesma coisa que long. UINT e WPARAM são a mesma coisa que  unsigned int. Então por que existem tantas formas diferentes de se dizer uma mesma coisa? Eu tenho um palpite. Acho que isso é para quem estar lendo seu código perceber logo de que se trata determinada variável.

Você não vai precisar chamar essa função: o DispatchMessage já faz isso, e passa todos os parâmetros necessários (que mordomia!)... Mas acho que você deve saber que parâmetros o DispatchMessage passa para FuncaoDaJanela. 

No primeiro parâmetro, DispatchMessage passa Mensagem.hwnd
No segundo parâmetro, DispatchMessage passa Mensagem.message
No terceiro parâmetro, DispatchMessage passa Mensagem.wParam
No quarto parâmetro, DispatchMessage passa Mensagem.lParam

Tudo o que temos que fazer, portanto, é checar qual a mensagem do Windows e, se nos interessar, escrever o que deve ser feito. Você pode colocar um tanto de if's, mas é pra evitar isso que existe o switch, afinal. Então, depois do WinMain, coloque o seguinte:

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL)
{
  switch (MsgID)
  {
    case WM_CLOSE:
    case WM_DESTROY:
      if (MessageBox(hJanela, "Deseja realmente sair?", "Sair", 
      MB_YESNO + MB_ICONQUESTION)==IDYES)
         PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hJanela, MsgID, ParamW, ParamL); 
  }

  return 0;
}
 

Calma, vou explicar... Primeiro, verificamos qual fo a mensagem. Se a mensagem foi WM_CLOSE ou WM_DESTROY, vai aparecer a pergunta "Deseja realmente sair?". Se o usuário escolher Sim, o programa acaba. Se o usuário escolher Não, o programa continua e FuncaoDaJanela vai retornar 0, indicando que o Windows não precisa fazer nada, está tudo sob controle.

Mas se qualquer acontecer qualquer outra coisa, então FuncaoDaJanela vai retornar DefWindowProc(hJanela, MsgID, ParamW, ParamL). E para que serve essa função DefwindowProc? Para cuidar das mensagens que não cuidamos em FuncaoDaJanela (seriam necessários muitos case para cuidar de todas as mensagens que o Windows pode retornar...). Você apenas passa os parâmetros que foram passados para FuncaoDaJanela. A partir disso, DefWindowProc vai retornar um número, e você repassa ele. Através desse número é que o Windows vai decidir o que ele vai fazer, mas isso não tem nada a ver com seu programa, é responsabilidade do Windows. Apenas retorne o valor que DefWindowProc retornou, e o resto acontece sozinho...

Aqui acaba o nosso exemplo, que fica assim:
(Não se esqueça de deixar o MessageBox em uma linha só. Se você dividir o MessageBox em duas linhas de um jeito errado, você vai obter vários erros...)



/* EX03.C - uma simples janela vazia */ 
#include <windows.h> 

const char *NOME = "TantoFaz", 
           *TITULO = "Capítulo 2 - Abrindo janelas"; 

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL); 

int WINAPI WinMain(HINSTANCE hInstancia, HINSTANCE hInstanciaAnt, 
           char *LinhaCmd, int Visualizacao) 
{ 
  HWND hJanela; 
  MSG Mensagem; 
  WNDCLASS ClasseDaJanela; 

  ClasseDaJanela.style = CS_HREDRAW | CS_VREDRAW; 
  ClasseDaJanela.lpfnWndProc = FuncaoDaJanela; 
  ClasseDaJanela.cbClsExtra = 0; 
  ClasseDaJanela.cbWndExtra = 0; 
  ClasseDaJanela.hInstance = hInstancia; 
  ClasseDaJanela.hIcon = LoadIcon(hInstancia, NULL); 
  ClasseDaJanela.hCursor = LoadCursor(NULL, IDC_ARROW); 
  ClasseDaJanela.hbrBackground = GetStockObject(WHITE_BRUSH); 
  ClasseDaJanela.lpszClassName = NOME; 

  if (!RegisterClass(&ClasseDaJanela)) 
  { 
    MessageBox(NULL, "Nao foi possivel abrir a janela.\nPressione OK para finalizar.", "Erro", MB_OK + MB_ICONERROR); 
    return 0; 
  }  

  hJanela = CreateWindow(NOME, TITULO, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 100, NULL, hJanela, hInstancia, NULL); 

  ShowWindow(hJanela, Visualizacao); 

  while(GetMessage(&Mensagem, hJanela, 0, 0)) 
  { 
    DispatchMessage(&Mensagem); 
  } 

  return Mensagem.wParam; 
} 

LRESULT CALLBACK FuncaoDaJanela(HWND hJanela, UINT MsgID, 
                 WPARAM ParamW, LPARAM ParamL)
{
  switch (MsgID)
  {
    case WM_CLOSE:
    case WM_DESTROY:
      if (MessageBox(hJanela, "Deseja realmente sair?", "Sair", 
      MB_YESNO + MB_ICONQUESTION)==IDYES)
         PostQuitMessage(0);
      break;
    default:
      return DefWindowProc(hJanela, MsgID, ParamW, ParamL); 
  }

  return 0;
}


Pode compilar e executar, deve funcionar. Eu mesmo copiei o código daqui, colei no Bloco de Notas, coloquei o Messagebox em uma linha só, gravei como EX03.C, compilei, executei e funcionou. Vai aparecer uma janela pequena, com fundo branco, vazia, mas você sabe o quanto foi difícil fazer essa pobre janela aparecer na tela e fazer tudo o que ela faz =)


E-mail
Copyright © 1998 - Rodrigo Rocha Gomes e Souza
ICQ: 7745525 | Nickname: xROD
1