Month: January 2021

off-topic.. GameMaker Studio 2

“Web-browser maker Opera acquired YoYo Games for $10 million. The firm’s GameMaker Studio 2 game engine has been downloaded 10 million times since 2012, which doesn’t exactly make it the biggest fish in the pond but I am curious to see whether Opera can offer something novel and accessible. ”

será que o GameMaker Studio 2 vai ser um software grátis?

Tags : ,

Humble Software Bundle: Maps Extravaganza Encore (uma campanha)

Até parece que sou comercial, mas esta é outra campanha da Humble mas o caso não é tanto para GameDev mas para videojogos digitais. Tratam-se de um asset e cursos que permitem desenvolver jogos digitais. São vários os cursos online para usar o Unity e outros temas paralelos. Da lista de prendas desta campanha consta:
Finish It! Motivation & Processes For Game Devs
Math For Video Games
Complete Unity Developer 2D
How To Get A Job In The Video Game Industry
Git Smart: Learn Git in Unity, SourceTree & GitHub
POLYGON City Pack by Synty Studios
Complete Unity Developer 3D
Unity Multiplayer Coding & Networking
RPG Core Combat Creator
Unity Dialogue & Quests Creator
RPG Inventory Systems Creator

custo mínimo total: 20.72 euros

+infos(a campanha): LINK

Tags : , , ,

Novos talentos Fnac

“Está aberta mais uma edição dos NTF este ano com mais uma categoria – Videojogos! Tens três meses para nos enviares o teu projeto!
Quando te fazem esquecer o mundo lá fora, mostra-nos a paixão dentro de ti. Liberta o que te faz viajar no lugar, sentir borboletas no estomago e te põe os olhos a brilhar. O planeta está num turbilhão, sim, mas não se conhecem limites para a criatividade. Deixa a emoção falar e o teu talento explodir.”

O júri:

“Partilha connosco(1) a Build do jogo jogável para PC/MAC, o trailer do jogo e ainda 3 minutos de vídeo de gameplay do jogo e deixa o teu talento explodir.
Serão distinguidos 1 vencedor e 2 menções honrosas que serão conhecidas na entrega de prémios em Junho. A decisão dos 3 premiados fica a cargo de um júri especializado composto por Ricardo Flores (Diretor da Lockwood Publishing Lisboa), Isaque Sanches (Coletivo A Ludoteca), André Santos (Jornalista FutureBehind) e Alexandre Barbosa (Jornalista e Diretor Assistente);

Os três projetos vencedores verão os seus jogos integrados nas plataformas de comunicação da FNAC, com possibilidade de download do videojogo. O vencedor recebe ainda um cheque de 1.500€ da REPSOL, a criação do seu jogo exposto nos fóruns FNAC, uma licença de 1 ano Unity (Lockwood) e um monitor OMEN X25 (HP).”

+infos(oficial): https://www.fnac.pt/novostalentos

+infos(regulamento): LINK

Tags : ,

6th International Conference on the Foundations of Digital Games (FDG)

“The 16th International Conference on the Foundations of Digital Games (FDG) 2021 is proud to invite research contributions in the form of papers, games and demos, doctoral consortium applications, as well as panel, competition, and workshop proposals. We invite contributions from within and across any discipline committed to advancing knowledge on the foundations of games: computer science and engineering, humanities and social sciences, arts and design, mathematics and natural sciences. Papers and Games-and-Demos submissions will receive double-blind peer reviews. Workshops, panels, competitions, and all other submissions will be single-blind. All papers are guaranteed at least three reviews. Games and Demos are guaranteed two reviews. There will be no rebuttal. As in previous years, we aim to publish the FDG 2021 proceedings in the ACM Digital Library. ”

Tracks:
Game Development Methods and Technologies
This track is dedicated to submissions focused on different methods, technologies, and tools used for the development of computer games. The submissions may discuss development of both software and content. Topics include, but are not limited to, algorithms, software tools, methodologies, pipelines, and production issues. This track invites both scholarly studies as well as speculative position papers on the subject.
This track includes the use of virtual reality and augmented reality for games. Submissions that examine, validate, invalidate or create practices, patterns, mechanics, dynamics or aesthetics are encouraged to submit. Examples of such work include the examination and innovation of design methods for AR/VR, alternative methods of interaction and haptics, historic examinations of the involved technologies, analyses and critiques on the use of AR/VR, innovative AR/VR technologies and the examination of mechanics and games designed forAR/VR.

Games Beyond Entertainment and Game Education
This track calls for papers showing results on the use of games, gaming, and game design for primary goals that are not entertainment. Topics include serious or transformational games, games with a purpose, advergames and exergames, gamification and gameful design, game-based learning, informal learning in games, and educational and other ‘serious’ uses of entertainment games and gaming practices. Authors are encouraged to highlight the importance of the target problem that the game is addressing, and how their design or research findings makes a contribution to the current state of research on games for a purpose.
This track is also concerned with the teaching of games, game development and design, and game-related concepts at all levels of education and training. Topics include design and development of curricula, instructional methods, teaching tools and techniques, assessment methods, learning/instructional activities, collegiate game programs, e-sports and educational program management.

Game Analytics and Visualization
This track is suitable for all papers pertaining to aspects of game data science, analytics and game data visualization. This includes work based on player behavioral data analysis, including player modeling, churn analysis, and creating or understanding players’ profiles as well as aspects of business intelligence, such as performance evaluation or workflow optimization. Papers submitted to this track should present contributions that advance the current state-of-the-art, taking into account the knowledge bases in academia and industry, of players, play behaviors, processes or performance. We encourage submissions that span methodological approaches including quantitative, qualitative and mixed-methods, as well as novel contributions to methodological approaches. Examples of topics include visualization of game data, analysis of behavioral (or other) game data, advances in methodological approaches to analyze and visualize game data, as well as applying or expanding statistical methods, machine learning, including deep learning, and AI, as well as visualization algorithms used to collect or analyze game data.

Game Artificial Intelligence
This track focuses on the many applications of computational and artificial intelligence to the playing, design, development, improvement, and testing of video games. Topics include general game-playing AI, procedural and player-driven content generation, mixed-initiative authoring tools, computational narrative, believable agents, and AI assisted game design.

Game Criticism and Analysis
This track calls for papers that approach the criticism and analysis of games from humanities-informed perspectives. Submissions are encouraged from scholars engaging in narrative, visual and software studies approaches to games and games criticism using methodologies such as archival research, hermeneutics, and oral history. This track will also consider critical theoretical and/or historical analysis of games, and game genres from perspectives such as (but not limited to) postcolonial theory, feminism, historicism, subaltern studies, queer theory, the environmental humanities, psychoanalysis, and other related topics. This track also includes the emerging trends such as esports and streaming from a humanities-informed perspective. Such work includes studies of demographic trends in esports and streaming, feminist and gender studies perspectives, social-cultural critiques and analyses of streaming and eSports subcultures, examination of societal and economic impact of eSports

Game Design and Player Experience
This track focuses on the exploration of different ways for designing and implementing interaction between the player and the game, as well as on understanding the experiences derived from those interactions. This track will consider qualitative and quantitative experimental studies. Topics include, but are not limited to, games design, mechanics, persuasive games, novel use of controllers, user research, and player psychology.

+infos(oficial): http://fdg2021.org/

Tags : , , ,

GameJam 2021 em janeiro (online)

A Global Game Jam está de volta! Num ano atípico, e face as complicações do mundo exterior, o servidor do GameDev.PT abre as portas como espaço e jam site da Global Game Jam Online, com a comunidade juntar-se novamente para mais um fim-de-semana a criar jogos lado-a-lado com o resto do mundo, pela primeira vez em formato online!

Este ano, os jam sites portugueses juntam-se também em parceria como Global Game Jam Portugal para ajudar a levar os jam sites ainda mais longe!

Se queres participar numa game jam, não deixes que a pandemia te impeça! Entre 29 e 31 de Janeiro, participa connosco por 48 horas, forma uma equipa e cria o teu jogo! O evento é aberto a toda a comunidade, e convidamos profissionais, estudantes e amadores a juntarem-se, mesmo que nunca tenham participado numa jam antes!

Queres participar? Entra no nosso Discord e lê a sala #ggj-info para mais informações! Depois disso, inscreve-te no jam site aqui através do site da Global Game Jam!

Todos os anuncios em relação à jam serão feitos através do Discord!

+infos(oficial): LINK

 

Tags : ,

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 : , , , ,

Gamer’s Chest, All-in-One Tabletop Game Organizer

Encontrei este projeto no kickstarter para os jogos de tabuleiro.. tão fixe!

+infos(a campanha): LINK

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 : , , , , ,

sessão 10 – Programação gráfica orientada a eventos (os recursos win32)

O uso de recursos vem ajudar a construir elementos gráficos e sem ter que os desenhar!

Para fazer uso, é adicionar a um projeto vazio o recurso:

Existem vários tipos:

Sendo que uma das primeiras opções podem ser o Icon:

O ícone pode ser desenhado, pelo que é recomendado que primeiro se eliminem os que já existem, quando faltar um, adicionamos um vazio de 24 bits para termos o máximo de cores disponíveis, e de seguida voltamos a eliminar o ultimo ícone que ficou e que existia por defeito.

 

Usando a barra de desenho no topo podemos alterar este ícone

 

Nas propriedades podemos alterar o nome do ID, por exemplo para IDI_ICON_APP

Outro recurso pode ser o uso de menus..

Nas propriedades podemos indicar que as opções na barra de menus sejam de ação ao invés de mostrar outras opções no menu:
Popup -> FALSE

As caixa de diálgo:

E as opções das caixas de diálogo:
Static Text
Edit Control
Button
Accelerator (para lidar com as teclas de atalho)
String Table
(…)

Nos Accelerator são definidas os comportamentos das teclas de atalho:

As Strings table (definir ID e definir um texto para eles):

e ainda o cursor, sendo que no final:

e os comportamentos dos componentes..

um exemplo:

#include <windows.h>
#include <Windowsx.h>
#include <tchar.h>
#include "resource.h" //importante para acesso aos IDs..

#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€")
};

//três soluções para tratamentos de eventos de janelas: dialog boxes
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;

//estrutura agreagadora para nao usar variaveis globais
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)); // ID
	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); //janela tem espaço extra
	wcApp.cbWndExtra = 0;
	wcApp.hbrBackground = CreateSolidBrush(RGB(220, 220, 220));

	if (!RegisterClassEx(&wcApp))
		return(0);

	hWnd = CreateWindow(
		szProgName,
		TEXT("SO2 - win 32"),
		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); //obter da janela bytes da struct de dados

	ShowWindow(hWnd, nCmdShow);


	//processamento 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);

	switch (messg)
	{
	case WM_CREATE:
		EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_DISABLED | MF_GRAYED);
		EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTO, 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); //ficam disponiveis
			EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTO, MF_ENABLED); //ficam disponiveis
			break;

		case ID_CONSULTA:
		case ID_ACCELERATOR_C:

			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_L:
		case ID_LEVANTAMENTO:

			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)
	{
		//lidar com clique no menu
	case WM_COMMAND:

		if (LOWORD(wParam) == IDOK)
		{
			GetDlgItemText(hWnd, IDC_LEVANTAMENTO_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

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

Como começar?
no visualstudio 2019, podemos começar com uma aplicação normal de consola.
alterar de projeto gráfico nas propriedades


um exemplo de um ficheiro base:

nclude <windows.h>
#include <tchar.h>

/* ===================================================== */
/* Programa base (esqueleto) para aplicações Windows     */
/* ===================================================== */
// Cria uma janela de nome "Janela Principal" e pinta fundo de branco
// Modelo para programas Windows:
//  Composto por 2 funções: 
//	WinMain()     = Ponto de entrada dos programas windows
//			1) Define, cria e mostra a janela
//			2) Loop de recepção de mensagens provenientes do Windows
//     TrataEventos()= Processamentos da janela (pode ter outro nome)
//			1) É chamada pelo Windows (callback) 
//			2) Executa código em função da mensagem recebida

LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM);

// Nome da classe da janela (para programas de uma só janela, normalmente este nome é 
// igual ao do próprio programa) "szprogName" é usado mais abaixo na definição das 
// propriedades do objecto janela
TCHAR szProgName[] = TEXT("Base");

// ============================================================================
// FUNÇÃO DE INÍCIO DO PROGRAMA: WinMain()
// ============================================================================
// Em Windows, o programa começa sempre a sua execução na função WinMain()que desempenha
// o papel da função main() do C em modo consola WINAPI indica o "tipo da função" (WINAPI
// para todas as declaradas nos headers do Windows e CALLBACK para as funções de
// processamento da janela)
// Parâmetros:
//   hInst: Gerado pelo Windows, é o handle (número) da instância deste programa 
//   hPrevInst: Gerado pelo Windows, é sempre NULL para o NT (era usado no Windows 3.1)
//   lpCmdLine: Gerado pelo Windows, é um ponteiro para uma string terminada por 0
//              destinada a conter parâmetros para o programa 
//   nCmdShow:  Parâmetro que especifica o modo de exibição da janela (usado em  
//        	   ShowWindow()

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 é uma estrutura cujos membros servem para 
			// definir as características da classe da janela

  // ============================================================================
  // 1. Definição das características da janela "wcApp" 
  //    (Valores dos elementos da estrutura "wcApp" do tipo WNDCLASSEX)
  // ============================================================================
  wcApp.cbSize = sizeof(WNDCLASSEX);      // Tamanho da estrutura WNDCLASSEX
  wcApp.hInstance = hInst;		         // Instância da janela actualmente exibida 
						         // ("hInst" é parâmetro de WinMain e vem 
		                               // inicializada daí)
  wcApp.lpszClassName = szProgName;       // Nome da janela (neste caso = nome do programa)
  wcApp.lpfnWndProc = TrataEventos;       // Endereço da função de processamento da janela
                                          // ("TrataEventos" foi declarada no início e
                                          // encontra-se mais abaixo)
  wcApp.style = CS_HREDRAW | CS_VREDRAW;  // Estilo da janela: Fazer o redraw se for
                                          // modificada horizontal ou verticalmente

  wcApp.hIcon = LoadIcon(NULL, IDI_APPLICATION);   // "hIcon" = handler do ícon normal
							             // "NULL" = Icon definido no Windows
							             // "IDI_AP..." Ícone "aplicação"
  wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); // "hIconSm" = handler do ícon pequeno
							             // "NULL" = Icon definido no Windows
							             // "IDI_INF..." Ícon de informação
  wcApp.hCursor = LoadCursor(NULL, IDC_ARROW);	// "hCursor" = handler do cursor (rato) 
							// "NULL" = Forma definida no Windows
							// "IDC_ARROW" Aspecto "seta" 
  wcApp.lpszMenuName = NULL;			// Classe do menu que a janela pode ter
							// (NULL = não tem menu)
  wcApp.cbClsExtra = 0;				// Livre, para uso particular
  wcApp.cbWndExtra = 0;				// Livre, para uso particular
  wcApp.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH); 
  // "hbrBackground" = handler para "brush" de pintura do fundo da janela. Devolvido por
  // "GetStockObject".Neste caso o fundo será branco

  // ============================================================================
  // 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("Exemplo de Janela Principal em C"),// Texto que figura na barra do título
	WS_OVERLAPPEDWINDOW,	// Estilo da janela (WS_OVERLAPPED= normal)
	CW_USEDEFAULT,		// Posição x pixels (default=à direita da última)
	CW_USEDEFAULT,		// Posição y pixels (default=abaixo da última)
	CW_USEDEFAULT,		// Largura da janela (em pixels)
	CW_USEDEFAULT,		// Altura da janela (em pixels)
	(HWND) HWND_DESKTOP,	// handle da janela pai (se se criar uma a partir de
					// outra) 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,		// handle da instância do programa actual ("hInst" é 
					// passado num dos parâmetros de WinMain()
	0);				// Não há parâmetros adicionais para a janela
  // ============================================================================
  // 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
  // ============================================================================
  // O Windows envia mensagens às janelas (programas). Estas mensagens ficam numa fila de
  // espera até que GetMessage(...) possa ler "a mensagem seguinte"	
  // Parâmetros de "getMessage":
  // 1)"&amp;lpMsg"=Endereço de uma estrutura do tipo MSG ("MSG lpMsg" ja foi declarada no  
  //   início de WinMain()):
  //			HWND hwnd		handler da janela a que se destina a mensagem
  //			UINT message		Identificador da mensagem
  //			WPARAM wParam		Parâmetro, p.e. código da tecla premida
  //			LPARAM lParam		Parâmetro, p.e. se ALT também estava premida
  //			DWORD time		Hora a que a mensagem foi enviada pelo Windows
  //			POINT pt		Localização do mouse (x, y) 
  // 2)handle da window para a qual se pretendem receber mensagens (=NULL se se pretendem
  //   receber as mensagens para todas as
  // janelas pertencentes à thread actual)
  // 3)Código limite inferior das mensagens que se pretendem receber
  // 4)Código limite superior das mensagens que se pretendem receber

  // NOTA: GetMessage() devolve 0 quando for recebida a mensagem de fecho da janela,
  // 	  terminando então o loop de recepção de mensagens, e o programa 

  while (GetMessage(&amp;lpMsg,NULL,0,0)) {	
     TranslateMessage(&amp;lpMsg);	// Pré-processamento da mensagem (p.e. obter código 
					// ASCII da tecla premida)
     DispatchMessage(&amp;lpMsg);	// Enviar a mensagem traduzida de volta ao Windows, que
					// aguarda até que a possa reenviar à função de 
					// tratamento da janela, CALLBACK TrataEventos (abaixo)
  }

  // ============================================================================
  // 6. Fim do programa
  // ============================================================================
  return((int)lpMsg.wParam);	// Retorna sempre o parâmetro wParam da estrutura lpMsg
}

// ============================================================================
// FUNÇÃO DE PROCESSAMENTO DA JANELA
// Esta função pode ter um nome qualquer: Apenas é necesário que na inicialização da
// estrutura "wcApp", feita no início de // WinMain(), se identifique essa função. Neste
// caso "wcApp.lpfnWndProc = WndProc"
//
// WndProc recebe as mensagens enviadas pelo Windows (depois de lidas e pré-processadas
// no loop "while" da função WinMain()
// Parâmetros:
//		hWnd	O handler da janela, obtido no CreateWindow()
//		messg	Ponteiro para a estrutura mensagem (ver estrutura em 5. Loop...
//		wParam	O parâmetro wParam da estrutura messg (a mensagem)
//		lParam	O parâmetro lParam desta mesma estrutura
//
// NOTA:Estes parâmetros estão aqui acessíveis o que simplifica o acesso aos seus valores
//
// A função EndProc é sempre do tipo "switch..." com "cases" que descriminam a mensagem
// recebida e a tratar.
// Estas mensagens são identificadas por constantes (p.e. 
// WM_DESTROY, WM_CHAR, WM_KEYDOWN, WM_PAINT...) definidas em windows.h
// ============================================================================

LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) {	
  switch (messg) {	
    case WM_DESTROY:	// Destruir a janela e terminar o programa 
                        // "PostQuitMessage(Exit Status)"		
	PostQuitMessage(0);
	break;		
    default:
      // Neste exemplo, para qualquer outra mensagem (p.e. "minimizar","maximizar","restaurar")
      // não é efectuado nenhum processamento, apenas se segue o "default" do Windows
      return(DefWindowProc(hWnd,messg,wParam,lParam));
      break;  // break tecnicamente desnecessário por causa do return
  }
  return(0);
}

uma unica janela e com a informação persistente..

#include <windows.h>
#include <tchar.h>
#include <windowsx.h>

LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM);

TCHAR szProgName[] = TEXT("Base");

//main
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) {
	HWND hWnd;		
	MSG lpMsg;		
	WNDCLASSEX wcApp;
	//caracteristicas especificas de todas as janelas, comuns:
	// o ícone, título, cor de fundo, localização

	wcApp.cbSize = sizeof(WNDCLASSEX);
	wcApp.hInstance = hInst;
	wcApp.lpszClassName = szProgName;
	wcApp.lpfnWndProc = TrataEventos;
									
	wcApp.style = CS_HREDRAW | CS_VREDRAW; 
	wcApp.hIcon = LoadIcon(NULL, IDI_WARNING);   
	wcApp.hIconSm = LoadIcon(NULL, IDI_SHIELD); 
	wcApp.hCursor = LoadCursor(NULL, IDC_ARROW );	
	wcApp.lpszMenuName = NULL;			
	wcApp.cbClsExtra = 0;				
	wcApp.cbWndExtra = 0;				
	//wcApp.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0));
	
	if (!RegisterClassEx(&wcApp))
		return(0);

	//caracteristicas especificas de cada Janela!!! -> CreateWindow
	hWnd = CreateWindow(
		szProgName,			
		TEXT("S02 - Exercício 01"),
		WS_OVERLAPPEDWINDOW,
		200, // Posição x pixels
		200, // Posição y pixels
		600, // Largura da janela (em pixels)
		600, // Altura da janela (em pixels)
		(HWND)HWND_DESKTOP,	
		(HMENU)NULL,		
		(HINSTANCE)hInst,
		0);				
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);		

	//ciclo que mantem o programa a funcionar
	while (GetMessage(&lpMsg, NULL,0, 0)) {
		TranslateMessage(&lpMsg);	
		DispatchMessage(&lpMsg);	
	}
	return((int)lpMsg.wParam);	
}

//função que trata os eventos, chamada pelo sistema operativo
//esta função pode estar associada a diferentes janelas
//coleção de eventos: UINT messg

//TCHAR c = '?'; //global

//para guardar o historial dos desenhos
typedef struct
{
	int xPos;
	int yPos;
	TCHAR c;
} PosChar;

LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) {
	HDC hdc;
	RECT rect;
	//int xPos, yPos;
	//static int xPos, yPos; //serem globais ou static para serem presistentes
	static PosChar posicoes[500]; //array de posições
	static int totalPos = 0;
	int i;
	
	static TCHAR chardAtual = '?'; //ou local

	PAINTSTRUCT ps;
	
	switch (messg) {
		//evento que é sempre disparado qd acontece o refresh!
		//todas as operações que escrevem na janela têm que surgir aqui WM_PAINT
		//todas as operaçõe de escrita é aqui WM_PAINT
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps); //inicio de escrita na janela
		//deseho
		//hdc = GetDC(hWnd);
		GetClientRect(hWnd, &rect); //toda a area do cliente
		FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela
		SetTextColor(hdc, RGB(255, 0, 255));
		SetBkMode(hdc, TRANSPARENT);
		
		for (i = 0; i < totalPos; i++) {
			rect.left = posicoes[i].xPos;
			rect.top = posicoes[i].yPos;
			DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP);
		}
		
		//ReleaseDC(hWnd, hdc);
		EndPaint(hWnd, &ps); //fim de escrita na janela
		break;

	//ignorar a limpeza do fundo, depois é usado o FillRect no WM_PAINT
	case WM_ERASEBKGND:
		return TRUE;
		
	/*case WM_PAINT:
		for(auto i : ops)
		{
			hdc = GetDC(hWnd);

			GetClientRect(hWnd, &rect);
			rect.bottom = i.xPos;
			rect.top = i.yPos;

			SetTextColor(hdc, RGB(0, 0, 0));
			SetBkMode(hdc, TRANSPARENT);

			DrawText(hdc, &i.pChar, 1, &rect, DT_SINGLELINE | DT_NOCLIP);

			ReleaseDC(hWnd, hdc);
		}
		
		break;
		*/

		
	case WM_LBUTTONDOWN:
		//xPos = GET_X_LPARAM(lParam); //#include <windowsx.h>
		//yPos = GET_Y_LPARAM(lParam); //#include <windowsx.h>
		if(totalPos<500){
			posicoes[totalPos].xPos = GET_X_LPARAM(lParam); //#include <windowsx.h>
			posicoes[totalPos].yPos = GET_Y_LPARAM(lParam); //#include <windowsx.h>
			posicoes[totalPos].c = chardAtual;
			totalPos++;
			InvalidateRect(hWnd, NULL, FALSE); //refresh da janela, ajuda a lidar com o fundo
		}

		////deseho
		//hdc = GetDC(hWnd);
		//GetClientRect(hWnd, &rect);
		//SetTextColor(hdc, RGB(255, 0, 255));
		//SetBkMode(hdc, TRANSPARENT);
		//rect.left = xPos;
		//rect.top = yPos;
		//DrawText(hdc, &c, 1, &rect, DT_SINGLELINE | DT_NOCLIP);
		//ReleaseDC(hWnd, hdc);
		
		break;
	case WM_CHAR:
		chardAtual = (TCHAR)wParam;
		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);
}

lidar com animações e com o flickering / oscilante

#include <windows.h>
#include <tchar.h>
#include <windowsx.h>

LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM);

TCHAR szProgName[] = TEXT("Base");

//(todas elas vão ser inicializadas na main, e não podem ser usadas variaveis estáticas)
//mas podia estar dentro de uma estrutura
//handle/acesso para o bitmap
HBITMAP hBmp;
//handle para o device content, o que vai permitir passar o bmp para este formato
HDC bmpDC;
//informação sobre a estrutura do bitmap (pe: largura e altura..)
BITMAP bmp;
//posição da imagem
int xBitmap;
int yBitmap;

//ou isto assim agora ou criar uma estrutura!
int limDireitoJanela;
HWND hWandGlobal;

//lidar com o flikring a centilação e lidar com os acesso concorrentes: mutex
HANDLE hMutex;


//lidar com a copia em memoria, double buffering
HDC memoriaCopiaDC = NULL;
HBITMAP hCopiaBitMap;//vai copiar as caracteristicas para a copia


//crair uma thread para lidar com as animações/tempo que passa
DWORD WINAPI movimentaImagem(LPVOID param)
{
	int direcaoMovimento = 1; //1, direita e -1,  esquerda
	int salto = 2; //salto de posições em termos de pixeis

	while(1)
	{
		WaitForSingleObject(hMutex, INFINITE);
		xBitmap = xBitmap + (direcaoMovimento * salto);
		if(xBitmap <=0)
		{
			xBitmap = 0;
			direcaoMovimento = 1;
		}
		else if(xBitmap >= limDireitoJanela)
		{
			xBitmap = limDireitoJanela;
			direcaoMovimento = -1;
		}
		ReleaseMutex(hMutex);
		
		//temos sempre que avisar o sistema para fazer uma nova pintura da janela
		InvalidateRect(hWandGlobal, NULL, FALSE);

		Sleep(1);
	}
}


int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) {
	HWND hWnd;
	MSG lpMsg;
	WNDCLASSEX wcApp;
	//caracteristicas especificas de todas as janelas, comuns:
	// o ícone, título, cor de fundo, localização

	wcApp.cbSize = sizeof(WNDCLASSEX);
	wcApp.hInstance = hInst;
	wcApp.lpszClassName = szProgName;
	wcApp.lpfnWndProc = TrataEventos;

	wcApp.style = CS_HREDRAW | CS_VREDRAW;
	wcApp.hIcon = LoadIcon(NULL, IDI_WARNING);
	wcApp.hIconSm = LoadIcon(NULL, IDI_SHIELD);
	wcApp.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcApp.lpszMenuName = NULL;
	wcApp.cbClsExtra = 0;
	wcApp.cbWndExtra = 0;
	//wcApp.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0));

	if (!RegisterClassEx(&wcApp))
		return(0);

	//caracteristicas especificas de cada Janela!!! -> CreateWindow
	hWnd = CreateWindow(
		szProgName,
		TEXT("S02 - Exercício 01"),
		WS_OVERLAPPEDWINDOW,
		200, // Posição x pixels
		200, // Posição y pixels
		600, // Largura da janela (em pixels)
		600, // Altura da janela (em pixels)
		(HWND)HWND_DESKTOP,
		(HMENU)NULL,
		(HINSTANCE)hInst,
		0);

	//aqui vem a a informação do bitmap
	//1º carregar o bitmap (124x124)
	hBmp = LoadImage(NULL, TEXT("flag.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	//ir buscar informações sobre o recurso, carregar os metadados da imagem
	GetObject(hBmp, sizeof(bmp), &bmp);
	//passar do handle da imagem para o device content
	HDC hdc;
	hdc = GetDC(hWnd);
	//criar uma copia
	bmpDC = CreateCompatibleDC(hdc); //devolve um novo handle
	//aplicar o bitmap ao device content
	SelectObject(bmpDC, hBmp);
	//fechar o device content da janela
	ReleaseDC(hWnd, hdc);

	//definir as posições iniciais da imagem, e centrar por exemplo
	RECT rect;
	GetClientRect(hWnd, &rect);
	//xBitmap = (LARGURA_JANELA / 2) - (bmp.bmWidth/2);
	//yBitmap = (ALTURA_JANELA / 2) - (bmp.bmHeight / 2);
	xBitmap = (rect.right / 2) - (bmp.bmWidth/2);
	yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2);
	//escrever a imagem no tratamento de eventos..

	//definir o que é o limite dieito
	limDireitoJanela = rect.right - bmp.bmWidth;
	hWandGlobal = hWnd;

	hMutex = CreateMutex(NULL, FALSE, NULL);
	
	//criar a thread
	CreateThread(NULL, 0, movimentaImagem, NULL, 0, NULL);
	
	
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	//ciclo que mantem o programa a funcionar
	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); //inicio de escrita na janela
		GetClientRect(hWnd, &rect); //toda a area do cliente
		
		//fazer a copia para o buffering
		if(memoriaCopiaDC == NULL)
		{
			//ainda nao foi feita a copia em memoria
			//criar a copia
			memoriaCopiaDC = CreateCompatibleDC(hdc);
			//obter as caracteristicas, copia figdigna mas falta lidar com o resize..
			hCopiaBitMap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
			SelectObject(memoriaCopiaDC, hCopiaBitMap);
			DeleteObject(hCopiaBitMap); //libertar recursos
		}		
		
		//FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela
		FillRect(memoriaCopiaDC, &rect, CreateSolidBrush(RGB(0, 255, 0))); //fundo da janela

		WaitForSingleObject(hMutex, INFINITE);
		//BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY);
		BitBlt(memoriaCopiaDC, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY);
		//SRCCOPY copiar a totalidade dos pixeis
		ReleaseMutex(hMutex); // resolver problemas do xBitmap, yBitmap

		//vai ser necessário copiar para a principal
		BitBlt(hdc,0,0, rect.right, rect.bottom, memoriaCopiaDC, 0, 0, SRCCOPY);

		EndPaint(hWnd, &ps);
		break;
		
	//lidar com o resize da janela, redimensionamento da janela
	case WM_SIZE:
		WaitForSingleObject(hMutex, INFINITE);
		//LOWORD(lParam) -> menos significativa representa a largura
		//HIWORD(lParam) -> mais significativa representa a altura
		xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2);
		yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2);
		//temos tb que ajusatr o limite da janela
		limDireitoJanela = LOWORD(lParam) - bmp.bmWidth;

		//libterar o memoriaCopiaDC
		ReleaseDC(hWnd, memoriaCopiaDC);
		memoriaCopiaDC = NULL;
		
		ReleaseMutex(hMutex);
		break;

		
	case WM_ERASEBKGND:
		return TRUE;
	
	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 : , , , ,

em kotlin: gps terceira versão com mapa

Adicionar uma dependência via menus no build-gradle (module)
(1)

ficheiro: build-gradle(module)

...
dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'com.google.android.gms:play-services-location:17.1.0'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

ficheiro: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pt.isec.estuda.amov_gpsv1">

    <!-- pedir estas permissões -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!-- pedir estas permissões para aceder aos mapas e ao estado da rede não são em runtime -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Amov_gpsv1">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="AIzaSyD4M076vcUKcPZNGlyBzFshU-J4jJ98x5g"/>
    </application>

</manifest>

ficheiro: MainActivity.kt

package pt.isec.estuda.amov_gpsv1

import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.graphics.Color
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions

const val TAG = "Location"

class MainActivity : AppCompatActivity(), OnMapReadyCallback{

    var locEnable = false //caso não ajam permissões

    lateinit var fLoc : FusedLocationProviderClient

    val ISEC = LatLng(40.1925, -8.4115)
    val DEIS = LatLng(40.1925, -8.4128)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fLoc = FusedLocationProviderClient(this)

        //pedir o objeto google maps e vai ser necessário o OnMapReadyCallback
        (supportFragmentManager.findFragmentById(R.id.map) as? SupportMapFragment)?.getMapAsync(this)
    }

    override fun onResume() {
        super.onResume()
        startLocationServices(true)
        //para poupanças de energia, ativar o mais tarde possivel
    }

    override fun onPause() {
        super.onPause()
        //para poupanças de energia, desactivar o mais cedo possivel
        if(locEnable) {
            locEnable = false
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        //se tivermos as permissões
        //locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this)
        if(requestCode == 1289) {
            startLocationServices(false)
        }
    }

    fun startLocationServices(askPerm : Boolean) {
        //1ºprovider, 2º intervalo de segundos,3º distancia minima ,4º  o listener
        //se for for telemovel NETWORK_PROVIDER
        //dar permissões: ACCESS_FINE_LOCATION
        //e pedir em runtime, add permissions check
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            //pedir as permissões //&& ou ||
            if (askPerm) {
                ActivityCompat.requestPermissions(this, arrayOf(
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION), 1289) //1289 um valor qualquer!
            } else {
                finish()
            }
            return
        }
        val locRequest = LocationRequest().apply {
            interval = 4000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            //ocultar para ja estes dois:
            //fastestInterval = 2000
            //maxWaitTime = 10000
            //numUpdates /? a ver, quantidade de updates
        }
        //activar
        locEnable = true
        fLoc.requestLocationUpdates(locRequest, locationCallBack, null)
    }

    //função locationCallBack
    var locationCallBack = object : LocationCallback() {
        override fun onLocationResult(p0: LocationResult?) {
            Log.i(TAG, "onLocationAvailability: ")
            //locations é uma lista de localizações
            p0?.locations?.forEach {
                Log.i(TAG, "onLocationAvailability: ${it.latitude} ${it.longitude}")
            }
        }
    }

    @SuppressLint("MissingPermission")
    override fun onMapReady(map: GoogleMap?) {
        //implementar o omMapReady, sendo p0 o ojecto google maps
        map ?: return //if map == null return
        map.isMyLocationEnabled = true
        map.uiSettings.isCompassEnabled = true
        map.uiSettings.isZoomControlsEnabled = true
        map.uiSettings.isZoomGesturesEnabled = true
        val cp = CameraPosition.Builder().target(ISEC).zoom(17f).bearing(0f).tilt(0f).build()
        map.animateCamera(CameraUpdateFactory.newCameraPosition(cp))
        map.addCircle(
            CircleOptions().center(ISEC).radius(150.0).fillColor(Color.argb(128,128,128,128)).strokeColor(Color.rgb(128,0,0)).strokeWidth(4f)
        )
        val mo = MarkerOptions().position(ISEC).title("ISEC-IPC").snippet("Instituo.....")
        val isec = map.addMarker(mo)
        isec.showInfoWindow()
        map.addMarker(MarkerOptions().position(DEIS).title("DEIS-ISEC"))
    }

}

ficheiro: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!-- incluimos o mapa como um fragmento -->
    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.google.android.gms.maps.SupportMapFragment"/>
</FrameLayout>
Tags : , , , , , ,

em kotlin: gps segunda versão

Adicionar uma dependência via menus no build-gradle (module)
(1)

(2)

(3)

ficheiro: build-gradle(module)

...
dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'com.google.android.gms:play-services-location:17.1.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

ficheiro: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pt.isec.estuda.amov_gpsv1">

    <!-- pedir estas permissões -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Amov_gpsv1">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

ficheiro: MainActivity.kt

package pt.isec.estuda.amov_gpsv1

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*

const val TAG = "Location"

class MainActivity : AppCompatActivity() {


    var locEnable = false //caso não ajam permissões

    lateinit var fLoc : FusedLocationProviderClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fLoc = FusedLocationProviderClient(this)
    }

    override fun onResume() {
        super.onResume()
        startLocationServices(true)
        //para poupanças de energia, ativar o mais tarde possivel
    }

    override fun onPause() {
        super.onPause()
        //para poupanças de energia, desactivar o mais cedo possivel
        if(locEnable) {
            locEnable = false
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        //se tivermos as permissões
        //locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this)
        if(requestCode == 1289) {
            startLocationServices(false)
        }
    }

    fun startLocationServices(askPerm : Boolean) {
        //1ºprovider, 2º intervalo de segundos,3º distancia minima ,4º  o listener
        //se for for telemovel NETWORK_PROVIDER
        //dar permissões: ACCESS_FINE_LOCATION
        //e pedir em runtime, add permissions check
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            //pedir as permissões //&& ou ||
            if (askPerm) {
                ActivityCompat.requestPermissions(this, arrayOf(
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION), 1289) //1289 um valor qualquer!
            } else {
                finish()
            }
            return
        }
        val locRequest = LocationRequest().apply {
            interval = 4000
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            //ocultar para ja estes dois:
            //fastestInterval = 2000
            //maxWaitTime = 10000
            //numUpdates /? a ver, quantidade de updates
        }
        //activar
        locEnable = true
        fLoc.requestLocationUpdates(locRequest, locationCallBack, null)
    }


    }
    //função locationCallBack
    var locationCallBack = object : LocationCallback() {
        override fun onLocationResult(p0: LocationResult?) {
            Log.i(TAG, "onLocationAvailability: ")
            //locations é uma lista de localizações
            p0?.locations?.forEach {
                Log.i(TAG, "onLocationAvailability: ${it.latitude} ${it.longitude}")
            }
        }
}

Com esta versão são feitos updates mais rapidamente, com a ajuda do LocationRequest.PRIORITY_HIGH_ACCURACY
este é o método recomendado pela google..

Tags : , , , , , ,

em kotlin: gps primeira versão

ficheiro: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pt.isec.estuda.amov_gpsv1">

    <!-- pedir estas permissões -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Amov_gpsv1">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

ficheiro: MainActivity.kt

package pt.isec.estuda.amov_gpsv1

import android.Manifest
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.core.app.ActivityCompat

const val TAG = "Location"

class MainActivity : AppCompatActivity(), LocationListener {

    lateinit var locationM : LocationManager
    var locEnable = false //caso não ajam permissões

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        locationM = getSystemService(LOCATION_SERVICE) as LocationManager
    }

    override fun onResume() {
        super.onResume()
        startLocationServices(true)
        //para poupanças de energia, ativar o mais tarde possivel
    }

    override fun onPause() {
        super.onPause()
        //para poupanças de energia, desactivar o mais cedo possivel
        if(locEnable) {
            locationM.removeUpdates(this)
            locEnable = false
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        //se tivermos as permissões
        //locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this)
        if(requestCode == 1289) {
            startLocationServices(false)
        }
    }

    fun startLocationServices(askPerm : Boolean){
        //1ºprovider, 2º intervalo de segundos,3º distancia minima ,4º  o listener
        //se for for telemovel NETWORK_PROVIDER
        //dar permissões: ACCESS_FINE_LOCATION
        //e pedir em runtime, add permissions check
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            //pedir as permissões //&& ou ||
                if(askPerm) {
                    ActivityCompat.requestPermissions(this, arrayOf(
                            Manifest.permission.ACCESS_FINE_LOCATION,
                            Manifest.permission.ACCESS_COARSE_LOCATION), 1289) //1289 um valor qualquer!
                }else{
                    finish()
                }
            return
        }
        locationM.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 100f, this)
        locEnable = true
        //outras:
        //addProximityAlert, funções de proximidade para um determinado local
        //addNmeaListener, ir buscar as mensagens de baixo nivel que um chip gps consegue obter
        //requestSingleUpdate, ir buscar apenas um update(uma localização armazenada)
        //getLastKnownLocation, obter um objeto Location, obtemos logo uma localização mesmo que não seja correta
        //providers, acerca dos requisitos da rede
    }

    override fun onLocationChanged(location: Location) {
        val latitude = location.latitude
        val longitude = location.longitude
        Log.i(TAG, "onLocationChanged: $latitude $longitude")
        //a ver o location.distanceTo()

    }
}
Tags : , , , , , ,

sessão 6b – Semáforos (produtor/consumidor)

N produtores M consumidores
buffer circular (parte 1)
um buffer circular é um array em que as pontas estão ligadas
se estiver cheio o array e precisarmos de preencher, voltamos à posição zero
podemos usar de forma infinita o array, estando a sobrepor os índices anteriores
cuidados com o buffer circular:
a leitura
a escrita
sincronização necessária
garantir que o consumidor só lê se existir informação para ser lida
se o produtor produzir muita informação pode preencher dados que o consumidor ainda não leu
assim o produtor só pode escrever numa posição escrita quando souber que o consumidor já a leu
usamos assim dois semáforos e dois mutexes (os mutexes resolver a questão da exclusão mutua)
os semáforos:
um para saber quantas posições do buffer circular estão preenchidas
e um para saber quantas posições do buffer circular estão vazias
na prática:
criar dois semáforos
produtor

no produtor:
while (COND) {
	item_p = produz_item();
	esperar(&sem_vazios);
	esperar(&sem_mutex_p);
	buffer[in] = item_p;
	in = (in + 1) % DIM;  //incrementar a posição de escrita
	assinalar(&sem_mutex_p);
	assinalar(&sem_itens); //indica que existem coisas para ler
}
no consumidor:
while (COND) {
	esperar(&sem_itens); //controlar as posições ocupadas
	esperar(&sem_mutex_c); //
	item_c = buffer[out]; //ler o buffer
	out = (0Ut + 1) % DIM; //incrementar a posição de leitura
	assinalar(&sem_mutex_c);
	assinalar(&sem_vazios); //indica que já existe uma posição vaiza
	trata_item(item_c);	
}

dois mutexes
para termos N produtores e M consumidores
os produtores não podem todos ocupar a mesma posição
os semáforos só garantem que que se existirem posições vazias os produtores podem ter a possibilidade popular essas posições
mas não garante a sincronização, e isso é feito com a exclusão mutua
então surge o mutex, para garantir não haver problema ao acesso concorrente
cada consumidor vai ler uma posição, a sua própria posição.
exemplo:
5 produtores são 5 processos
5 consumidores são 5 processos
podem existir
5 produtores 0 consumidores, sendo que quando o array estiver cheio eles não devem produzir mais e ficam à espera
0 produtores 5 consumidores, sendo que quando o array estiver vazio eles não devem consumir mais e ficam à espera

assim fica o produtor:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define TAM_BUFFER 10 //tamanho do buffer circular

typedef struct
{
    int id;
    int valor;
} CelulaBuffer;

//a memória partilhada
typedef struct{
    int nP; //numero de produtores
    int nC; //numero de consumidores
    int posE; //proxima posição de escrita
    int posL; //proxima posição de leitura
    CelulaBuffer buffer[TAM_BUFFER]; //estrutura do buffer circular
} BufferCircular;

//lidar com a informação dos handles semáforos, threads..
typedef struct
{
    BufferCircular * memoriaPartilhada;
	//hanelde para cada uma dos semaforoes
    HANDLE hSemEscrita; //controla as posições vazias (sem_vazios)
    HANDLE hSemLeitura; // controla as posições que estão para ser lidos (sem_intens)
    HANDLE hMutex; //mutex para controlar as exclusões mutuas (contexto: um exclusivo para todos os produtoes e um para todos os consumidores)
    int terminar; //flag para indicar a thread para lidar quando é que ela termina, 1 para sair, 0 para o contrário
	//id do produtor
	int id;
} DadosThreads;

int num_aletaorios(int min, int max)
{
    //gerar os valores aleatorios
    return rand() % (max - min + 1) + min;
}

//thread da logica do produtor
DWORD WINAPI ThreadProdutor(LPVOID param)
{
    DadosThreads* dados=(DadosThreads*)param;
    int contador = 0;
	
    CelulaBuffer cel; //celula do buffer circular que vai ser preenchida
	while(!dados->terminar)
	{
		//logica do produtor
        cel.id = dados->id;
		//um valor aleatorio
        cel.valor = num_aletaorios(10,99);

		//escrever no buffer cicular, se existir uma posição de escrita
        WaitForSingleObject(dados->hSemEscrita, INFINITE); //vão ter 10 posições
        //aceder a essa posição em exclusão mutua
        WaitForSingleObject(dados->hMutex, INFINITE);
		//os dois waits foram desbloqueados então vamos escrever no buffer circular
        CopyMemory(
            &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->posE], //ponteiro (&) para onde vamos copiar
            &cel, //origem
            sizeof(CelulaBuffer)//quantidade de informação que vai ser copiada
            );
		//a proxima posição de escrita tem que ser incremantada
        dados->memoriaPartilhada->posE++;
		//se eu atingir o tamanho do buffer circular, pode ser feito com a logica da divisão por zero
		if(dados->memoriaPartilhada->posE == TAM_BUFFER)
		{
            dados->memoriaPartilhada->posE = 0;
		}
		//libertar o mutex (entre os produtores)
        ReleaseMutex(dados->hMutex);
		//libertar o semaforo: libertar UMA posição de leitura, para o consumidor poder ler
		//o produto espera por uma posição de escrita e liberta uma posição de leitura
		//o consumidor espera por uma posição de leitura e liberta uma posição de escrita
        ReleaseSemaphore(dados->hSemLeitura, 1, NULL);
        contador++; //quantidade de item produzidos em cada iteração
        _tprintf(TEXT("\nProdutor %d, produziu %d"), dados->id, cel.valor);
		
        Sleep(num_aletaorios(2,4)*1000);
	}
    _tprintf(TEXT("\nProdutor %d, produziu %d"), dados->id, contador);
    return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	//criar a estrutua de memoria partilhada
    HANDLE hFileMap;
    //estrutura de dados para a memoria partilhada
    DadosThreads dados;
    //
    BOOL primeirProcesso = FALSE;

	//handle para a thread
    HANDLE hThread;
    TCHAR comando[100];
	
#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif
    //para o aletorio
    srand((unsigned int)time(NULL));

	
    //criar ou abrir os semaforos antes das funções .. para não usar unmaps
	dados.hSemEscrita = CreateSemaphore(
		NULL, //segurança
        TAM_BUFFER,//iniciais no maximo - começa com a totalidade das poisções libertas
        TAM_BUFFER,//finais no maximo - começa com a totalidade das poisções libertas
        TEXT("SO_SEM_ESCRITA")//nome do semaforo
    );
	//controlar as posições que podem ser lidas
    dados.hSemLeitura = CreateSemaphore(
        NULL, //segurança
        0,//iniciais nenhumas, não existe nada para ser lido
        TAM_BUFFER,//finais, o maximo, a totalidade
        TEXT("SO_SEM_LEITURA")//nome do semaforo
    );
    //criar os mutexs para a exclusão mutua dos produtores
    dados.hMutex = CreateMutex(
        NULL,
        FALSE,
        TEXT("SO_MUTEX_PRODUTORES")
    );
	//testar os três ultimos
	if(dados.hSemEscrita == NULL || dados.hSemLeitura == NULL || dados.hMutex == NULL)
	{
        _tprintf(TEXT("\nErro no CreateSemaphore OU no CreateMutex"));
        return 1;
	}
	
    hFileMap = OpenFileMapping(
    FILE_MAP_ALL_ACCESS, //aceder 
        FALSE, //não vai haver processos
        TEXT("SO2_MEM_PARTILHADA") //nome igual ao create file
        );
	if(hFileMap == NULL)
	{
        primeirProcesso = TRUE;
        //existe um poblema no openFileMapping
		//então criamos..
		hFileMap = CreateFileMapping(
        INVALID_HANDLE_VALUE, //handle para o FILE que vai ser criado pelo sistema operativo
            NULL, //atributos de segurança
            PAGE_READWRITE, //nivel de protecção, tipo de acesso , normal ser leitura/escrita
            0, //dimensão da memoria partilhada (mais significativa e menos significativa, a menos fica a 0)
            sizeof(BufferCircular), //parte menos significativa fica com o tamanho da memoria partilhada
            TEXT("SO2_MEM_PARTILHADA")//nome para o file map
            );
            //verificação ao file mapping
            if (hFileMap == NULL)
            {
                _tprintf(TEXT("\nErro no CreateFileMapping"));
                return 1;
            }
	}
    
	//fazer o mapeamento da memoria partilhada
    dados.memoriaPartilhada = (BufferCircular *) MapViewOfFile(
        hFileMap, //o handle para o file mapping
        FILE_MAP_ALL_ACCESS, //permissões escrita/leitura porque tanto o consumidor e produtor fazem as duas coisas
        0, //off-set, a partir do local onde queremos mapear a memoria partilhada
        0,
        0 //tudo a zero porque queremos mapear desde o inicio até ao final..
    );
    //deve haver um cast para o MapViewOfFile, porque se correr bem será um ponteiro para void (BufferCircular *)
    //verificação do MapViewOfFile
    if (dados.memoriaPartilhada == NULL)
    {
        _tprintf(TEXT("\nErro no MapViewOfFile"));
        return 1;
    }
	//temos que incializar as variaveis que estão associados à estrutra circular
    //int nP; //numero de produtores
	//int nC; //numero de consumidores
	//int posE; //proxima posição de escrita
	//int posL; //proxima posição de leitura

	//mas estas variaveis só devem ser carregadas quando o primeiro produtor arrancar, apenas e só!
	if(primeirProcesso == TRUE){
	    dados.memoriaPartilhada->nC = 0;
	    dados.memoriaPartilhada->nP = 0;
	    dados.memoriaPartilhada->posE = 0;
	    dados.memoriaPartilhada->posL = 0;
    }

    dados.terminar = 0;

	//incrementar o numero de produtores
    WaitForSingleObject(dados.hMutex, INFINITE);
    dados.memoriaPartilhada->nP++;
    dados.id = dados.memoriaPartilhada->nP;
	//libertar o mutex
    ReleaseMutex(dados.hMutex);

	//a thread
    hThread = CreateThread(NULL, 0, ThreadProdutor, &dados, 0, NULL);
	if(hThread != NULL)
	{
		//thread criada
        _tprintf(TEXT("\nEscreva qualquer coisa para sair.."));
        _getts_s(comando, 100);
        dados.terminar = 1;
        WaitForSingleObject(hThread, INFINITE);
	}

    //memoria partilhada
    UnmapViewOfFile(dados.memoriaPartilhada);
	//close dos handles: termina quando processo termina, não é preciso criar isto!
	
    return 0;
}

assim fica o consumidor:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <time.h>

#define TAM_BUFFER 10

typedef struct
{
    int id;
    int valor;
} CelulaBuffer;

typedef struct {
    int nP; 
    int nC; 
    int posE; 
    int posL; 
    CelulaBuffer buffer[TAM_BUFFER];
} BufferCircular;

typedef struct
{
    BufferCircular* memoriaPartilhada;
    HANDLE hSemEscrita; 
    HANDLE hSemLeitura;
    HANDLE hMutex; 
    int terminar; 
    int id;
} DadosThreads;

DWORD WINAPI ThreadConsumidor(LPVOID param)
{
    DadosThreads* dados = (DadosThreads*)param;
    int contador = 0;
	//consumidor
    int soma = 0;

    CelulaBuffer cel;
    while (!dados->terminar)
    {
    	//bloquer no semaforo de leitura
    	//consumidor
        WaitForSingleObject(dados->hSemLeitura, INFINITE); 
        WaitForSingleObject(dados->hMutex, INFINITE);
    	//consumidor
        CopyMemory(
            &cel, //variavel interna, local
            &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->posL], //origem, proxima posição de leitura
            sizeof(CelulaBuffer)
        );
        //consumidor
        dados->memoriaPartilhada->posL++;
        //consumidor
        if (dados->memoriaPartilhada->posL == TAM_BUFFER)
        {
            dados->memoriaPartilhada->posL = 0;
        }
        ReleaseMutex(dados->hMutex);
    	//consumidor
        ReleaseSemaphore(dados->hSemEscrita, 1, NULL);
        contador++;
    	//consumidor
        soma += cel.valor;
        _tprintf(TEXT("\nConsumidor %d, consumiu %d"), dados->id, cel.valor);
    }
    _tprintf(TEXT("\nConsumidor %d, somando um valor %d"), dados->id, soma);
    return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
    //criar a estrutua de memoria partilhada
    HANDLE hFileMap;
    //estrutura de dados para a memoria partilhada
    DadosThreads dados;
    //
    BOOL primeirProcesso = FALSE;

    //handle para a thread
    HANDLE hThread;
    TCHAR comando[100];

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif
    //para o aletorio
    srand((unsigned int)time(NULL));

    //criar ou abrir os semaforos antes das funções .. para não usar unmaps
    dados.hSemEscrita = CreateSemaphore(
        NULL, //segurança
        TAM_BUFFER,//iniciais no maximo - começa com a totalidade das poisções libertas
        TAM_BUFFER,//finais no maximo - começa com a totalidade das poisções libertas
        TEXT("SO_SEM_ESCRITA")//nome do semaforo
    );
    //controlar as posições que podem ser lidas
    dados.hSemLeitura = CreateSemaphore(
        NULL, //segurança
        0,//iniciais nenhumas, não existe nada para ser lido
        TAM_BUFFER,//finais, o maximo, a totalidade
        TEXT("SO_SEM_LEITURA")//nome do semaforo
    );
    //consumidor
    dados.hMutex = CreateMutex(
        NULL,
        FALSE,
        TEXT("SO_MUTEX_CONSUMIDORES")
    );
    //testar os três ultimos
    if (dados.hSemEscrita == NULL || dados.hSemLeitura == NULL || dados.hMutex == NULL)
    {
        _tprintf(TEXT("\nErro no CreateSemaphore OU no CreateMutex"));
        return 1;
    }

    hFileMap = OpenFileMapping(
        FILE_MAP_ALL_ACCESS, //aceder 
        FALSE, //não vai haver processos
        TEXT("SO2_MEM_PARTILHADA") //nome igual ao create file
    );
    if (hFileMap == NULL)
    {
        primeirProcesso = TRUE;
        //existe um poblema no openFileMapping
        //então criamos..
        hFileMap = CreateFileMapping(
            INVALID_HANDLE_VALUE, //handle para o FILE que vai ser criado pelo sistema operativo
            NULL, //atributos de segurança
            PAGE_READWRITE, //nivel de protecção, tipo de acesso , normal ser leitura/escrita
            0, //dimensão da memoria partilhada (mais significativa e menos significativa, a menos fica a 0)
            sizeof(BufferCircular), //parte menos significativa fica com o tamanho da memoria partilhada
            TEXT("SO2_MEM_PARTILHADA")//nome para o file map
        );
        //verificação ao file mapping
        if (hFileMap == NULL)
        {
            _tprintf(TEXT("\nErro no CreateFileMapping"));
            return 1;
        }
    }

    //fazer o mapeamento da memoria partilhada
    dados.memoriaPartilhada = (BufferCircular*)MapViewOfFile(
        hFileMap, //o handle para o file mapping
        FILE_MAP_ALL_ACCESS, //permissões escrita/leitura porque tanto o consumidor e produtor fazem as duas coisas
        0, //off-set, a partir do local onde queremos mapear a memoria partilhada
        0,
        0 //tudo a zero porque queremos mapear desde o inicio até ao final..
    );
    //deve haver um cast para o MapViewOfFile, porque se correr bem será um ponteiro para void (BufferCircular *)
    //verificação do MapViewOfFile
    if (dados.memoriaPartilhada == NULL)
    {
        _tprintf(TEXT("\nErro no MapViewOfFile"));
        return 1;
    }
    //temos que incializar as variaveis que estão associados à estrutra circular
    //int nP; //numero de produtores
    //int nC; //numero de consumidores
    //int posE; //proxima posição de escrita
    //int posL; //proxima posição de leitura

    //mas estas variaveis só devem ser carregadas quando o primeiro produtor arrancar, apenas e só!
    if (primeirProcesso == TRUE) {
        dados.memoriaPartilhada->nC = 0;
        dados.memoriaPartilhada->nP = 0;
        dados.memoriaPartilhada->posE = 0;
        dados.memoriaPartilhada->posL = 0;
    }

    dados.terminar = 0;

    //consumidor
    WaitForSingleObject(dados.hMutex, INFINITE);
    dados.memoriaPartilhada->nC++;
    dados.id = dados.memoriaPartilhada->nC;
    //libertar o mutex
    ReleaseMutex(dados.hMutex);

    //a thread
    hThread = CreateThread(NULL, 0, ThreadConsumidor, &dados, 0, NULL);
    if (hThread != NULL)
    {
        //thread criada
        _tprintf(TEXT("\nEscreva qualquer coisa para sair.."));
        _getts_s(comando, 100);
        dados.terminar = 1;
        WaitForSingleObject(hThread, INFINITE);
    }

    //memoria partilhada
    UnmapViewOfFile(dados.memoriaPartilhada);
    //close dos handles: termina quando processo termina, não é preciso criar isto!

    return 0;
}

para relembrar a versão light do semáforo:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <shlwapi.h>

//memória partilhada entre os processos
//usar mecanismos de sincronização, para garantir exclusão mutua nesse bloco de memtória partilhada
//em windows: mapeamento de ficheiros

#define NUM_CARACTERES 100

typedef struct
{
	//ponteiro para memoria partilhada
	TCHAR* fileViewMap;
	//handle para o evento
	HANDLE hEvent;
	//handle para o mutex
	HANDLE hMutex;
	//flag para controlar as threads (aquela que escreve o fim)
	int terminar;
} ThreadDados;

DWORD WINAPI ThreadLer(LPVOID param)
{
	ThreadDados* dados = (ThreadDados*)param;

	while (1)
	{
		//bloquear à espera do evento evitar a espera ativa
		WaitForSingleObject(dados->hEvent, INFINITE);

		if (dados->terminar)
			break;

		WaitForSingleObject(dados->hMutex, INFINITE);

		//desbloqueou: e aqui deve ser o codigo mais curto possivel!!!
		//é uma zona critica
		_tprintf(TEXT("\nmensagem recebida: %s"), dados->fileViewMap);

		//libertar o mutex
		ReleaseMutex(dados->hMutex);
		Sleep(1000);
	}
	return 0;
}

DWORD WINAPI ThreadEscrever(LPVOID param)
{
	ThreadDados* dados = (ThreadDados*)param;
	TCHAR msg[NUM_CARACTERES];

	while (!(dados->terminar))
	{
		_fgetts(msg, NUM_CARACTERES, stdin);
		msg[_tcslen(msg) - 1] = '\0';

		if (_tcscmp(msg, TEXT("fim")) == 0)
		{
			dados->terminar = 1;
		}

		//bloquear à espera do evento evitar a espera ativa
		WaitForSingleObject(dados->hMutex, INFINITE);

		//limpar a memoria partilhada
		ZeroMemory(dados->fileViewMap, NUM_CARACTERES * sizeof(TCHAR));

		//copiar o conteudo para a memoria partilhada
		CopyMemory(dados->fileViewMap, msg, _tcslen(msg) * sizeof(TCHAR));
		//libertar o mutex
		ReleaseMutex(dados->hMutex);

		SetEvent(dados->hEvent);
		Sleep(500);
		ResetEvent(dados->hEvent);
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	HANDLE hfileMap;

	ThreadDados dados;

	//criar as threads
	HANDLE hThreads[2]; //1 de escrita  e 1 de leitura

	//semaforos, é uma generalziação do mutex
	HANDLE hSemaforo;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	//criar o semaforo antes da memoria partilhada
	hSemaforo = CreateSemaphore(NULL, 
		2, //qts começam disponiveis de inicio
		2, //qt são ao mesmo tempo
		TEXT("SO2_SEMAFORO")
	);
	
	if (hSemaforo == NULL)
	{
		_tprintf(TEXT("\nErro no CreateSemaphore"));
		return 1;
	}
	//esperar por uma slote
	_tprintf(TEXT("\naguardar por uma slote.."));
	WaitForSingleObject(hSemaforo, INFINITE); //quem não tem espaço aguarda.
	_tprintf(TEXT("\nchat.."));
	
	hfileMap = CreateFileMapping(
		INVALID_HANDLE_VALUE, //sistema operativo que faça a gestao
		NULL,
		PAGE_READWRITE,
		0,
		NUM_CARACTERES * sizeof(TCHAR), //tamanho do file mapping
		TEXT("SO2_MEM_PART") //nome do file mapping
	);
	if (hfileMap == NULL)
	{
		_tprintf(TEXT("\nErro no CreateFileMapping"));
		return 1;
	}
	dados.fileViewMap = (TCHAR*)MapViewOfFile(
		hfileMap,
		FILE_MAP_ALL_ACCESS,
		0,
		0,
		0
	);

	if (dados.fileViewMap == NULL)
	{
		_tprintf(TEXT("\nErro no fileViewMap"));
		return 1;
	}

	//o evento
	dados.hEvent = CreateEvent(
		NULL,
		TRUE, //reset manual
		FALSE,
		TEXT("SO2_EVENTO")
	);

	if (dados.hEvent == NULL)
	{
		_tprintf(TEXT("\nErro no CreateEvent"));
		UnmapViewOfFile(dados.fileViewMap); //pois ja temos e memoria partilhada
		return 1;
	}

	dados.hMutex = CreateMutex(
		NULL,
		FALSE,
		TEXT("SO2_MUTEX")
	);

	//sincronização da memoria partilhada
	if (dados.hMutex == NULL)
	{
		_tprintf(TEXT("\nErro no CreateMutex"));
		UnmapViewOfFile(dados.fileViewMap); //pois ja temos e memoria partilhada
		return 1;
	}

	dados.terminar = 0;
	//criar as threads (a de ler e a de escrever)
	hThreads[0] = CreateThread(NULL, 0, ThreadLer, &dados, 0, NULL);
	hThreads[1] = CreateThread(NULL, 0, ThreadEscrever, &dados, 0, NULL);

	if (hThreads[0] != NULL && hThreads[1] != NULL)
	{
		WaitForMultipleObjects(2, hThreads, TRUE, INFINITE);
	}

	//lidar com os semaforos
	ReleaseSemaphore(hSemaforo, 1, NULL);

	UnmapViewOfFile(dados.fileViewMap);
	CloseHandle(hfileMap);

	return 0;
}
Tags : , , ,

sessão 6 – Memória partilhada / Sincronização – II Memória partilhada e Semáforos

Bibliografia de apoio:
Capítulos 5, 6, 7 e 8 do Livro Windows System Programming (da bibliografia) (149-155, 194-195, 230-231, 285-287)

MSDN:
Synchronization Objects https://docs.microsoft.com/pt-pt/windows/win32/sync/synchronization-objects
Synchronization Objects (inclui semáforos) https://docs.microsoft.com/pt-pt/windows/win32/sync/synchronization-objects
Wait Functions https://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx
Time Functions https://docs.microsoft.com/en-us/windows/win32/sysinfo/time-functions
Named Shared Memory https://docs.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory

A memória partilhada entre diferentes processos, e é controlado através de um Mutex

A memória partilhada:
é uma zona de memória que pode ser acedida por um ou mais processo, ou threads, em paralelo
o acesso normalmente é controlado com recurso a um semáforo (mutex), ou outro mecanismo de sincronização / exclusão mutua

O mapeamento de ficheiros em memória:
usa-se para facilitar o acesso a ficheiros
o ficheiro comporta-se como uma zona de memória de acesso comum, como se fosse um array partilhado entre processos
CreateFile (abrir o ficheiro),
CreateFileMapping (mapear o ficheiro em memória),
MapViewOfFile (criar uma vista do ficheiro ou parte dele),
FlushViewOfFile (sincronizar memória e ficheiro),
UnmapViewOfFile (terminar vista do ficheiro),
CloseHandle (usado para terminar o mapping e fechar o ficheiro

A memória partilhada:
CreateFileMapping (mapear o ficheiro em memória, criar zona de memória),
OpenFileMapping (obter um ponteiro para zona de memória já criada),
MapViewOfFile (criar uma vista do ficheiro ou parte dele, cada processo cria a sua vista para poder trabalhar),
FlushViewOfFile (sincronizar memória e ficheiro),
UnmapViewOfFile (terminar vista do ficheiro),
CloseHandle (usado para terminar o mapping e fechar o ficheiro

outras funções:
GetSystemInfo
CopyMemory

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

int _tmain(int argc, TCHAR* argv[]) {
	HANDLE hMapeamentoFicheiro, hFicheiro;
	char* pBuffer, aux;
	TCHAR auxt;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	//criar o ficheiro
	hFicheiro = CreateFile(
		TEXT("letras.txt"), 
		GENERIC_READ | GENERIC_WRITE, 
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 
		OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, 
		NULL
	);
	
	if(hFicheiro == INVALID_HANDLE_VALUE)
	{
		_tprintf(TEXT("Erro a abrir o ficheiro - CreateFile (%d)\n"), GetLastError());
		return 1;
	}

	//o mapeamento
	hMapeamentoFicheiro = CreateFileMapping(hFicheiro, NULL, PAGE_READWRITE, 0, 26, NULL);

	if(hMapeamentoFicheiro == NULL){
		_tprintf(TEXT("Erro no file map (%d)\n"), GetLastError());
		CloseHandle(hFicheiro);
	}
	//a vista
	pBuffer = (char*)MapViewOfFile(
		hMapeamentoFicheiro,
		FILE_MAP_READ | FILE_MAP_WRITE, 
		0, 
		0, 
		26
	); //26 bytes, do tamanho da memória partilhada

	if(pBuffer == NULL)
	{
		_tprintf(TEXT("Erro ao criar view da memoria partilada (erro %d)\n"), GetLastError());
		CloseHandle(hMapeamentoFicheiro);
		CloseHandle(hFicheiro);
		return 1;
	}

	_tprintf(TEXT("A modificar o ficheiro em memoria.\n"));

	for(unsigned int i = 0; i <13; i++)
	{
		aux = pBuffer[i];
		pBuffer[i] = pBuffer[25 - i];
		pBuffer[25 - i] = aux;
	}
	
	_tprintf(TEXT("Ficheiro por ordem inversa em memoria.\n"));

	for(unsigned int i=0; i< 26; i++)
	{
		_tprintf(TEXT("%c"), pBuffer[i]);
	}

	UnmapViewOfFile(pBuffer);
	CloseHandle(hMapeamentoFicheiro);
	CloseHandle(hFicheiro);
	
	return 0;
}

um programa que recebe input de um utilizador
o programa escreve em memoria partilhada o que o utilizador escreve
o programa tem uma thread que esta sempre a tentar ler da memoria partilhada
e assim todos os outros programas leem essas mensagens
o acesso à memoria partilhada é controlado pelo mutex
o utilizador só escreve se o semáforo permitir
o controlo de entrada não pode ser feito com mutex, porque só deixa entrar um (é binário o controlo)
assim o mecanismo a usar é o semáforo (pode funcionar como uma pilha).
funções:
CreateSemaphore, OpenSemaphore, WaitForSingleObject, CloseHandle

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define SEM_BOUNCER_NAME TEXT("SEMBOUNCER") //nome do semáforo
#define MAXUSERS 3 //numero de processo que vão passar no semáforo

typedef struct _ControlData{ //tipo de estrutura de dados de controlo
	HANDLE hSemBouncer; //semáforo
} CData;

int _tmain(int argc, TCHAR* argv[]) {
	HANDLE sem; // Handle do semáforo
	CData cdata; //estrutura de dados de controlo

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif
	
	cdata.hSemBouncer = CreateSemaphore(
		NULL, //atributo de segurança
		MAXUSERS, //começa o programa logo com o max users
		MAXUSERS, //limite de users
		SEM_BOUNCER_NAME //nome do semáforo
	);

	if(cdata.hSemBouncer == NULL)
	{
		_tprintf(TEXT("\nerro a criar semáforo"));
		return FALSE;
	}else
	{
		_tprintf(TEXT("Semáforo criado\n"));
	}

	cdata.hSemBouncer = OpenSemaphore( //abrir o semáforo
	SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, 
		FALSE,
		SEM_BOUNCER_NAME
	);

	if(cdata.hSemBouncer == NULL)
	{
		_tprintf(TEXT("Problema na abertura do Semáforo (%d),\n"), GetLastError());
		return FALSE;
	}else
	{
		_tprintf(TEXT("Semáforo aberto.\n"));
	}

	_tprintf(TEXT("Vou aguardar no semáforo para entrar\n"));
	WaitForSingleObject(cdata.hSemBouncer, INFINITE);
	_tprintf(TEXT("Entrei. Qualquer tecla para continuar\n"));
	_gettch();
	ReleaseSemaphore(cdata.hSemBouncer, 1, NULL);
	_tprintf(TEXT("Semáforo libertado\n"));
	CloseHandle(cdata.hSemBouncer);
	
	return 0;
}

Resultado:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define SEM_BOUNCER_NAME TEXT("SEMBOUNCER") //nome do semáforo
#define MAXUSERS 3 //numero de processo que vão passar no semáforo

#define SHM_NAME TEXT("fmMsgSpace") //nome da memória
#define MUTEX_NAME TEXT("RWMUTEX") //nome do mutex

#define MSGTEXT_SZ 100 //tamanho máximo das mensagens
#define MSG_DELAY 500 //intervalo para leitura das mensagens pelos clientes

typedef struct _MSG{
	TCHAR szMessagem[MSGTEXT_SZ];
} Shared_MSG;

typedef struct _ControlData{ //tipo de estrutura de dados de controlo
	HANDLE hSemBouncer; //semáforo
	//para o mutex
	HANDLE hMapFile, hRWMutex;
	Shared_MSG* shared; // ponteiro para memoria partilhada
	HANDLE newmsg; //ponteiro para o evento
	int continuar; // vairvel para terminar as threads
} CData;

//função que lê do teclado e envia para memoria partilhada
void le_envia(CData * pcd) 
{
	Shared_MSG msg; //estrutura local

	while(pcd->continuar)
	{
		_tprintf(TEXT("Escreve mensagem (fim para sair)"));
		wscanf_s(TEXT("%s"), msg.szMessagem, (unsigned)_countof(msg.szMessagem));
		//_tprintf(TEXT("%s\n"), msg.szMessagem);

		if(_tcscmp(msg.szMessagem, TEXT("fim"))==0)
		{
			break;
		}

		WaitForSingleObject(pcd->hRWMutex, INFINITE);
		CopyMemory(pcd->shared, &msg, sizeof(Shared_MSG));
		ReleaseMutex(pcd->hRWMutex);

		//assinlar que há nova mensagem
		SetEvent(pcd->newmsg);
		//esperar que mensagem seja lida por outros processos
		Sleep(MSG_DELAY);
		//desativar evento
		ResetEvent(pcd->newmsg);
	}
	pcd->continuar = 0;
	_tprintf(TEXT("\nPrograma vai terminar"));
	//para desbloquear a outra thread e sair de imediato
	SetEvent(pcd->newmsg); 
}

DWORD WINAPI recebe_msg(LPVOID p)
{
	CData* pcd = (CData*)p;
	Shared_MSG msg;

	while(1)
	{
		WaitForSingleObject(pcd->newmsg, INFINITE); //aguardar evento
		if(!pcd->continuar)
		{
			break;
		}
		WaitForSingleObject(pcd->hRWMutex, INFINITE); //obter mutex
		CopyMemory(&msg, pcd->shared, sizeof(Shared_MSG));
		ReleaseMutex(pcd->hRWMutex);

		_tprintf(TEXT("Recebi: %s\n"), msg.szMessagem );
		Sleep(MSG_DELAY * 2);
	}
	//no cao de haver outras thread, evento não fica ativo para elas quando esta thread termina
	ResetEvent(pcd->newmsg);
	return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	HANDLE sem; // Handle do semáforo
	CData cdata; //estrutura de dados de controlo
	DWORD tid; //estrutura de dados de controlo
	
#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif
	
	cdata.hSemBouncer = CreateSemaphore(
		NULL, //atributo de segurança
		MAXUSERS, //começa o programa logo com o max users
		MAXUSERS, //limite de users
		SEM_BOUNCER_NAME //nome do semaforo
	);

	if(cdata.hSemBouncer == NULL)
	{
		_tprintf(TEXT("\nerro a criar semaforo"));
		return FALSE;
	}else
	{
		_tprintf(TEXT("Semáforo criado\n"));
	}

	cdata.hSemBouncer = OpenSemaphore( //abrir o semaforo
	SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, 
		FALSE,
		SEM_BOUNCER_NAME
	);

	if(cdata.hSemBouncer == NULL)
	{
		_tprintf(TEXT("\nProblema na aberura do Semáforo (%d)"), GetLastError());
		return FALSE;
	}else
	{
		_tprintf(TEXT("Semáforo aberto.\n"));
	}

	_tprintf(TEXT("\nVou agurdar no semáforo para entrar"));
	WaitForSingleObject(cdata.hSemBouncer, INFINITE);

	//criar a zona de memória 
	cdata.hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,
		NULL,
		PAGE_READWRITE,
		0,
		sizeof(Shared_MSG),
		SHM_NAME
	);

	if (cdata.hMapFile == NULL)
	{
		_tprintf(TEXT("Problema em criar zona de memoria (%d),\n"), GetLastError());
		return FALSE;
	}
	else
	{
		_tprintf(TEXT("\ncriada zona de memoria."));
	}
	
	//criar a zona de memória - a vista
	cdata.shared = (Shared_MSG*)MapViewOfFile(
		cdata.hMapFile, 
		FILE_MAP_ALL_ACCESS, 
		0, 
		0, //vista sobre a memoria inteira
		sizeof(Shared_MSG)
	);

	if (cdata.shared == NULL)
	{
		_tprintf(TEXT("Problema em criar a vista (%d),\n"), GetLastError());
		return FALSE;
	}
	else
	{
		_tprintf(TEXT("\ncriada a vista."));
	}
	
	//criar a zona de memória - o mutex (com um nome conhecido para todos os processos)
	cdata.hRWMutex = CreateMutex(
		NULL, 
		FALSE, 
		MUTEX_NAME
	);

	if (cdata.hRWMutex == NULL)
	{
		_tprintf(TEXT("Problema em criar o mutex (%d),\n"), GetLastError());
		return FALSE;
	}
	else
	{
		_tprintf(TEXT("\nmutex criado."));
	}

	cdata.newmsg = CreateEvent(NULL, TRUE, FALSE, TEXT("Evento"));
	cdata.newmsg = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, TEXT("Evento"));

	if(cdata.newmsg == NULL)
	{
		_tprintf(TEXT("Erro ao criar evento %s (%d).\n"), TEXT("Evento"), GetLastError());
		return FALSE;
	}else
	{
		_tprintf(TEXT("\nEvento lançado com sucesso"));
	}
	
	_tprintf(TEXT("Lançar a thread para ouvir o que se passa\n"));
	cdata.continuar = 1;
	sem = CreateThread(NULL, 0, recebe_msg, &cdata, 0, &tid);
	le_envia(&cdata);

	_tprintf(TEXT("Este cliente vai fechar\n"));
	WaitForSingleObject(sem, INFINITE);
	_tprintf(TEXT("Thread ouvinte encerrada\n"));
	
	ReleaseSemaphore(cdata.hSemBouncer, 1, NULL);

	UnmapViewOfFile(cdata.shared); //memoria partilhada
	CloseHandle(cdata.hMapFile);
	CloseHandle(cdata.newmsg);
	CloseHandle(cdata.hSemBouncer);
	
	return 0;
}

Resultado:

esta é uma versão melhorada do exercício anterior, já que permite ter um “control” que vai recebendo pedidos de “aviões” e que dá resposta (caso seja positiva) a cada um dos “aviões”
pelo “control” surge o seguinte:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define SEM_BOUNCER_NAME TEXT("SEMBOUNCER") //nome do semáforo
#define MAXUSERS 3 //numero de processo que vão passar no semáforo

#define SHM_NAME TEXT("fmMsgSpace") //nome da memória
#define MUTEX_NAME TEXT("RWMUTEX") //nome do mutex

#define MSGTEXT_SZ 100 //tamanho máximo das mensagens
#define MSG_DELAY 500 //intervalo para leitura das mensagens pelos clientes

typedef struct _MSG_AEROPORTO {
    TCHAR szMessagem[MSGTEXT_SZ];
    int pidMessagemControl; //aviao
    int pidMessagemAviao; //areoporto
    int comando; //0, semResposta, 1 pedido de aeroporto, 2, ok aeroporto, -1 , erro aeroporto
} Shared_MSG;

typedef struct _ControlData { //tipo de estrutura de dados de controlo
    HANDLE hSemBouncer; //semáforo
    //para o mutex
    HANDLE hMapFile, hRWMutex;
    Shared_MSG* shared; // ponteiro para memoria partilhada
    HANDLE newmsg; //ponteiro para o evento
    int continuar; // vairvel para terminar as threads
} CData;

//função que lê do teclado e envia para memoria partilhada
void le_envia(CData* pcd)
{
    Shared_MSG msg; //estrutura local

    while (pcd->continuar)
    {
        msg.pidMessagemControl = GetCurrentProcessId();
        _tprintf(TEXT("\n[control: %d] Escreve mensagem (fim para sair)"), msg.pidMessagemControl);
        wscanf_s(TEXT("%s"), msg.szMessagem, (unsigned)_countof(msg.szMessagem));
        //_tprintf(TEXT("%s\n"), msg.szMessagem);
        
    	
        if (_tcscmp(msg.szMessagem, TEXT("fim")) == 0)
        {
            break;
        }

        WaitForSingleObject(pcd->hRWMutex, INFINITE);
        CopyMemory(pcd->shared, &msg, sizeof(Shared_MSG)); //dest , source
        ReleaseMutex(pcd->hRWMutex);

        //assinlar que há nova mensagem
        SetEvent(pcd->newmsg);
        //esperar que mensagem seja lida por outros processos
        Sleep(MSG_DELAY);
        //desativar evento
        ResetEvent(pcd->newmsg);
    }
    pcd->continuar = 0;
    _tprintf(TEXT("\nPrograma vai terminar"));
    //para desbloquear a outra thread e sair de imediato
    SetEvent(pcd->newmsg);
}

DWORD WINAPI recebe_msg(LPVOID p)
{
    CData* pcd = (CData*)p;
    Shared_MSG msg;
    TCHAR nome[MSGTEXT_SZ];
    _tcscpy_s(nome, _countof(nome), _T("coimbra"));

    while (1)
    {
        WaitForSingleObject(pcd->newmsg, INFINITE); //aguardar evento
        if (!pcd->continuar)
        {
            break;
        }
        WaitForSingleObject(pcd->hRWMutex, INFINITE); //obter mutex
        CopyMemory(&msg, pcd->shared, sizeof(Shared_MSG)); //dest , source
        ReleaseMutex(pcd->hRWMutex);

    	if(msg.comando == 1){
			_tprintf(TEXT("\nRecebi: %s do %d\n"), msg.szMessagem, msg.pidMessagemAviao);
            if (wcscmp(msg.szMessagem, _T("coimbra")) == 0)
            {
                _tprintf(TEXT("\nSenhor aviao existe esse aeroporto.. volte sempre %d!"), msg.pidMessagemAviao);
                pcd->shared->pidMessagemAviao = msg.pidMessagemAviao;
                pcd->shared->comando = 2;
                //Sleep(MSG_DELAY);
            }else
            {
                pcd->shared->comando = -1;
            }
        }else
        {
            _tprintf(TEXT("\nAguardo por um pedido formal"));
            pcd->shared->comando = -1;
        }
        Sleep(MSG_DELAY * 2);
        pcd->shared->comando = -1;
    }
    //no cao de haver outras thread, evento não fica ativo para elas quando esta thread termina
    ResetEvent(pcd->newmsg);
    return 0;
}
BOOL comunicaAeroporto(BOOL valor)
{
    HANDLE sem; // Handle do semáforo
    CData cdata; //estrutura de dados de controlo
    DWORD tid; //estrutura de dados de controlo

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif

    cdata.hSemBouncer = CreateSemaphore(
        NULL, //atributo de segurança
        MAXUSERS, //começa o programa logo com o max users
        MAXUSERS, //limite de users
        SEM_BOUNCER_NAME //nome do semaforo
    );

    if (cdata.hSemBouncer == NULL)
    {
        _tprintf(TEXT("\nerro a criar semaforo"));
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nSemáforo criado"));
    }

    cdata.hSemBouncer = OpenSemaphore( //abrir o semaforo
        SYNCHRONIZE | SEMAPHORE_MODIFY_STATE,
        FALSE,
        SEM_BOUNCER_NAME
    );

    if (cdata.hSemBouncer == NULL)
    {
        _tprintf(TEXT("\nProblema na aberura do Semáforo (%d)"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nSemáforo aberto."));
    }

    _tprintf(TEXT("\nVou agurdar no semáforo para entrar"));
    WaitForSingleObject(cdata.hSemBouncer, INFINITE);

    //criar a zona de memória 
    cdata.hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        0,
        sizeof(Shared_MSG),
        SHM_NAME
    );

    if (cdata.hMapFile == NULL)
    {
        _tprintf(TEXT("\nProblema em criar zona de memoria (%d),"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\ncriada zona de memoria."));
    }

    //criar a zona de memória - a vista
    cdata.shared = (Shared_MSG*)MapViewOfFile(
        cdata.hMapFile,
        FILE_MAP_ALL_ACCESS,
        0,
        0, //vista sobre a memoria inteira
        sizeof(Shared_MSG)
    );

    if (cdata.shared == NULL)
    {
        _tprintf(TEXT("\nProblema em criar a vista (%d),"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\ncriada a vista."));
    }

    //criar a zona de memória - o mutex (com um nome conhecido para todos os processos)
    cdata.hRWMutex = CreateMutex(
        NULL,
        FALSE,
        MUTEX_NAME
    );

    if (cdata.hRWMutex == NULL)
    {
        _tprintf(TEXT("\nProblema em criar o mutex (%d),"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nmutex criado."));
    }

    cdata.newmsg = CreateEvent(NULL, TRUE, FALSE, TEXT("Evento"));
    cdata.newmsg = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, TEXT("Evento"));

    if (cdata.newmsg == NULL)
    {
        _tprintf(TEXT("\nErro ao criar evento %s (%d)."), TEXT("Evento"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nEvento lançado com sucesso"));
    }

    _tprintf(TEXT("\nLançar a thread para ouvir o que se passa"));
    cdata.continuar = 1;
    sem = CreateThread(NULL, 0, recebe_msg, &cdata, 0, &tid);
    le_envia(&cdata);

    _tprintf(TEXT("\nEste cliente vai fechar"));
    WaitForSingleObject(sem, INFINITE);
    _tprintf(TEXT("\nThread ouvinte encerrada"));

    ReleaseSemaphore(cdata.hSemBouncer, 1, NULL);

    UnmapViewOfFile(cdata.shared); //memoria partilhada
    CloseHandle(cdata.hMapFile);
    CloseHandle(cdata.newmsg);
    CloseHandle(cdata.hSemBouncer);

	valor = TRUE;
    return valor;
}


int _tmain(int argc, TCHAR* argv[]) {
    BOOL entra = FALSE;

    while (entra == FALSE)
    {
        entra = comunicaAeroporto(FALSE);
    }
}

pelo “aviao” surge o seguinte:

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define SEM_BOUNCER_NAME TEXT("SEMBOUNCER") //nome do semáforo
#define MAXUSERS 3 //numero de processo que vão passar no semáforo

#define SHM_NAME TEXT("fmMsgSpace") //nome da memória
#define MUTEX_NAME TEXT("RWMUTEX") //nome do mutex

#define MSGTEXT_SZ 100 //tamanho máximo das mensagens
#define MSG_DELAY 500 //intervalo para leitura das mensagens pelos clientes

typedef struct _MSG_AEROPORTO {
    TCHAR szMessagem[MSGTEXT_SZ];
    int pidMessagemControl; //aviao
    int pidMessagemAviao; //areoporto
    int comando; //0, semResposta, 1 pedido de aeroporto, 2, ok aeroporto, -1 , erro aeroporto
} Shared_MSG;

typedef struct _ControlData { //tipo de estrutura de dados de controlo
    HANDLE hSemBouncer; //semáforo
    //para o mutex
    HANDLE hMapFile, hRWMutex;
    Shared_MSG* shared; // ponteiro para memoria partilhada
    HANDLE newmsg; //ponteiro para o evento
    int continuar; // vairvel para terminar as threads
} CData;

//função que lê do teclado e envia para memoria partilhada
void le_envia(CData* pcd)
{
    Shared_MSG msg; //estrutura local
    

    while (pcd->continuar)
    {
        msg.pidMessagemAviao = GetCurrentProcessId();

        if (_tcscmp(msg.szMessagem, TEXT("fim")) == 0 || (pcd->shared->pidMessagemAviao == GetCurrentProcessId()  && pcd->shared->comando == 2))
        {
            _tprintf(TEXT("\ndone.."));
            break;
        }
        _tprintf(TEXT("\n[aviao: %d]Escreve mensagem (fim para sair)"), msg.pidMessagemAviao);
        wscanf_s(TEXT("%s"), msg.szMessagem, (unsigned)_countof(msg.szMessagem));
        //_tprintf(TEXT("%s\n"), msg.szMessagem);
    	
        msg.comando = 1;
        WaitForSingleObject(pcd->hRWMutex, INFINITE);
        CopyMemory(pcd->shared, &msg, sizeof(Shared_MSG)); //dest , source
        ReleaseMutex(pcd->hRWMutex);

        //assinlar que há nova mensagem
        SetEvent(pcd->newmsg);
        //esperar que mensagem seja lida por outros processos
        Sleep(MSG_DELAY);
        //desativar evento
        ResetEvent(pcd->newmsg);
    }
    pcd->continuar = 0;
    _tprintf(TEXT("\nPrograma vai terminar"));
    //para desbloquear a outra thread e sair de imediato
    SetEvent(pcd->newmsg);
}

DWORD WINAPI recebe_msg(LPVOID p)
{
    CData* pcd = (CData*)p;
    Shared_MSG msg;

    while (1)
    {
        WaitForSingleObject(pcd->newmsg, INFINITE); //aguardar evento
        if (!pcd->continuar)
        {
            break;
        }
        WaitForSingleObject(pcd->hRWMutex, INFINITE); //obter mutex
        CopyMemory(&msg, pcd->shared, sizeof(Shared_MSG)); //dest , source
        ReleaseMutex(pcd->hRWMutex);

    	if(msg.pidMessagemAviao == GetCurrentProcessId()){
            _tprintf(TEXT("\nProblemas com o nome do aeroporto"));
            msg.comando = 1;
        }else
        {
            _tprintf(TEXT("\n.."));
            //_tprintf(TEXT("\nRecebi: %s do %d"), msg.szMessagem, msg.pidMessagemControl);
        }
    	
        Sleep(MSG_DELAY * 2);
    }
    //no cao de haver outras thread, evento não fica ativo para elas quando esta thread termina
    ResetEvent(pcd->newmsg);
    return 0;
}

BOOL comunicaAeroporto(BOOL valor)
{
    HANDLE sem; // Handle do semáforo
    CData cdata; //estrutura de dados de controlo
    DWORD tid; //estrutura de dados de controlo

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif

    cdata.hSemBouncer = CreateSemaphore(
        NULL, //atributo de segurança
        MAXUSERS, //começa o programa logo com o max users
        MAXUSERS, //limite de users
        SEM_BOUNCER_NAME //nome do semaforo
    );

    if (cdata.hSemBouncer == NULL)
    {
        _tprintf(TEXT("\nerro a criar semaforo"));
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("Semáforo criado\n"));
    }

    cdata.hSemBouncer = OpenSemaphore( //abrir o semaforo
        SYNCHRONIZE | SEMAPHORE_MODIFY_STATE,
        FALSE,
        SEM_BOUNCER_NAME
    );

    if (cdata.hSemBouncer == NULL)
    {
        _tprintf(TEXT("\nProblema na aberura do Semáforo (%d)"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("Semáforo aberto.\n"));
    }

    _tprintf(TEXT("\nVou agurdar no semáforo para entrar"));
    WaitForSingleObject(cdata.hSemBouncer, INFINITE);

    //criar a zona de memória 
    cdata.hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,
        NULL,
        PAGE_READWRITE,
        0,
        sizeof(Shared_MSG),
        SHM_NAME
    );

    if (cdata.hMapFile == NULL)
    {
        _tprintf(TEXT("Problema em criar zona de memoria (%d),\n"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\ncriada zona de memoria."));
    }

    //criar a zona de memória - a vista
    cdata.shared = (Shared_MSG*)MapViewOfFile(
        cdata.hMapFile,
        FILE_MAP_ALL_ACCESS,
        0,
        0, //vista sobre a memoria inteira
        sizeof(Shared_MSG)
    );

    if (cdata.shared == NULL)
    {
        _tprintf(TEXT("Problema em criar a vista (%d),\n"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\ncriada a vista."));
    }

    //criar a zona de memória - o mutex (com um nome conhecido para todos os processos)
    cdata.hRWMutex = CreateMutex(
        NULL,
        FALSE,
        MUTEX_NAME
    );

    if (cdata.hRWMutex == NULL)
    {
        _tprintf(TEXT("Problema em criar o mutex (%d),\n"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nmutex criado."));
    }

    cdata.newmsg = CreateEvent(NULL, TRUE, FALSE, TEXT("Evento"));
    cdata.newmsg = OpenEvent(SYNCHRONIZE | EVENT_MODIFY_STATE, TRUE, TEXT("Evento"));

    if (cdata.newmsg == NULL)
    {
        _tprintf(TEXT("Erro ao criar evento %s (%d).\n"), TEXT("Evento"), GetLastError());
        return FALSE;
    }
    else
    {
        _tprintf(TEXT("\nEvento lançado com sucesso"));
    }

    _tprintf(TEXT("Lançar a thread para ouvir o que se passa\n"));
    cdata.continuar = 1;
    sem = CreateThread(NULL, 0, recebe_msg, &cdata, 0, &tid);
    le_envia(&cdata);

    _tprintf(TEXT("Este cliente vai fechar\n"));
    WaitForSingleObject(sem, INFINITE);
    _tprintf(TEXT("Thread ouvinte encerrada\n"));

    ReleaseSemaphore(cdata.hSemBouncer, 1, NULL);

    UnmapViewOfFile(cdata.shared); //memoria partilhada
    CloseHandle(cdata.hMapFile);
    CloseHandle(cdata.newmsg);
    CloseHandle(cdata.hSemBouncer);

    valor = TRUE;
    return valor;
}


int _tmain(int argc, TCHAR* argv[]) {
    BOOL entra = FALSE;



    while (entra == FALSE)
    {
        entra = comunicaAeroporto(FALSE);
    }
    _tprintf(TEXT("\ntou feliz... cara#######"));
    return 0;
}
Tags : , , ,

sessão 5 – Gestão básica de threads em Win32

Bibliografia de apoio:
Capítulos 6 e 8 do Livro Windows System Programming (da bibliografia)

MSDN:
Synchronization Objects https://docs.microsoft.com/pt-pt/windows/win32/sync/synchronization-objects
Wait Functions https://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx
Time Functions https://docs.microsoft.com/en-us/windows/win32/sysinfo/time-functions

Criar e sincronizar threads:
modelo de exclusão mutua ( com mutexes, um semáforo simplificado )
existe uma zona de memória que é partilhada entre threads mas que deve ser usada apenas quando está livre e trancada quando está ocupada (secção critica)
e serve para o mesmo processo ou entre processos
uma thread suspensa podem voltar com ResumeThread( HANDLE )

#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200
#define MAX_THREADS 2

BOOL sairThread = FALSE;

typedef struct {
	DWORD total_soma;
	int lim_inf, lim_sup;
} dados_thread;

DWORD WINAPI SomaPares(LPVOID lpParametro)
{
	dados_thread* dados = (dados_thread*) lpParametro;

	_tprintf(TEXT("Sou a thread %d somar pares de %d a %d\n"), GetCurrentThreadId(), dados->lim_inf, dados->lim_sup);
	
	for (int i = dados->lim_inf; i <= dados->lim_sup; i++)
	{
		if (i % 2 == 0){
			dados->total_soma += i;
		}
		if (i % 200 == 0){
			Sleep(1000);
		}
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	HANDLE hThreadArray[MAX_THREADS]; //handle threads
	dados_thread dado[MAX_THREADS];
	DWORD tid[MAX_THREADS], resultado;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	//pedir aqui ao utilizador os intervalos
	_tprintf(TEXT("qual o valor MIN do 1º intervalo\n"));
	wscanf_s(TEXT("%d"), &dado[0].lim_inf);
	_tprintf(TEXT("qual o valor MAX do 1º intervalo\n"));
	wscanf_s(TEXT("%d"), &dado[0].lim_sup);
	_tprintf(TEXT("qual o valor MIN do 2º intervalo\n"));
	wscanf_s(TEXT("%d"), &dado[1].lim_inf);
	_tprintf(TEXT("qual o valor MAX do 2º intervalo\n"));
	wscanf_s(TEXT("%d"), &dado[1].lim_sup);
	

	dado[0].total_soma = dado[1].total_soma = 0;
	
	hThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[0], 0, &(tid[0]));
	hThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) SomaPares, &dado[1], 0, &(tid[1]));

	if (hThreadArray[0] == NULL || hThreadArray[1] == NULL)
	{
		_tprintf(TEXT("nada de threads"));
		return -1;
	}
	
	//esperar pela primeira para terminar
	resultado = WaitForMultipleObjects(2, hThreadArray, FALSE, INFINITE);

	if(resultado == WAIT_OBJECT_0)
	{
		_tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[0], dado[0].total_soma);
		_tprintf(TEXT("resultado da thread[%d]: %d\n"), tid[1], dado[1].total_soma);
	}
	
	CloseHandle(hThreadArray[0]);
	CloseHandle(hThreadArray[1]);

	return 0;
}

como medir o tempo de execução de um programa no windows?

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main(void)
{
	LARGE_INTEGER frequencia, inicio, fim;
	double intervalo;

	QueryPerformanceFrequency(&frequencia); // obter ticks por segundo

	QueryPerformanceCounter(&inicio); //obter ticks iniciais
	//executar o codigo

	QueryPerformanceCounter(&fim); // obter ticks finais
	
	intervalo = (double)(fim.QuadPart - inicio.QuadPart) / frequencia.QuadPart; //obter tempo em segundos
	printf("O codigo demorou %.3f segundos a executar.\n", intervalo);
	
	return 0;
}
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define MAX_THREADS 20

typedef struct {
	unsigned int limiteBaixo;
	unsigned int limiteAlto;
	unsigned int* contadorPartilhado;
} TDados;

DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
	unsigned int i;
	TDados* data = (TDados*)lmParam;

	for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
	{
		if (i % 3 == 0)
		{
			(*(data->contadorPartilhado))++;
		}
	}
	return 0;
}


int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hThreads[MAX_THREADS];
	TDados tdados[MAX_THREADS];
	unsigned int numeroThreads, i, limSuperior = 400000000, contador = 0;
	LARGE_INTEGER tickSegundo, inicio, fim;
	double duracao;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	if (!QueryPerformanceCounter(&tickSegundo))
	{
		_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
	}
	_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
	_tscanf_s(TEXT("%u"), &numeroThreads);

	//lançar as threads (suspensas)
	for (i = 0; i < numeroThreads; i++)
	{
		tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
		tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
		tdados[i].contadorPartilhado = &contador;

		_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
		hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
	}
	
	//começar o cronometro, ativar as threadas
	QueryPerformanceCounter(&inicio);
	for (i = 0; i < numeroThreads; i++) //dividir os intervalos
	{
		ResumeThread(hThreads[i]); //ativar as threads
	}
	WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
	//esperar que as threads terminem, ler o cronometro
	QueryPerformanceCounter(&fim);

	duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
	_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);
	
	for (i = 0; i < numeroThreads; i++)
	{
		CloseHandle(hThreads[i]);
	}
	//existe um problema de sincronização
	//falta sincronização das threads
	//o numero ideal de threads é igual ao numero de cores do processador
	//4 cores, 4 threads
	//4 cores, 5 thread, fica mais lento...
	//o escalonamento de processos..
	return 0;
}
//usar mutexes para garantir acesso em exlucsão mutua ao contador global
//createMutexA, waitForSingleObject, ReleaseMutex, CloseHandle

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define MAX_THREADS 20

typedef struct {
	unsigned int limiteBaixo;
	unsigned int limiteAlto;
	unsigned int* contadorPartilhado;
	//receber o mutex
	HANDLE mutexPartilhado;
} TDados;

DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
	unsigned int i;
	TDados* data = (TDados*)lmParam;

	for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
	{
		if (i % 3 == 0)
		{
			//esperar no mutex, incrementar, libertar
			WaitForSingleObject(data->mutexPartilhado, INFINITE);
			(*(data->contadorPartilhado))++;
			ReleaseMutex(data->mutexPartilhado);
		}
	}
	return 0;
}


int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hThreads[MAX_THREADS];
	TDados tdados[MAX_THREADS];
	unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
	LARGE_INTEGER tickSegundo, inicio, fim;
	double duracao;
	//o mutex
	HANDLE mutex;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	if (!QueryPerformanceCounter(&tickSegundo))
	{
		_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
	}
	_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
	_tscanf_s(TEXT("%u"), &numeroThreads);

	//criar o mutex
	mutex = CreateMutex(NULL, FALSE, NULL);
	
	//lançar as threads (suspensas)
	for (i = 0; i < numeroThreads; i++)
	{
		tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
		tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
		tdados[i].contadorPartilhado = &contador;

		//passar o mutex
		tdados[i].mutexPartilhado = mutex;

		_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
		hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
	}

	//começar o cronometro, ativar as threadas
	QueryPerformanceCounter(&inicio);
	for (i = 0; i < numeroThreads; i++) //dividir os intervalos
	{
		ResumeThread(hThreads[i]); //ativar as threads
	}
	WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
	//esperar que as threads terminem, ler o cronometro
	QueryPerformanceCounter(&fim);

	duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
	_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);

	for (i = 0; i < numeroThreads; i++)
	{
		CloseHandle(hThreads[i]);
	}
	//fechar o mutex
	CloseHandle(mutex);
	//agora os resultados já nao vão variar, vão ser sempre os corretos
	//o tempo de execução aumenta
	return 0;
}

critical section: recurso partilhado (em vez de mutexes)
mecanismo de controlo de acesso: CriticalSections, espécie de mutex, que tambem controla o acesso à secção critica
é uma espera ativa, que vai verificar um determinado numero de vezes se pode entrar
criticalsection é como um mini-mutex optimizado para threads dentro do mesmo processo
a thread tem um periodo de espera ativa à entrada da criticalsection, em que vai consultado para ver se pode entrar um numero de vezes (no max spin count)
pode aliviar o esforço de sinalização entre threads principalmente para spin count baixos
eficicente quando o recurso fica bloqueado por períodos curtos: assim não é preciso suspender e depois sinalizar threads tantas vezes

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define MAX_THREADS 20

typedef struct {
	unsigned int limiteBaixo;
	unsigned int limiteAlto;
	unsigned int* contadorPartilhado;
	//referencia da criticalSection
	CRITICAL_SECTION * cirticalSectionPartilhada;
} TDados;

DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
	unsigned int i;
	TDados* data = (TDados*)lmParam;

	for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
	{
		if (i % 3 == 0)
		{
			//entrada na critical section
			EnterCriticalSection(data->cirticalSectionPartilhada);
			(*(data->contadorPartilhado))++;
			//saida na critical section
			LeaveCriticalSection(data->cirticalSectionPartilhada);
		}
	}
	return 0;
}


int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hThreads[MAX_THREADS];
	TDados tdados[MAX_THREADS];
	unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
	LARGE_INTEGER tickSegundo, inicio, fim;
	double duracao;
	//declaração variavel critical section
	CRITICAL_SECTION criticalSection;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	if (!QueryPerformanceCounter(&tickSegundo))
	{
		_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
	}
	_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
	_tscanf_s(TEXT("%u"), &numeroThreads);

	//inicializar da criticalsection
	if(!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal..
	{
		return 0;
	}

	//lançar as threads (suspensas)
	for (i = 0; i < numeroThreads; i++)
	{
		tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
		tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
		tdados[i].contadorPartilhado = &contador;

		//passar o ponteiro da critical section
		tdados[i].cirticalSectionPartilhada = &criticalSection;

		_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
		hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
	}

	//começar o cronometro, ativar as threadas
	QueryPerformanceCounter(&inicio);
	for (i = 0; i < numeroThreads; i++) //dividir os intervalos
	{
		ResumeThread(hThreads[i]); //ativar as threads
	}
	WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
	//esperar que as threads terminem, ler o cronometro
	QueryPerformanceCounter(&fim);

	duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
	_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);

	for (i = 0; i < numeroThreads; i++)
	{
		CloseHandle(hThreads[i]);
	}
	//remover a criticalsection
	DeleteCriticalSection(&criticalSection);

	//melhoria de desempenho muito consideravel
	//
	
	return 0;
}

eventos: atuam como flags para assinalar à thread se pode continuar ou não
podem ser usados para outros cenários que não threads
a usar: CreateEventA, SetEvent

#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

#define MAX_THREADS 20

typedef struct {
	unsigned int limiteBaixo;
	unsigned int limiteAlto;
	unsigned int* contadorPartilhado;
	CRITICAL_SECTION* cirticalSectionPartilhada;
	//referência a evento
	HANDLE eventoPartilhado;
	
} TDados;

DWORD WINAPI ContaMultiplosTres(LPVOID lmParam)
{
	unsigned int i;
	TDados* data = (TDados*)lmParam;
	//aguardar evento antes de começar, a thread fica à espera que o evento seja TRUE
	WaitForSingleObject(data->eventoPartilhado, INFINITE);

	for (i = data->limiteBaixo; i <= data->limiteAlto; i++)
	{
		if (i % 3 == 0)
		{
			EnterCriticalSection(data->cirticalSectionPartilhada);
			(*(data->contadorPartilhado))++;
			LeaveCriticalSection(data->cirticalSectionPartilhada);
		}
	}
	return 0;
}


int _tmain(int argc, TCHAR* argv[])
{
	HANDLE hThreads[MAX_THREADS];
	TDados tdados[MAX_THREADS];
	unsigned int numeroThreads, i, limSuperior = 4000000, contador = 0;
	LARGE_INTEGER tickSegundo, inicio, fim;
	double duracao;
	CRITICAL_SECTION criticalSection;
	//evento
	HANDLE evento;
	

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	if (!QueryPerformanceCounter(&tickSegundo))
	{
		_tprintf(TEXT("Erro: obter ticks por segundo.\n"));
	}
	_tprintf(TEXT("\nNumero de threads (max %d)->"), MAX_THREADS);
	_tscanf_s(TEXT("%u"), &numeroThreads);

	if (!InitializeCriticalSectionAndSpinCount(&criticalSection, 400)) //spinout 400, normal..
	{
		return 0;
	}
	//criar o evento
	evento = CreateEvent(NULL, TRUE, FALSE, NULL);
	for (i = 0; i < numeroThreads; i++)
	{
		tdados[i].limiteBaixo = 1 + (limSuperior / numeroThreads) * i;
		tdados[i].limiteAlto = (limSuperior / numeroThreads) * (i + 1);
		tdados[i].contadorPartilhado = &contador;

		tdados[i].cirticalSectionPartilhada = &criticalSection;

		//passar o evento para a thread
		tdados[i].eventoPartilhado = evento;

		_tprintf(TEXT("thread %u: %u a %u\n"), i, tdados[i].limiteBaixo, tdados[i].limiteAlto);
		hThreads[i] = CreateThread(NULL, 0, ContaMultiplosTres, &tdados[i], CREATE_SUSPENDED, NULL);
	}

	QueryPerformanceCounter(&inicio);
	//gerar evento para threads avançarem
	SetEvent(evento);

	//o ResumeThread já não é necessário
	
	WaitForMultipleObjects(numeroThreads, hThreads, TRUE, INFINITE); //esperar que as threads terminem
	QueryPerformanceCounter(&fim);

	duracao = (double)(fim.QuadPart - inicio.QuadPart) / tickSegundo.QuadPart;
	_tprintf(TEXT("Contados %u numeros em %lf segundos.\n"), contador, duracao);

	for (i = 0; i < numeroThreads; i++)
	{
		CloseHandle(hThreads[i]);
	}
	DeleteCriticalSection(&criticalSection);
	//termina a o evento
	CloseHandle(evento);

	//as threds começam todas ao mesmo tempo, o tempo vai demorar
	//este mecanismo permite desbloquear um processo
	
	return 0;
}
Tags : , , ,

Magallanes_ICC :

Está a decorrer um “Open Call Programa de Apoio ao Empreendedorismo Criativo” onde se inclui a apresentação para Jogos/ softwares educativos

+infos(oficial): https://arterialab.uevora.pt/criativos/

Tags : , ,

sessão 4 – Gestão básica de threads em Win32

Bibliografia de apoio:
Capítulos 6, 7 e 8 do Livro Windows System Programming (da Bibliografia) (pags. 194-195, 223-232, 243-245,252-253, 279-281)

MSDN:
Acerca de threads e processos https://docs.microsoft.com/en-us/windows/win32/procthread/about-processes-and-threads
Gestão de threads https://docs.microsoft.com/en-us/windows/win32/procthread/multiple-threads
Criação de threads (exemplo) https://docs.microsoft.com/en-us/windows/win32/procthread/creating-threads
Funções básicas de espera (WaitforSingleObject / WaitForMultipleObjects) https://docs.microsoft.com/en-us/windows/win32/sync/wait-functions#single-object-waitfunctions
Mutexes https://docs.microsoft.com/en-us/windows/win32/sync/using-mutex-objects

Para criar uma thread:
sabe o que é necessário que seja executado em simultâneo para além da thread principal (e  sem interromper a thread principal)

CreateThread(
NULL, //segurança: descritores de segurança, só o utilizador é que pode interagir com a thread
0, //tamanho máximo da pilha, cria quantas variáveis locais que ela quiser criar (e só pertencem a ela)
FuncaoThread, //nome da função para correr em simultâneo  (podemos passar um parâmetro para esta função)
&total, //LPVOID parametro, ponteiro para void, uso referencia para poder obter o valor atualizado
0, //eu pretendo que a thread comece a correr, onde 0 é para começar a correr (por oposição de a deixar suspensa, com o resume: CREATE_SUSPENDED)
&threadId //opcional: é o threadID, é um DWORD ou colocar NULL se não quiser usar
);
//uma unica thread
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200

DWORD WINAPI FuncaoThread(LPVOID parametro)
{
	int* dados = (int*)parametro;
	for (int i = 0; i <= 100; i++)
	{
		(*dados) += i;
		if(i%15== 0)
		{
			Sleep(1000);
		}
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	DWORD t1;
	int total = 0;
	HANDLE hThread;
	
	hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) FuncaoThread, &total, 0, &t1);
	//sleep()
	//WaitForSingleObject()
	if(hThread != NULL)
	{
		WaitForSingleObject(hThread, INFINITE); //esperar até terminar ou dar um tempo
		_tprintf(TEXT("resultado da thread: %d"), total);
		//getchar();
		CloseHandle(hThread);
	}else
	{
		_tprintf(TEXT("nada de threads"));
	}
	return 0;
}
DWORD WaitForMultipleObjects(
  DWORD        nCount, //numero de handles do array
  const HANDLE *lpHandles, //array de handles
  BOOL         bWaitAll, // true se espero por todas
  DWORD        dwMilliseconds //indicar se espero pelo fim de todas
);
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200
#define MAX_THREADS 2

BOOL sairThread = FALSE;

typedef struct {
	int total, limite_inferior, limite_superior;
} dados_thread;

DWORD WINAPI FuncaoThread(LPVOID parametro)
{
	dados_thread * dados = (dados_thread *) parametro; //ponteiro para a estrutura
	for (int i = dados->limite_inferior; i <= dados->limite_superior; i++)
	{
		dados->total += i;
		if (i % 15 == 0)
		{
			Sleep(1000);
			if(sairThread)
			{
				break;
			}
		}
	}
	return 0;
}


DWORD WINAPI comandos(LPVOID parametro)
{
	TCHAR comando[TAM];
	while(!sairThread)
	{
		_tprintf(TEXT("Escrever 'sair' para terminar.. \n"));
		wscanf_s(TEXT("%s"),comando, TAM-1);

		if(wcscmp(comando, TEXT("sair"))==0)
		{
			sairThread = TRUE;
		}
	}
	return 0;
}

int _tmain(int argc, TCHAR* argv[]) {
	DWORD t1, t2;
	dados_thread dado[MAX_THREADS];
	HANDLE hThreadArray[MAX_THREADS]; //handle threads

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	dado[0].limite_inferior = 0;
	dado[0].limite_superior = 100;
	dado[0].total = 0;

	dado[1].limite_inferior = 0;
	dado[1].limite_superior = 100;
	dado[1].total = 0;
	
	hThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncaoThread, &dado[0], 0, &t1);
	hThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FuncaoThread, &dado[1], 0, &t2);

	CreateThread(NULL, 0, comandos, NULL, 0, NULL);
	 
	if (hThreadArray[0] == NULL || hThreadArray[1] == NULL)
	{
		_tprintf(TEXT("nada de threads"));
		ExitProcess(3);
	}
	WaitForMultipleObjects(2, hThreadArray, TRUE, INFINITE);

	_tprintf(TEXT("resultado da thread[%d]: %d\n"), t1, dado[0].total);
	_tprintf(TEXT("resultado da thread[%d]: %d\n"), t2, dado[1].total);
	CloseHandle(hThreadArray[0]);
	CloseHandle(hThreadArray[1]);
	
	return 0;
}
Tags : , , ,

sessão 3 – DLL – Bibliotecas de ligação dinâmica

Bibliografia de apoio:
Material das aulas teóricas e incluindo documentos de apoio ao tópico de bibliotecas dinâmicas
Capítulo 5 do Livro Windows System Programming (da Bibliografia) (pags. 167 em diante)

MSDN:
Run-Time Dynamic Linking (Overview geral) https://docs.microsoft.com/en-us/windows/win32/dlls/run-time-dynamic-linking
Using Run-Time Dynamic Linking (Inclui exemplos) https://docs.microsoft.com/en-us/windows/win32/dlls/using-run-time-dynamic-linking
Referência do API para lidar com DLL (libloaderapi) https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/

Manipulação do registry: cria uma chave (se não existir), e abre a chave

#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200

int _tmain(int argc, TCHAR* argv[]) {
    HKEY chave;
    TCHAR nome[TAM] = TEXT("SOFTWARE\\SO2");
    TCHAR par_nome[TAM] = TEXT("1º autor");
    TCHAR par_valor[TAM] = TEXT("João Sá");
    DWORD o_que_aconteceu;
    DWORD n_vezes = 1;
    DWORD tam = sizeof(DWORD);

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif

    if (RegCreateKeyEx(HKEY_CURRENT_USER, nome, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &chave, &o_que_aconteceu) == ERROR_SUCCESS)
    {
        if (o_que_aconteceu == REG_CREATED_NEW_KEY)
        {
            _tprintf(TEXT("chave %s criada com sucesso !"), nome);
            RegSetValueEx(chave, par_nome, 0, REG_SZ, (LPBYTE)par_valor, (_tcslen(par_valor) + 1) * sizeof(DWORD));
            RegSetValueEx(chave, TEXT("Número de execuções"), 0, REG_DWORD, (LPBYTE)&n_vezes, sizeof(DWORD));
        }
        else
        {
            _tprintf(TEXT("chave %s aberta com sucesso\n"), nome);
            RegQueryValueEx(chave, TEXT("Número de execuções"), 0, NULL, (LPBYTE)&n_vezes, &tam);
            n_vezes++;
            _tprintf(TEXT("Esta é a %d vez que o programa é executado"), n_vezes);
            RegSetValueEx(chave, TEXT("Número de execuções"), 0, REG_DWORD, (LPBYTE)&n_vezes, sizeof(DWORD));
        }
        return -1;
    }
    else
    {
        _tprintf(TEXT("Erro ao abrir/criar chave %s"), nome);
    }
    RegCloseKey(chave);
    return 0;
}

Referência do API para lidar com DLL: LINK
Fazer a leitura pelo modo implícito na arquitetura x86

//base.c
#include "principal.h"
#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

int _tmain(int argc, TCHAR* argv[])
{
    double local;
	
#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif
    //usar a variavel DLL
	do
	{
		_tprintf(TEXT("Valor atual para a variavel global da DLL:%lf\nNovo valor:"), factor);
		_tscanf_s(TEXT("%lf"), &factor);
		_tprintf(TEXT("Valor atual para a variavel global da DLL:%lf\n"), factor);
		_tprintf(TEXT("Valor a passar como argumento à função da DLL:"));
		_tscanf_s(TEXT("%lf"), &local);
		_tprintf(TEXT("Resultado da função da DLL:%lf\n"), applyFactor(local));
	} while (factor != -1);

    return 0;
}

 

//base.h
#pragma once

#include <windows.h>

#ifdef SO2F3DLL_EXPORTS
#define DLL_IMP_API __declspec(dllexport)
#else
#define DLL_IMP_API __declspec(dllimport)
#endif

DLL_IMP_API double factor;
//variavel global, serve para cada processo
//cada processo acede à sua

DLL_IMP_API double applyFactor(double v); //funcao

carregar para o projeto o ficheiro .LIB, e colocar o ficheiro DLL na pasta do executável

Fazer a leitura pelo modo explicito
_mais independente
_mais controlo ao programador (pode ser usada apenas na altura em que preciso: da variável, da função, .. que está na DLL)
_só preciso quando for executar o programa
_não vai ser necessário o ficheiro h com a “estrutura” que consta no DLL

#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 256

int obterValorUtilizador()
{
	int valor = 0;
	TCHAR buffer[TAM] = _TEXT("");
	//obter o nome da chave
	_tprintf(_TEXT("introduza um valor: "));
	fflush(stdin);
	_fgetts(buffer, TAM, stdin);
	//retirar o \n
	buffer[_tcsclen(buffer) - 1] = '\0';
	valor = _tstoi(buffer);
	return valor;
}



int _tmain(int argc, TCHAR* argv[])
{
	int a, b;
	int sair = 0;

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
	_setmode(_fileno(stderr), _O_WTEXT);
#endif

	_tprintf(TEXT("Valor inicial da variavel da DLL \n \n"));
	double (*ptrVar)(double) = NULL;
	double (*ptrFunc)(double) = NULL;
	HINSTANCE hLib = LoadLibrary(TEXT("so2x86.dll"));

	if (hLib != NULL)
	{
		ptrFunc = (double(*)(double))GetProcAddress(hLib, "applyFactor");
		ptrVar = (double(*)(double))GetProcAddress(hLib, "factor");
		_tprintf(TEXT("parte2\n \n"));
		if (ptrVar != NULL &amp;amp;&amp;amp; ptrFunc != NULL)
		{
			_tprintf(TEXT("parte3\n \n"));
			_tprintf(_TEXT("Valor inicial da variavel da DLL: %f\n"), *((double*)ptrVar));
			do
			{
				a = 0;
				b = 0;
				_tprintf(_TEXT("vairavel (a)\n"));
				a = obterValorUtilizador();
				if (a == 1)
				{
					sair = 1;
					break;
				}
				*((double*)ptrVar) = (double)a;
				_tprintf(_TEXT("Valor novo da variavel da DLL: %f\n"), *((double*)ptrVar));

				b = obterValorUtilizador();
				_tprintf(_TEXT("Valor novo da variavel da DLL: %f\n"), ptrFunc(b));
			} while (sair != 1);
		}
		if (hLib != NULL)
		{
			FreeLibrary(hLib);
		}
	}
	return 0;
}

Criar uma DLL
Mudar o projeto para concretizar uma DLL e não um ficheiro executavel (nas propriedades do projeto)

O visual studio sugere uma variável de nome _WINDLL

//source.c
#include <tchar.h>
#include <stdlib.h>
//#define SO2F3DLL_EXPORTS
//vou usar a constante sugerida pelo VS


#include <time.h>

#include "Header.h" //deve ser feito depois do define

double varGlobal = 4;
int varInternaDLL = 5;

double aplicarFactor(double argumento)
{
	_tprintf(_TEXT("esta é a minha DLL"));
	return varGlobal;
}

double geraAleatorio(int valor_min, int valor_max)
{
	static int n_vezes = 1;
	if(n_vezes == 1)
	{
		srand(time(NULL));
		n_vezes++;
	}
	return rand() % (valor_max - valor_min) + valor_min;
}
#pragma once
//header.h
//diretivas para o importar e para o exportar
//SO2F3DLL_EXPORTS é uma constante que fou ter que partilhar e vai para o .c
//#ifdef SO2F3DLL_EXPORTS
#ifdef _WINDLL
#define DLL_IMP_API __declspec(dllexport)
#else
#define DLL_IMP_API __declspec(dllimport)
#endif

//porque quero que outros usem, e tem que ser exportado
DLL_IMP_API double varGlobal; //não vai ser exportada

int varInternaDLL;

double geraAleatorio(int valor_min, int valor_max);

DLL_IMP_API double aplicarFactor(double argumento);

e fica criada uma DLL

 

Tags : , , ,

sessão 2 – Criar, consultar, alterar e remover chaves do Registry.

Bibliografia de apoio:
Capítulo 3 do Livro Windows System Programming (da Bibliografia) (pags. 86-97)
MSDN:
Registry Functions: https://docs.microsoft.com/en-us/windows/win32/sysinfo/registry-functions
32-bit and 64-bit Application Data in the Registry: https://docs.microsoft.com/en-us/windows/win32/sysinfo/32-bit-and-64-bit-application-data-in-the-registry?redirectedfrom=MSDN

Criação de processo e espera até terminar:

TCHAR comando[TAM] = TEXT("notepad-exe ficheiro.txt");
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);

if (CreateProcess(NULL, comando, NULL, NULL, 0, 0, NULL, &si, &pi)
{
	_tprintf(TEXT("processo com PID: %d foi lançado"), pi.dwProcessId);
	WaitForSingleObject(pi.hProcess, INFINITE);
}

O editor do registry: registry editor (regedit)
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE (só admin, todos os programas que são instalados)

Para criar fica assim:

#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200

int _tmain(int argc, TCHAR* argv[]) {
    HKEY chave; //handle para a chave depois de ser aberta/criada
	//caminho e nome da chave é criada SOFTWARE\\SO2\\chaveLab, uso da contrabarra "\\"
    TCHAR chave_nome[TAM] = TEXT("SOFTWARE\\SO2\\chaveLab"), par_nome[TAM], par_valor[TAM];
    DWORD resultado; //com o que aconteceu com a chave

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif

	//para criar ou abrir uma chave do registry
    //RegCreateKeyEx retorna um valor se a chave foi criada/aberta com sucesso
    if(RegCreateKeyEx(
        //HKEY                        hKey,
        HKEY_CURRENT_USER,
        //nome da chave
        //LPCTSTR                     lpSubKey,
        chave_nome,
        //DWORD                       Reserved,
        0,
        //LPTSTR                      lpClass,
        NULL,
        //DWORD                       dwOptions, longevidade da chave
        REG_OPTION_NON_VOLATILE,
        //REGSAM                      samDesired,
        KEY_ALL_ACCESS,
        //const LPSECURITY_ATTRIBUTES lpSecurityAttributes, segurança da chave
        NULL,
       // PHKEY                       phkResult,
        &chave,
        //LPDWORD                     lpdwDisposition
        &resultado
    ) != ERROR_SUCCESS)
    {
        _tprintf(TEXT("chave não foi criada nem aberta! ERRO!"));
        return -1;
    }
	
    if(resultado == REG_CREATED_NEW_KEY)
    {
        _tprintf(TEXT("a chave foi criada: %s"), chave_nome);
    }else
    {
        _tprintf(TEXT("a chave foi criada: %s"), chave_nome);
    }
    RegCloseKey(chave); //importante: fechar sempre o Handle da chave 
    return 0;
}


Os atributos de uma chave são: nome, tipo, e valor

#include <windows.h>
#include <tchar.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>

#define TAM 200

int _tmain(int argc, TCHAR* argv[]) {
    HKEY chave; //handle para a chave depois de ser aberta/criada
	//caminho e nome da chave é criada SOFTWARE\\SO2\\chaveLab, uso da contrabarra "\\"
    TCHAR chave_nome[TAM] = TEXT("SOFTWARE\\SO2\\chaveLab");
    TCHAR par_nome[TAM] = TEXT("1º atributo");
	TCHAR par_valor[TAM] = TEXT("É o novo valor");
    DWORD resultado; //com o que aconteceu com a chave

#ifdef UNICODE 
    _setmode(_fileno(stdin), _O_WTEXT);
    _setmode(_fileno(stdout), _O_WTEXT);
    _setmode(_fileno(stderr), _O_WTEXT);
#endif

	//para criar ou abrir uma chave do registry
    //RegCreateKeyEx retorna um valor se a chave foi criada/aberta com sucesso
    if(RegCreateKeyEx(
        //HKEY                        hKey,
        HKEY_CURRENT_USER,
        //nome da chave
        //LPCTSTR                     lpSubKey,
        chave_nome,
        //DWORD                       Reserved,
        0,
        //LPTSTR                      lpClass,
        NULL,
        //DWORD                       dwOptions, longevidade da chave
        REG_OPTION_NON_VOLATILE,
        //REGSAM                      samDesired,
        KEY_ALL_ACCESS,
        //const LPSECURITY_ATTRIBUTES lpSecurityAttributes, segurança da chave
        NULL,
       // PHKEY                       phkResult,
        &chave,
        //LPDWORD                     lpdwDisposition
        &resultado
    ) != ERROR_SUCCESS)
    {
        _tprintf(TEXT("chave não foi criada nem aberta! ERRO!"));
        return -1;
    }
	
    if(resultado == REG_CREATED_NEW_KEY)
    {
        _tprintf(TEXT("a chave foi criada: %s"), chave_nome);
    }else
    {
        _tprintf(TEXT("a chave foi aberta: %s"), chave_nome);
    }
    //criar oar nomeatributo = valor (1 por cada chamada da função)
    if(RegSetValueEx(
        //HKEY       hKey,
        chave,
        //LPCSTR     lpValueName,
        par_nome,
        //DWORD      Reserved,
        0,
        //DWORD      dwType, //tipo: string (REG_SZ), inteiro (REG_WORD), bloco de bytes (REG_BINARY)
        REG_SZ,
        //const BYTE * lpData, //ponteiro para void, dá para tudo como o malloc
        (const LPCBYTE /*igual c const BYTE **/) par_valor,
        //DWORD      cbData
        sizeof(TCHAR) * (_tcsclen(par_valor)+1)
        //+1 para o /0, para ler o /0 
    ) != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Atributo %s não foi alterado nem criado ERRO\n"), par_nome);
    }
    //concatenar qq tipos de dados e guardar numa string
	//_strpintf_s (par_valor, TAM, TEXT("%s %d"), par_nome, tamanho);

	
	//consultar valor de um atributo
    //strcpy(par_nome, TEXT("2º atributo ")); //-> caso fosse necessário escrever a string em c depois de criada
    par_valor[0] = '\0';
	DWORD tamanho = sizeof(par_valor);
	if(RegQueryValueEx(
       // HKEY     hKey,
       chave,
       // LPCTSTR  lpValueName,
       par_nome,
        //LPDWORD  lpReserved, //é para consulta fica a NULL
        0,
        //LPDWORD  lpType,
        NULL,
        //LPBYTE   lpData,
        (LPCBYTE)par_valor,
        //LPDWORD  lpcbData
        &tamanho
    ) != ERROR_SUCCESS)
	{
        _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome);
	}else
	{
        _tprintf(TEXT("Atributo encontrado com o valor: %s e tamanho %d"), par_valor, tamanho);
	}

    DWORD valor_inteiro = 1022;
    valor_inteiro *= 2;
    _tcscpy_s(par_nome, TAM, TEXT("atributo inteiro"));
    if (RegSetValueEx(
        chave,
        par_nome,
        0,
        REG_DWORD,
        (LPCBYTE) &valor_inteiro,
        sizeof(DWORD)
    ) != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome);
    }

    _tcscpy_s(par_nome, TAM, TEXT("atributo binario"));

    if (RegSetValueEx(
        chave,
        par_nome,
        0,
        REG_BINARY,
        (LPCBYTE) &valor_inteiro,
        sizeof(DWORD)
    ) != ERROR_SUCCESS)
    {
        _tprintf(TEXT("Atributo %s não foi encotnrado!! ERRO\n"), par_nome);
    }
    RegCloseKey(chave); //importante: fechar sempre o Handle da chave 
    return 0;
}

Tags : , , ,

sessão 1 – Trabalhar com caracteres UNICODE na consola, Criar processos utilizando a Windows API e esperar pelo seu término.

Bibliografia de apoio:
Capítulos 2 e 6 do Livro Windows System Programming (da Bibliografia) (pgs. 34-37, 181-187, 192-195)
MSDN: Strings & Unicode e Processes

Como começar:

#include <stdio.h>

int main()
{
	printf("Até amanhã\n");
	getchar();
	return 0;
}

ou Use Unicdoe Character Set
ou Use Multi-Byte Character Set

apesar de estar em Use Unicdoe Character Set não é suficiente para escrever os caracteres especiais, vai ser necessário

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <io.h>

#define MAX 256

int _tmain(int argc, TCHAR* argv[]) {
	//argc -> numero de argumentos
	//argv -> argumentos
	////TCHAR vou aceitar argumentos com caracteres especiais

	TCHAR result[MAX] = TEXT("Olá! Este programa ainda não representa UNICODE\n");

#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT); //#include <fcntl.h> #include <io.h>
	_setmode(_fileno(stdout), _O_WTEXT); //#include <fcntl.h> #include <io.h>
	_setmode(_fileno(stderr), _O_WTEXT); //#include <fcntl.h> #include <io.h>
#endif


	_tprintf(TEXT("Frase:%s Tamanho:%d (caractares) %d (bytes)\n"), result, _tcslen(result), _tcslen(result) * sizeof(TCHAR));

	//mas se fossem funções que não trabalhem com caracteres não é necessário
	srand(time(NULL));
	int aleatorio = rand();

	_gettchar();

	return 0;
}
//manipulação de strings:
//char -> 1 bytes (caracteres que estão na tabela ASCII)
//a alternativa é usar o -> wchar (tem o dobro da capacidade, 2 bytes) e já permite armazenar caracteres UNICODE
//ao invés de ter duas versões do progrma, apenas uso uma versão do código
//TCHAR, não é um tipo nativo
//#include <windows.h>
//#include <tchar.h>
//e desta forma não vou ter que fazer duas versões do programa: para sistemas mais antigos e mais modernos
//_tmain -> main ou wamin
//uma string fixa, cada letra ocupa 2 bytes -> L"Olá! Este programa ainda não representa UNICODE\n"
//ou uso a macro TEXT("") ou _T("") -> "" para resolver as strings fixas
//e vou usar também o _tprintf -> printf ou wprintf
//_tcslen -> strlen
//_gettchar -> getchar()
//preparar a consola para mostrar caracteres wide:
//  _setmode(stdin, constante);
// _setmode(stdout, constante);
//calcular o tamanho real:
// _tcslen(result)*sizeof(TCHAR) // não interessa se está ou não em unicode
#include <windows.h>
#include <tchar.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#define MAX 256

//LPTSTR -> TCHAR *
//LPCTSTR -> const THACAR *


int _tmain(int argc, LPTSTR argv[]) {
	TCHAR str[MAX], result[MAX] = TEXT("Olá! Este programa é para aceitar UNICODE. Insira \'fim\' para sair\n"); 
		unsigned int i;
	//UNICODE: Por defeito, a consola Windows não processa caracteres wide. 
	//A maneira mais fácil para ter esta funcionalidade é chamar _setmode:
#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
#endif
	do {
		_tprintf(result);
		fflush(stdin);
		_fgetts(str, MAX, stdin); //fgets msdn -> _fgetts
		//Retirar \n 
		str[_tcslen(str) - 1] = '\0';
		//Maiúsculas 
		for (i = 0; i < _tcslen(str); i++)
			str[i] = _totupper(str[i]);

		_stprintf_s(result, MAX, TEXT("Frase:%s, Tamanho:%d\n"), str, _tcslen(str));
	} while (_tcsicmp(TEXT("FIM"), str));
	return 0;
}
//setlocale -> para lidar com os caracteres especiais em maiúsculas 

Os processos em Windows (child processes) CreateProcess (em linux era fork)

#include <windows.h>
#include <tchar.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#define MAX 256

int _tmain(int argc, LPTSTR argv[]) {
	TCHAR str[MAX], result[MAX] = TEXT("Olá! Este programa é para aceitar UNICODE. Insira \'fim\' para sair\n");
	unsigned int i;
#ifdef UNICODE 
	_setmode(_fileno(stdin), _O_WTEXT);
	_setmode(_fileno(stdout), _O_WTEXT);
#endif
	do {
		_tprintf(result);
		fflush(stdin);
		_fgetts(str, MAX, stdin); //fgets msdn -> _fgetts
		//Retirar \n 
		str[_tcslen(str) - 1] = '\0';
		//Maiúsculas 
		for (i = 0; i < _tcslen(str); i++)
			str[i] = _totupper(str[i]);

		_stprintf_s(result, MAX, TEXT("Frase:%s, Tamanho:%d\n"), str, _tcslen(str));
	} while (_tcsicmp(TEXT("FIM"), str));

	TCHAR texto[256] = TEXT("notepad");

	STARTUPINFO si; //
	PROCESS_INFORMATION pi; //

	//memset();
	ZeroMemory(&si, sizeof(si)); // mesmo que o memset mas com tudo a zero
	ZeroMemory(&pi, sizeof(pi)); // resultado da função: pid do processo, pid da threat principal do processo,
	//e handle (ponteiro para estrutura) do processo,  e handle para a thread principal
	//pid unico no sistema para cada processo, o handle podem ser vários, cada um com determinada permissão por exemplo
	
	si.cb = sizeof(STARTUPINFO); //tamanho da estrutura tem que estar preenchido, pelo menos esse!! .cb
		
	//criar um processo -> correr um programa que já foi compilado
	if (CreateProcess(NULL,   // No module name (use command line): se fosse o proprio -> argv[0] ou GetModuleFileName
		//argv[1],						       // Command line
		texto,
		NULL,				 // Process handle not inheritable
		NULL,					// Thread handle not inheritable
		FALSE,					// Set handle inheritance to FALSE
		0,						// No creation flags
		NULL,           // Use parent's environment block
		NULL,           // Use parent's starting directory 
		&si,            // Pointer to STARTUPINFO structure
		&pi)           // Pointer to PROCESS_INFORMATION structure
		)
	{
		_tprintf(TEXT("processo foi lançado PID: %d THREAD: %d"), pi.dwProcessId, pi.dwThreadId );
	}else
	{
		_tprintf("CreateProcess failed (%d).\n", GetLastError());
		return;
	}

	//esperar pelo processo terminar em linux: wait
	WaitForSingleObject(pi.hProcess, INFINITE); //ms >0 ou então quando terminar INFINITE
	//se for 0 só para consulta
	
	return 0;
}
Tags : , , ,