Criando a instancia de uma janela(windows).
Quando criamos um projeto do windows no Visual Studio, ele nos fornece o código abaixo para a criação de uma janela padrão:
// Comment #include "stdafx.h" #include "FirstWindow.h" #define MAX_LOADSTRING 100 // Global Variables: HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);Podemos notar que ele cria três variáveis(hInst,szTitle e szWindowClass ).
hInst -> Variável global para armazenar o handle da instância da nossa aplicação.
szTitle -> Array de caracteres wchar_t(UNICODE) que dará nome a nossa aplicação.
szWindowClass -> Arra de wchar_t que dará nome a nossa primeira janela.E quatro funções(MyRegisterClass,InitInstance,WndProc e About).
MyRegisterClass-> É usada para instanciar a minha classe WNDCLASSEX, usada para descrever algumas propriedades da minha janela.
InitInstance-> Aqui é feita a chamada para a função CreateWindow, onde minha janela é criada.
WndProc-> Este ponto é muito importante, pois, é onde processamos todas as mensagens passadas pelo sistema operacional,mouse, teclado etc ... para o handle da nossa janela.
About-> É o callback de uma janela secundária.
Vamos falar um pouco sobre essa abordagem da MS para a criação deste projeto genérico de win32 e porque ele não é o mais apropriado para uma game engine.
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);//Previne algumas Warnings Messages do compilador.
//Isso acontece quando algum parâmetro não é usado no corpo da função.
//Apenas uma boa prática de programação, pois assim podemos compilar nosso código sem warnings por parte do VS.
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_FIRSTWINDOW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
//Dá nome a nossa aplicação e a nossa primeira janla
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_FIRSTWINDOW));
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
Acima podemos verificar nosso método main( wWinMain ) com alguns comentários, mas minha principal observação é na função GetMessage.Uma alternativa ao GetMessage é a função PeekMessage, muitas aplicações Win32 usam GetMessage, o problema é que ao usar GetMessage ficamos bloqueados dentro laço esperando por uma mensagem.
while (GetMessage(...) > 0) { TranslateMessage(); DispatchMessage(); }
Enquanto a função PeekMessage não, essa função consulta a queue de mensagens, ela retorna a primeira mensagem da fila, se não houver nenhuma mensagem ela retorna NULL.
while (WM_QUIT != uMsg.message)
{
while (PeekMessage (&uMsg, NULL, 0, 0, PM_REMOVE) > 0) //Or use an if statement
{
TranslateMessage (&uMsg);
DispatchMessage (&uMsg);
}
}
O processamento de mensagens é uma parte crítica da programação Win32, através dela nos comunicamos com o Sistema Operacional, mouse, teclado etc... quando uma aplicação não responde as mensagens ao sistema operacional, o sistema informa ao usuário que a aplicação não está respondendo. O usuário por sua vez acredita que a aplicação travou, ou que algum erro ocorreu. Fazemos isso através do PeekMessage, TranslateMessage e DispatchMessage;
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FIRSTWINDOW));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_FIRSTWINDOW);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
WNDCLASSEX é a classe que descreve algumas propriedades de uma janela, está ligada a instância da nossa aplicação. Na propriedade lpfnWndProc é onde definimos qual será nossa função callback descrita mais abaixo.
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
Aqui criamos a nossa janela, uma das coisas que eu adicionaria a este sample da microsoft seria mudar a variável local hwnd para global, assim eu declararia ela no início do programa juntamente com as outras três variáveis comentadas no início do artigo.
// Global Variables:
g_hwnd = NULL; // global windows handle
HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
A função WinProc( windows procedure ) trata e processa as mensagens recebidas pela nossa janela,
Podemos observar que ela possui os mesmos parametros de uma mensagem, O primeiro é o handle da janela retornado na função CreateWindow, o segundo parametro identifica a mensagem, os ultimos dois são so parametros da mensagem.
Vamos imaginar que o usuário mexa o mouse, nossa janela vai receber uma mensagem do sistema operacional identificada como WM_MOUSEMOVE, para sabermos onde o mouse se encontra no eixo x e y devemos então obter os parametros da mensagem:
xPos = GET_X_LPARAM(lParam);
yPos = GET_Y_LPARAM(lParam);
Uma defProc bem básica abaixo demonstra por si só uma esqueleto bem básico:
switch (iMsg)
{
case WM_CREATE :
return 0 ;
case WM_PAINT :
return 0 ;
case WM_DESTROY :
return 0 ;
}
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
Podemos criar nossas próprias mensagens e processa-las em nossas janelas abaixo um exemplo:
#define MY_MSG_NOVO_DOCUMENTO (WM_USER+1)
mas falaremos disso em uma postagem mais adiante.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
Quando compilamos e rodamos o código padrão do Visual Studio para uma aplicação Windows, podemos observar no defproc da nossa janela que ela trata um clique no menu, para chamar uma janela about:
case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break;Neste momento criamos uma janela secundária, ela foi confeccionada anteriormente e ficou armazenada nos recursos do programa. Note que passamos o hWnd Owner, ou seja a janela pai, e o método About, que se trata do defproc para essa janela. que é tratado separadamente.
Nenhum comentário:
Postar um comentário