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);
}


















