Tag: 2ª versão
Sessão 2/2 – Named Pipes, overlapped IO, duplex
Comunicação assíncrona – overlapped I/O
operações que normalmente bloqueiam até completarem (read, write) podem ser completadas em background pelo SO
isto consegue-se passando um ponteiro não nulo para uma estrutura do tipo OVERLAPPED
se na estrutura for passado um handle para um evento, esse evento é assinalado quando a operação for completada pelo SO
//escritor.c 2ª versao #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define TAM 256 #define PIPE_NMAX_INSTANCIAS 10 typedef struct { HANDLE hInstance; OVERLAPPED overlap; BOOL active; //se ja tem um cliente activo ou nao } PIPEDATA; PIPEDATA hPipes[PIPE_NMAX_INSTANCIAS]; HANDLE hEvents[PIPE_NMAX_INSTANCIAS], hMutex; int termina = 0; DWORD WINAPI ThreadMensagens(LPVOID param) { DWORD n; int i; TCHAR buf[TAM]; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(hMutex, INFINITE); for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { if (hPipes[i].active) { if (!WriteFile(hPipes[i].hInstance, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe %d! (WriteFile)\n"),i); exit(-1); }else{ _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } } } ReleaseMutex(hMutex); } while (_tcscmp(buf, TEXT("fim"))); //terminar termina = 1; for (i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { SetEvent(hEvents[i]); } return 0; } int _tmain(int argc, LPTSTR argv[]) { int i; int numClientes = 0; HANDLE hEventTemp, hPipeTemp, hThread; DWORD waitOffset, nbytes; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif hMutex = CreateMutex(NULL, FALSE, NULL); if(hMutex == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n")); exit(-1); } for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia %d do pipe '%s' ... (CreateNamedPipe)\n"), i, PIPE_NAME); hPipeTemp = CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_NMAX_INSTANCIAS, //mais clientes 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 1000, NULL // ); if (hPipeTemp == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Esperar ligação overlapped do leitor %d... (ConnectNamedPipe)\n"), i); hPipes[i].hInstance = hPipeTemp; hEventTemp = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEventTemp == NULL) { _tprintf(TEXT("[ERRO] Criar Event! (CreateEvent)")); exit(-1); } ZeroMemory(&hPipes[i].overlap, sizeof(hPipes[i].overlap)); hEvents[i] = hEventTemp; hPipes[i].overlap.hEvent = hEvents[i]; if (ConnectNamedPipe(hPipeTemp, &hPipes[i].overlap) != 0) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } hPipes[i].active = FALSE; } hThread = CreateThread(NULL, 0, ThreadMensagens, NULL, 0, NULL); if (hThread == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n")); exit(-1); } while(!termina && numClientes < PIPE_NMAX_INSTANCIAS) { _tprintf(TEXT("[ESCRITOR] Esperar pela ligçaão de leitor %d (WaitForMultipleObjects + GetOverlappedResult)\n"), numClientes); i = waitOffset - WAIT_OBJECT_0; _tprintf(TEXT("[ESCRITOR] Novo i = %d\n"), i); if(i>= 0 && i < PIPE_NMAX_INSTANCIAS) { ResetEvent(hEvents[i]); WaitForSingleObject(hMutex, INFINITE); _tprintf(TEXT("Ativar pipe %d\n"), i); hPipes[i].active = TRUE; ReleaseMutex(hMutex); numClientes++; } } WaitForSingleObject(hThread, INFINITE); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { if (!DisconnectNamedPipe(hPipes[i].hInstance)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } CloseHandle(hPipes[i].hInstance); } exit(0); }
encontrei mais informações sobre este exemplo em:
https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-overlapped-i-o
e a Comunicação bidirecional (duplex)
Sessão 1/2 – Named Pipes
Interprocessos (processo a falar uns com os outros):
Memoria partilhada
Named pipes
Threads do mesmo processo:
espaço de endereçamento do processo
Named pipes:
paradigma cliente <–> servidor
Um pipe com múltiplas instâncias, para resolver o problema de cinco clientes e um servidor
Nos namedPipes permitem comunicação bidirecional (se tiverem permissões de escrita e leitura para o cliente e servidor, mas não pode ser em simultâneo)
Servidor:
Tem uma thread para cada cliente
1º CreateNamedPipe
2º ConnectNamedPipe
3º Thread Secundária para atender os clientes
4º (fechar) FlushFileBuffers
5º (fechar) DisconnecNamedPipes
6º (fechar) CloseHandle
Cliente:
1º (ligar) CreateFile ou CallNamedPipe
2º (aguarda pela instância) WaitNamedPipe
3º SetNamedPipeHandleState
4º fechar os handles do processo
exemplos base
//escritor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { DWORD n; HANDLE hPipe; TCHAR buf[256]; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, sizeof(buf), sizeof(buf), 1000, NULL); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } while (1) { _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n); } while (_tcscmp(buf, TEXT("fim"))); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(hPipe)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } Sleep(2000); CloseHandle(hPipe); exit(0); }
//leitor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> // ... #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { TCHAR buf[256]; HANDLE hPipe; int i = 0; BOOL ret; DWORD n; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n")); hPipe = CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hPipe == NULL) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Liguei-me...\n")); while (1) { ret = ReadFile(hPipe, buf, sizeof(buf), &n, NULL); buf[n / sizeof(TCHAR)] = '\0'; if (!ret || !n) { _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n); break; } _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf); } CloseHandle(hPipe); Sleep(200); return 0; }
Nova versão com comnetários:
//escritor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { DWORD n; HANDLE hPipe; TCHAR buf[256]; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, //nome pipe PIPE_ACCESS_OUTBOUND, //modo de abertura do pipe só o servidor escreve PIPE_WAIT | // bloqueante: bloqueio ate escrever PIPE_TYPE_MESSAGE | // organização da informação: agrupa bytes em mensagens na escrita PIPE_READMODE_MESSAGE, // organização da informação: agrupa bytes em mensagens na leitura 1, // numero maximo de instancias sizeof(buf), // tamanho da mensagem sizeof(buf), // tamanho da mensagem 1000, // tempo de espera do cliente para usar no: waitNamedPipe (cliente) NULL // ); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } while (1) { _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n); } while (_tcscmp(buf, TEXT("fim"))); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(hPipe)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } Sleep(2000); CloseHandle(hPipe); exit(0); }
//leitor.c ou cliente.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { TCHAR buf[256]; HANDLE hPipe; int i = 0; BOOL ret; DWORD n; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) { //garantir que existe do lado do servidor algo ou definir um tempo ms _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n")); hPipe = CreateFile( PIPE_NAME, // no do ficheiro, nome do namedPipe GENERIC_READ, // (1) 0, // nao se aplica NULL, // nao interessa OPEN_EXISTING, // sempre OPEN, pois já esta criada FILE_ATTRIBUTE_NORMAL, // por default NULL // nao interessa ); //(1) //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_OUTBOUND então aqui tem que ser GENERIC_READ //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_INBOUND então aqui tem que ser GENERIC_WRITE //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_DUPLEX então aqui tem que ser GENERIC_READ | GENERIC_WRITE if (hPipe == NULL) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Liguei-me...\n")); while (1) { ret = ReadFile( hPipe, // handle para o pipe buf, // onde vai colocar a informação lida sizeof(buf), // a quantidade máxima que pode ler &n, // escreve o numero de bytes que foi lida, inclui o \0 NULL // overllaped nao estamos a usar ); buf[n / sizeof(TCHAR)] = '\0'; //ler o \0 no fim da string if (!ret || !n) { //quando surge o disconnect por parte do servidor _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n); break; } _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf); } CloseHandle(hPipe); Sleep(200); return 0; }
uma versão nova do escritor/servidor para aceitar vários clientes:
//escritor.c 2ª versao #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define PIPE_NMAX_INSTANCIAS 10 typedef struct{ HANDLE hPipesCliente[PIPE_NMAX_INSTANCIAS]; int numeroClientes; HANDLE hMutex; //mutexs acesso int terminar; //controlar o fim da thread } ThreadDados; //a thread DWORD WINAPI ThreadMensagens(LPVOID param) { ThreadDados* dados = (ThreadDados*)param; TCHAR buf[256]; DWORD n; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(dados->hMutex, INFINITE); //região critica.. dados->numeroClientes, dados->hPipesCliente[i] for(int i = 0; i < dados->numeroClientes; i++){ if (!WriteFile(dados->hPipesCliente[i], buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } //fim da região cirtica ReleaseMutex(dados->hMutex); } while (_tcscmp(buf, TEXT("fim"))); //terminar dados->terminar = 1; //i) simular o criar um novo cliente, para o "fim" funcionar CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return 0; } int _tmain(int argc, LPTSTR argv[]) { HANDLE hPipe; //thread ThreadDados dados; //f) HANDLE hThread; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif dados.numeroClientes = 0; dados.terminar = 0; //criar mutex dados.hMutex = CreateMutex(NULL, FALSE, NULL); if(dados.hMutex == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n")); exit(-1); } //g) hThread = CreateThread(NULL, 0, ThreadMensagens, &dados, 0, NULL); if(hThread == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n")); exit(-1); } //e) while (!dados.terminar) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_NMAX_INSTANCIAS, //mais clientes 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 1000, NULL // ); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } //b) WaitForSingleObject(dados.hMutex, INFINITE); //a) ciclo de aceitação dos novos clientes dados.hPipesCliente[dados.numeroClientes] = hPipe; dados.numeroClientes++; //b) ReleaseMutex(dados.hMutex); } //h) esperar que a thread termine WaitForSingleObject(hThread, INFINITE); //c) encerrar com todos os clientes for (int i = 0; i < dados.numeroClientes; i++) { _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(dados.hPipesCliente[i])) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } exit(0); }
sessão 11 – Programação gráfica orientada a eventos (2ª versão)
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Esta é a segunda revisão destes conteúdos, usar os recursos do win32 GDI:
1) adicionar recurso
2) escolher Icon
e apagar todos.. e ficar só com um.
3) aceder ao ID do recurso, e alterar para IDI_ICON_APP
4) adicionar um menu, com duas opções fixas e uma opção com submenus
o login, com popup a FALSE
e atribuir os ID a cada uma das opções.
5) as caixas de dialogo
e os componentes para a caixa de diálogo:
6) atalhos de teclas, aceleradores no windows
7) string table
ter um texto configurável, num recurso à parte. Utilidade: internacionalização da aplicação (tradução)
7) pointer
8) os comportamentos, usar o seguinte:
#include <windows.h> #include <Windowsx.h> #include <tchar.h> #include "resource.h" #define NUM_CLIENTES 3 #define LIST_SIZE 8 TCHAR* LIST_ITENS[] = { TEXT("10€"), TEXT("20€"), TEXT("40€"), TEXT("60€"), TEXT("80€"), TEXT("100€"), TEXT("150€"), TEXT("200€") }; LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLogin(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLevantar(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Ficha8"); typedef struct { unsigned int ID; TCHAR username[16]; TCHAR password[16]; unsigned int saldo; } cliente; typedef struct { unsigned int tipo; // 1 = depósito, 2 = levantamento unsigned int quantia; unsigned int ID; } operacao; typedef struct { cliente clientes[NUM_CLIENTES]; operacao historico[200]; unsigned int numOperacoes; } dados; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; HANDLE hAccel; dados dadosPartilhados; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); wcApp.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_PRINCIPAL); wcApp.cbClsExtra = sizeof(dados); wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(220, 220, 220)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("SO2 - Ficha 8"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 150, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); dadosPartilhados.numOperacoes = 5; // Apenas para testar... LONG_PTR x = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&dadosPartilhados); ShowWindow(hWnd, nCmdShow); hAccel = LoadAccelerators(NULL, MAKEINTRESOURCE(IDR_ACCELERATOR)); while (GetMessage(&lpMsg, NULL, 0, 0)) { if (!TranslateAccelerator(hWnd, hAccel, &lpMsg)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR str1[512], str2[512]; dados* dadosPartilhados; dadosPartilhados = (dados*)GetWindowLongPtr(hWnd, GWLP_USERDATA); switch (messg) { case WM_CREATE: EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_DISABLED | MF_GRAYED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_DISABLED | MF_GRAYED); break; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_LOGIN: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LOGIN), hWnd, TrataEventosLogin); EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_ENABLED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_ENABLED); break; case ID_CONSULTA: case ID_ACCELERATOR_CONSULTA: LoadString(NULL, IDS_STR_CONSULTA, str1, 512); _stprintf_s(str2, 512, TEXT("%s (%d)"), str1, dadosPartilhados->numOperacoes); MessageBox(hWnd, str2, TEXT("String Table"), MB_OK | MB_ICONINFORMATION); break; case ID_ACCELERATOR_LEVANTAMENTOS: case ID_LEVANTAMENTOS: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LEVANTAMENTO), NULL, TrataEventosLevantar); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, messg, wParam, lParam); break; } return(0); } LRESULT CALLBACK TrataEventosLogin(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR username[16]; switch (messg) { case WM_COMMAND: if (LOWORD(wParam) == IDOK) { GetDlgItemText(hWnd, IDC_EDIT_LOGIN, username, 16); MessageBox(hWnd, username, TEXT("Username"), MB_OK | MB_ICONINFORMATION); } else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hWnd, 0); return TRUE; } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; } LRESULT CALLBACK TrataEventosLevantar(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { int i; switch (messg) { case WM_INITDIALOG: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); SendMessage(hwndList, LB_RESETCONTENT, 0, 0); for (i = 0; i < LIST_SIZE; i++) SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)LIST_ITENS[i]); break; case WM_COMMAND: if (LOWORD(wParam) == IDC_LIST_MONTANTES) { switch (HIWORD(wParam)) { case LBN_DBLCLK: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); i = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); MessageBox(hWnd, LIST_ITENS[i], TEXT("ListBox"), MB_OK | MB_ICONINFORMATION); break; } } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; }
Alguns comentários:
#include <windows.h> #include <Windowsx.h> #include <tchar.h> #include "resource.h" //aceder aos ids, macros simbólicas #define NUM_CLIENTES 3 #define LIST_SIZE 8 TCHAR* LIST_ITENS[] = { TEXT("10€"), TEXT("20€"), TEXT("40€"), TEXT("60€"), TEXT("80€"), TEXT("100€"), TEXT("150€"), TEXT("200€") }; //tratamento de eventos das janelas LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLogin(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLevantar(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosAcerca(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Ficha8"); typedef struct { unsigned int ID; TCHAR username[16]; TCHAR password[16]; unsigned int saldo; } cliente; typedef struct { unsigned int tipo; // 1 = depósito, 2 = levantamento unsigned int quantia; unsigned int ID; } operacao; //partilhar informação entre o mains e as funções de tratamentos de eventos typedef struct { cliente clientes[NUM_CLIENTES]; operacao historico[200]; unsigned int numOperacoes; } dados; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; HANDLE hAccel; dados dadosPartilhados; //partilha de recursos wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; //para poder usar as macros fazer uso do MAKEINTRESOURCE wcApp.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); wcApp.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_PRINCIPAL); wcApp.cbClsExtra = sizeof(dados); wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(220, 220, 220)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("SO2 - Ficha 8"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 150, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //partilha de recursos dadosPartilhados.numOperacoes = 5; // Apenas para testar... LONG_PTR x = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&dadosPartilhados); ShowWindow(hWnd, nCmdShow); //ciclo de processamentos das mensagens, dos atalhos hAccel = LoadAccelerators(NULL, MAKEINTRESOURCE(IDR_ACCELERATOR)); while (GetMessage(&lpMsg, NULL, 0, 0)) { if (!TranslateAccelerator(hWnd, hAccel, &lpMsg)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR str1[512], str2[512]; dados* dadosPartilhados; dadosPartilhados = (dados*)GetWindowLongPtr(hWnd, GWLP_USERDATA); //partilha de recursos switch (messg) { case WM_CREATE: //código de inicialização do sistema EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_DISABLED | MF_GRAYED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_DISABLED | MF_GRAYED); break; case WM_COMMAND: //a parte menos significativa trás o ID da opção da janela que deu origem ao WM_COMMAND switch (LOWORD(wParam)) { case ID_LOGIN: //contexto, ID da macro da dialog box, o handler da janela principal, função para tratar o evento DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LOGIN), hWnd, TrataEventosLogin); EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_ENABLED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_ENABLED); break; //a parte menos significativa trás o ID da opção da janela que deu origem ao WM_COMMAND case ID_CONSULTA: case ID_ACCELERATOR_CONSULTA: LoadString(NULL, IDS_STR_CONSULTA, str1, 512); _stprintf_s(str2, 512, TEXT("%s (%d)"), str1, dadosPartilhados->numOperacoes); MessageBox(hWnd, str2, TEXT("String Table"), MB_OK | MB_ICONINFORMATION); break; case ID_ACCELERATOR_LEVANTAMENTOS: case ID_LEVANTAMENTOS: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LEVANTAMENTO), NULL, TrataEventosLevantar); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, messg, wParam, lParam); break; } return(0); } LRESULT CALLBACK TrataEventosLogin(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR username[16]; switch (messg) { case WM_COMMAND: if (LOWORD(wParam) == IDOK) { GetDlgItemText(hWnd, IDC_EDIT_LOGIN, username, 16); MessageBox(hWnd, username, TEXT("Username"), MB_OK | MB_ICONINFORMATION); } else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hWnd, 0); return TRUE; } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; } LRESULT CALLBACK TrataEventosLevantar(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { int i; switch (messg) { case WM_INITDIALOG: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); SendMessage(hwndList, LB_RESETCONTENT, 0, 0); for (i = 0; i < LIST_SIZE; i++) SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)LIST_ITENS[i]); break; case WM_COMMAND: if (LOWORD(wParam) == IDC_LIST_MONTANTES) { switch (HIWORD(wParam)) { case LBN_DBLCLK: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); i = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); MessageBox(hWnd, LIST_ITENS[i], TEXT("ListBox"), MB_OK | MB_ICONINFORMATION); break; } } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; }
sessão 10 – Programação gráfica orientada a eventos (2ª versão)
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Esta é a segunda revisão destes conteúdos (desenhar completo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //função que vai ser chamada pelo windows sempre que acontece alguma coisa: eventos.. LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //nome da janela //winMain: main na parte gráfica int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; // hWnd é o handler da janela, gerado mais abaixo por CreateWindow() MSG lpMsg; // MSG é uma estrutura definida no Windows para as mensagens WNDCLASSEX wcApp; // WNDCLASSEX definir as características da classe da janela //1. caracteristicas da janela wcApp.cbSize = sizeof(WNDCLASSEX); // Tamanho da estrutura WNDCLASSEX wcApp.hInstance = hInst;// Instância da janela actualmente exibida wcApp.lpszClassName = szProgName; // Nome da janela (neste caso = nome do programa) wcApp.lpfnWndProc = TrataEventos; //indicar a a função que trata eventos wcApp.style = CS_HREDRAW | CS_VREDRAW; // Estilo da janela: Fazer o redraw se for // modificada horizontal ou verticalmente wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); // "hCursor" = handler do cursor (rato) // "NULL" = Forma definida no Windows // "IDC_ARROW" Aspecto "seta" wcApp.lpszMenuName = NULL; // (NULL = não tem menu) wcApp.cbClsExtra = 0; // Livre, para uso particular wcApp.cbWndExtra = 0; // Livre, para uso particular wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); //cor de fundo // 2. Registar a classe "wcApp" no Windows if (!RegisterClassEx(&wcApp)) return(0); // 3. Criar a janela hWnd = CreateWindow( szProgName, // Nome da janela (programa) definido acima TEXT("Estudo para o exame"),// Texto que figura na barra do título WS_OVERLAPPEDWINDOW, // Estilo da janela (WS_OVERLAPPED= normal) CW_USEDEFAULT, // Posição x pixels (CW_USEDEFAULT=à direita da última) CW_USEDEFAULT, // Posição y pixels (CW_USEDEFAULT=abaixo da última) 800, // Largura da janela (em pixels) 600, // Altura da janela (em pixels) (HWND)HWND_DESKTOP, // handle da janela pai ou HWND_DESKTOP se a janela for a primeira, criada a partir do "desktop" (HMENU)NULL, // handle do menu da janela (se tiver menu) (HINSTANCE)hInst, 0); // 4. Mostrar a janela ShowWindow(hWnd, nCmdShow); // "hWnd"= handler da janela, devolvido por // "CreateWindow"; "nCmdShow"= modo de exibição (p.e. // normal/modal); é passado como parâmetro de WinMain() UpdateWindow(hWnd); // Refrescar a janela (Windows envia à janela uma mensagem para pintar, mostrar dados, (refrescar)… // 5. Loop de Mensagens while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); // pre processar os eventos DispatchMessage(&lpMsg); // } //GetMessage se recebe zero, fechar a janela, sai do ciclo // 6. Fim do programa return((int)lpMsg.wParam); // Retorna sempre o parâmetro wParam da estrutura lpMsg } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { // UINT messg: código do evento de carregar numa tecla // valor da tecla que foi carregada é do WPARAM wParam // lParam: posixaoX, posicaoY do rato na janela static TCHAR c = '?'; //ou global //static int posX = 0; //ou static ou global, com o BeginPaint //static int posY = 0; //ou static ou global, com o BeginPaint HDC hdc; //para a escrita em janela!, handle device content RECT rect; //espaço de desenho static TCHAR charValue = '?'; PAINTSTRUCT ps; //array dos histórico das posicções static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: //evento para refresh da janela //operação de escrita REAL na janela é aqui hdc= BeginPaint(hWnd, &ps); //substitui GetDC(hWnd); // a posição da escrita GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); //pintar funto controlado SetTextColor(hdc, RGB(0, 123, 0)); //fundo transparente SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; //escrita do texto DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); //substitui ReleaseDC(hWnd, hdc); break; //case WM_ERASEBKGND: //somos nós a tratar o BKRND // return TRUE; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); //macro GET_X_LPARAM posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam);//macro GET_Y_LPARAM posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); //janela invalida, pinta de novo } //limpar a janela com o beginpaint //inicio processo desenhar/escrever //hdc = GetDC(hWnd); //fim processo desenhar/escrever //ReleaseDC(hWnd, hdc); break; case WM_CLOSE: //fechar a janela if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) //combinar flags | == IDYES) { DestroyWindow(hWnd); //fechar a janela } break; case WM_DESTROY: // Destruir a janela e terminar o programa PostQuitMessage(0); // "PostQuitMessage(Exit Status)" break; default: //mt importante, para lidar com o que não foi feito tratamento return(DefWindowProc(hWnd, messg, wParam, lParam)); break; // break tecnicamente desnecessário por causa do return } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar completo e limpo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { static TCHAR c = '?'; HDC hdc; RECT rect; static TCHAR charValue = '?'; PAINTSTRUCT ps; static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: hdc= BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); SetTextColor(hdc, RGB(0, 123, 0)); SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); break; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam); posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); } break; case WM_CLOSE: if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar uma BMP na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar um BMP animado na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animção int limDireito; HWND hWndGlobalJanela; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animação int limDireito; HWND hWndGlobalJanela; //concorrencia, problema de sincronização HANDLE hMutex; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar WaitForSingleObject(hMutex, INFINITE); //problema de sincronização BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); ReleaseMutex(hMutex); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência e lidar com a flickering cintilação) com uso do doubleBuffering:
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); HBITMAP hBMP; HDC bmpDC; BITMAP bmp; int xBitmap; int yBitmap; int limDireito; HWND hWndGlobalJanela; HANDLE hMutex; //1) lidar com o flickering HDC memDC = NULL; HBITMAP hCopiaBMP; //copiar as caracteristicas DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); GetObject(hBMP, sizeof(bmp), &bmp); HDC hdc; hdc = GetDC(hWnd); bmpDC = CreateCompatibleDC(hdc); SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); RECT rect; GetClientRect(hWnd, &rect); limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); //2) lidar com o flickering if(memDC == NULL) { //primeiro vez que estou a passar pela memoria //criar a cópia memDC = CreateCompatibleDC(hdc); hCopiaBMP = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); SelectObject(memDC, hCopiaBMP); DeleteObject(hCopiaBMP); } //3) usar a copia em memoria e nao na principal //FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 255, 0))); WaitForSingleObject(hMutex, INFINITE); //BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); BitBlt(memDC, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); //4) escrever a copia em memoria e aplicar na totalidade da janela BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: WaitForSingleObject(hMutex, INFINITE); xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); limDireito = LOWORD(lParam) - bmp.bmWidth; //5) para obrigar a entrar no if no WM_PAINT ReleaseDC(hWnd, memDC); memDC = NULL; ReleaseMutex(hMutex); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }