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)

Tags : , , , ,

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);
}
Tags : , , , ,

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

Tags : , , , , ,

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

Tags : , , , , ,