Criando um Aplicativo WindowsAutor : Albrecht |
Este tutorial foi escrito para dar uma noção básica de programação de aplicativos Windows utilizando a Win32 API.
O Objetivo final é que você aprenda a escrever um aplicativo, contendo uma janela. O código de fonte do aplicativo criado, será um esqueleto para seus aplicativos utilizando OpenGl e Directx. e que possua o compilador Visual C++ 6. Caso não tenha o Visual C++ 6, pegue o Dev-C++ em http://www.bloodshed.net/
Espero que aproveite.
Felipe Albrecht Primeiramente criaremos um "Hello, world" simples para windows.
Um "Hello, world" deste tipo para Dos seria algo assim:
#include <stdio.h> void main( ) { printf("Hello, world! "); Vamos ver o "Hello, world" para windows:
#include<windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { return 0; } Vamos agora analisar o código: A primeira linha: #include<windows.h> É
A segunda linha:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) Nos programas em C e C++ para dos, Linux, Unix e etc, a função inicial é a "main", no Windows, a função main é a WinMain. Neste caso ela esta retornando um int e o WINAPI é uma convenção utilizada quando se usa alguma
int WINAPI WinMain(
HINSTANCE hInstance , // ponteiro para a instancia atual; HINSTANCE hPrevInstance , // (Ultrapassado!) LPSTR lpCmdLine , // ponteiro para a linha de comando; int nCmdShow // estado que deve ser mostrada a janela. ); Vocês irão me perguntar o que é "Instancia": Atravez dela o windows
Por enquanto é isso que se precisa saber. Na próxima seção estudaremos com mais detalhes questões mais complexas.
Começando a criação
Nesta seção vamos começar a criar o nosso aplicativo.
Confesso que é complicado explicar o funcionamento e a programação de um aplicativo Win32.
A primeira vez que li como criar um programa com uma janela para o windows, fiquei bem confuso. Tentarei utilizar uma maneira simples de explicar o funcionamento de todo aplicativo, irei cortar-lo em pedaços e explicar cada parte separadamente.
Um programa windows básico, se divide em tantas partes:
* A inicialização ( WinMain );
* A criação da janela ( CreateWindowEx );
* O loop de mensagens ( PeekMessage);
* O processamento das mensagens ( WndProc ).
Inicialização:
Protótipo da função de inicialização: int WINAPI WinMain( HINSTANCE hPrevInstance , // (Ultrapassado!) LPSTR lpCmdLine , // ponteiro para a linha de comando; int nCmdShow // estado que deve ser mostrada a janela. );
Explicando:
Quando o programa é carregado, a primeira função do programa a ser chamada é a WinMain. A função WinMain também passa a Instancia do aplicativo pelo parâmetro hInstance. A WinMain também passa para o programa a linha de comando excluindo o nome do programa. Também é especificado na WinMain como a janela do aplicativo deve ser mostrada.
Criação da janela:
Para se criar uma janela, é necessário primeiro se criar uma classe que conterá os dados da janela criada. A Win32 API provede um struct para definir os dados da janela, o nome desta struct é WNDCLASSEX.
Abaixo é apresentado o protótipo desta classe:
typedef struct _WNDCLASSEX { // Classe da janela;
UINT cbSize; // Tamanho da janela; UINT style; // Estilos da janela; WNDPROC lpfnWndProc; // Ponteiro para funções; int cbClsExtra; // nao sera utilzado; int cbWndExtra; // nao sera utilzado; HANDLE hInstance; // Instancia do aplicativo; HICON hIcon; // Ícone da janela; HCURSOR hCursor; // Cursor padrão da janela; HBRUSH hbrBackground; // Cor de fundo da janela; LPCTSTR lpszMenuName; // Menu da janela; LPCTSTR lpszClassName; // Nome da classe HICON hIconSm; // Ícone pequeno da janela. } WNDCLASSEX; Para criar a variável da classe da janela, é só definir:
WNDCLASSEX wndClass;
Depois ir definindo os atributos: windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WndProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;Para a janela saber qual é a instancia utilizada.
windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); Define qual será o ícone “grande” da janela. Neste caso será um ícone padrão do windows. windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); Define qual será o cursor padrão da janela. windowClass.lpszClassName = "MyClass";O nome da classe. windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
Depois falta apenas registrar a classe:
RegisterClassEx(&windowClass);
Pronto! A classe da sua janela já está definida e registrada, pronta para o uso.
Criando a janela. Com a classe da janela setada e registrada, podemos criar a janela. Como foi mostrado no início do capítulo, a criação de uma janela é feita utilizando a função CreateWindowEx(). Protótipo da função de criação da janela: HWND CreateWindowEx( DWORD dwExStyle , // o estilo da janela extendido. LPCTSTR lpClassName , // ponteiro para o nome da classe registrada. LPCTSTR lpWindowName , // ponteiro para o nome da janela. DWORD dwStyle , // estilo da janela. int x, // posição horizontal da janela. int y, // posição vertical da janela. int nWidth , // comprimento da janela. int nHeight , // altura da janela HWND hWndParent, // ponteiro para ohandle to parent or owner window HMENU hMenu , // ponteiro para o menu HINSTANCE hInstance , // ponteiro para a Instancia do aplicativo. LPVOID lpParam // dados para a criação da janela. ); Leia os comentarios de cada paramêntro, a maioria é alto explicativo. Irei explicar algum dos paremetros mais importantes: lpClassName : Este parametro diz o nome da classe da janela que voce pretende utilizar. Já como vamos utilizar a classe criada anteriormente, deverá ser setado como “MyClass”. lpWindowName : O nome da janela, e também o título a ser mostrado. Poderiamos colocar como “Meu Primeiro Aplicativo”. dwStyle : É uma flag que diz para o CreateWindowEx como a janela deve se comportar e aparecer. Alguns exemplos: WS_TILED Cria uma janela com título e borda. WS_MAXIMIZEBOX Cria uma janela que tem o botão de maximizar. WS_MINIMIZEBOX Cria uma janela que tem o botão de minimizar. WS_CAPTION Cria uma janela que contem a barra de título WS_SYSMENU Cria uma janela que contém o menu na sua barra de título. (WS_CAPTION tem que estar setado) WS_SIZEBOX Cria uma janela que contenha borda para redimensionar o seu tamanho. WS_OVERLAPPEDWINDOW Cria uma janela com as especificações de todos anteriormente citados.
A função CreateWindowEx() retorna o valor NULL se não foi criada uma janela com sucesso, se não a função retorna um ponteiro para a janela criada.
Agora que sabemos os parametros, podemos usar a função: hwnd = CreateWindowEx(NULL, // Sem estilo adicionais "MyClass", // Nome da classe da janela a ser usada "A REAL Windows Application!", // Nome do aplicativo WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU, // Estilos 100, 100, // x,y cordenadas 400, 400, // largura, altura NULL, // Ponteiro para parentes (Não há) NULL, // Ponteiro para o menu hInstance, // Instancia do aplicativo NULL); // Parametros extras (Não há) Este código criará uma janela de 400 x 400, que quando visível ficará na esquerda ao topo. A janela está pronta, na próxima seção iremos ver o Loop de Mensagens. Ultimos Detalhes
Já iniciamos o programa e criamos uma janela. Agora falta fazer ele funcionar. Como isso?
Com o loop de mensagens e o processamento destas mensagens. Os aplicativos se comunicam com o windows através de uma fila de mensagem. Por causa desta fila, todos programas Windows necessitam por um loop de mensagem (Message Loop) na função WinMain(). O loop checa e lê as mensagens pendentes da fila e manda uma mensagem de volta para o Windows. Se usa a função PeekMessage() para checar se há mensagens na fila. Aqui está o protótipo da função PeekMessage():
BOOL PeekMessage(
LPMSG lpMsg, // informação sobre a mensagem HWND hWnd, // ponteiro para a janela UINT wMsgFilterMin, // primeira mensagem UINT wMsgFilterMax, // ultima mensagem UINT wRemoveMsg // opções para remover a mensagem ) Se uma mensagem está na fila de espera, o PeekMessage() pega ela, tira da fila e a coloca no parâmetro lpMsg. Especificando NULL para o parâmetro hWnd, faz com que o PeekMessage() cheque a pilha do aplicativo corrente. Os parâmetros wMsgFilterMin e wMsgFilterMax serão setados como NULL para o nosso propósito. O último paramento, wRemoveMsg será determinado com o valor PM_REMOVE, que dirá ao PeekMessage() para remover mensagens depois que elas forem processadas. Outra função utilizada é a TranslateMessage(). Ela traduz as mensagens básicas do teclado (WM_CHAR, WM_KEYDOWN, WM_KEYUP) em mensagens WM_COMMAND dependendo dos aceleradores (atalhos) de teclado configurados.
A ultima função do loop de mensagens é a DispatchMessage().Ela manda a mensagem para a janela (e callback de janela) adequada.
Vamos observar o código de um loop de mensagem: while (!done) { if (msg.message == WM_QUIT) // recebesse a mensagem QUIT { done = true; // se sim, ento hora de sair. } else { DispatchMessage(&msg); // mande a mensagem para a lista de }
}
return msg.wParam; Quando se entra no loop, PeekMessage() checa se há alguma mensagem na fila de espera.Se encontrado uma mensagem, e se ela for uma mensagem WM_QUIT, então o aplicativo chama o código específico para processar a mensagem. Após isto, a mensagem é traduzida e despachada. O loop de mensagens provê uma maneira particularmente simples e efetiva de tratar uma larga quantidade de mensagens que o Windows manda para o aplicativo. Agora já temos as mensagens “pegas” pelo loop de mensagens, falta processa-las. O problema está que o windows manda centenas mensagens diferentes para o aplicativo, então como pegar as mensagens e como pegar apenas as que nos importa? A solução encontrada é utilizar o WndProc(). O WndProc atravéz de um switch , pega as mensagens de acordo com o conteúdo delas. O protótipo da função WndProc é LRESULT CALLBACK WndProc( HWND hwnd, // ponteiro para a janela UINT uMsg, // mensagem WPARAM wParam, // primeiro paramentro da mensagem LPARAM lParam // segundo parametro da mensagem: );
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ switch(message) case WM_CLOSE: // a janela é fechada PostQuitMessage(0); // Então saia do programa return 0; break; case WM_PAINT: // Janela necessita ser repintada BeginPaint(hwnd, &paintStruct); EndPaint(hwnd, &paintStruct); return 0; break; default: Este exemplo realmente não faz muito, de fato a única coisa que faz, é quando chega um WM_CLOSE, ele faz o programa fechar. Percebe-se 3 “cases” no exemplo: O WM_CREATE é chamado quando o programa é inicializado. Ali é o melhor lugar para se por os códigos iniciais do programa. O WM_CLOSE é chamado quando a janela é fechada. O mais normal neste caso é utilizar a função PostQuitMessage(0) para sair do programa. O parametro 0 (zero), diz que houve uma saída sem erros. Por fim, o WM_PAINT, ele é chamado quando a janela nescessita ser repintada. Como o objetivo final deste artigo é fazer uma janela na qual apareça escrito “Hello, world”, será nesta local será posta a parte que escreve na tela o “Hello, world”. Primeiramente deve-se criar um ponteiro para o conteúdo de um dispositivo. O nome é estranho, mas pense sendo apenas o ponteiro para alguma função. Ele é declarado assim: HDC hDC; Depois deve-se declarar uma estrutura onde será controlada a pintura. No exemplo acima ela já está declarada: PAINTSTRUCT paintStruct; Por fim , declarar o que será escrito na tela, no nosso caso “Hello, World”. Char string[] = “Hello, world”; Dentro do case WM_PAINT, deve-se por tudo em operação: hDC = BeginPaint(hwnd, &paintStruct); SetTextColor(hDC, COLORREF(0x00FF0000)); TextOut(hDC, 150, 150, string, sizeof(string)-1); EndPaint(hwnd, &paintStruct); return 0;// Retorna sem erro Pronto! Iniciamos o aplicativo, definimos como deverá ser a janela, iniciamos a janela, cuidamos do loop de mensagem, cuidamos do tratamento das mensagens e por por fim escrevemos na tela nosso “Hello, word”. A próxima seção será o código de fonte funcional do aplicativo e comentado os principais pontos.
Caso queira baixar o source clique aqui
Código de Fonte
#include <windows.h> // Header das funções padrões do windows
// O parte do programa que trata as funções do Windows. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hDC; // Contexto do dispositivo char string[] = "Hello, world!"; // Texto a ser mostrado switch(message) { case WM_CREATE: // A janela é criada return 0; break; case WM_CLOSE: // A janela é fechada PostQuitMessage(0); // Sai do programa return 0; break; case WM_PAINT: // A janela presisa ser repintada hDC = BeginPaint(hwnd, &paintStruct); SetTextColor(hDC, COLORREF(0x00FF0000)); // seta a cor do texto como azul. TextOut(hDC, 150, 150, string, sizeof(string)-1); // mostra o texto no meio da tela EndPaint(hwnd, &paintStruct); return 0; break; break; } return (DefWindowProc(hwnd, message, wParam, lParam)); } // Função inicial do programa int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { WNDCLASSEX windowClass; // Classe da janela HWND hwnd; // “Apelido” para a janela MSG msg; // Mensagem bool done; // Flag diz quando o programa está pronto // Define a strutura da classe do window windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WndProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); windowClass.lpszMenuName = NULL; windowClass.lpszClassName = "MyClass"; windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // Registra a classe da janela if (!RegisterClassEx(&windowClass)) return 0; // Classe registrada, então vamos criar a janela hwnd = CreateWindowEx(NULL, "MyClass", // Nome da classe "A REAL Windows Application!", // nome do aplicativo WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU, 100, 100, // cordenadas x, y 400, 400, // Comprimento e altura NULL, // Ponteiro para o parente NULL, // Ponteiro para o menu hInstance, // Instancia do aplicativo NULL); // Sem parametros extras // Checa para ser se a janela foi criada. if (!hwnd) // caso não seja, saia do programa return 0;
done = false; // Inicializa a variaval do loop
// Principal loop de mensagem
while (!done) { PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE); if (msg.message == WM_QUIT) // Recebesse uma mensagem de saida? { done = true; // Se sim, é hora de sair do loop. } else { TranslateMessage(&msg); // Traduza a mensagem DispatchMessage(&msg); // Mande a mensagem para a fila } } return msg.wParam; } |
sábado, 8 de janeiro de 2011
Criando um Aplicativo Windows
Assinar:
Postar comentários (Atom)
Nenhum comentário:
Postar um comentário