Apontamentos teóricos

::Slides05 – SO2 – 2022 – BibliotecasWindows v18.pdf
O ficheiro .lib (opcional). Usado para os casos de ligação implícita
O ficheiro .DEF (opcional). Método alternativo para identificar as funções em ligação explícita (identificação por número de ordem em vez de pelo nome)

O método mais habitual é o de identificar as funções pelo nome. A identificação posterior por parte dos programas que usam a DLL é feita então de duas formas: ligação implícita e ligação explícita
Em ambos os casos a DLL tem que estar sempre presente durante a execução

Ligação implícita
É usado um ficheiro auxiliar .lib = biblioteca de ligação estática para estabelecer a ponte entre o programa e as funções na DLL
O programa que usa as funções apenas precisa do .h e do .lib durante a compilação e usa as funções quase como se elas fizessem parte do seu código.
Ligação explícita
Não é necessário nenhum .lib, e, eventualmente, nem sequer um .h durante a compilação
O programa que usa a DLL tem que carregar explicitamente a DLL e depois procurar as funções pelo nome, invocando-as por ponteiro.

Código da DLL
Uma DLL é uma variante de um ficheiro executável.
Não se destina a executar por si só.
Age como um repositório de funções e recursos para ser usado por outros programas

Exemplo:
um função na DLL
#include
// Função “local“ -> não vai ser exportada
int factorial (int n) { // calcula factorial de n
int res = n;
n–;
while (n>1) {
res = res * n;
n–;
}
return res;
}

// Variável exportada
__declspec(dllexport) int nExemplo = 0;

// Função exportada
__declspec(dllexport) int fnExemplo(int n) {
if (n<0) n = -1; return factorial(n); } Resultado da compilação exemplo.dll → Código da DLL exemplo.lib → Ficheiro auxiliar para ligação implícita Ligação implícita Forma de uso da DLL que oculta o facto de se estarem a usar recursos externos ao programa. As funções aparentam estar no próprio programa e o seu uso é muito simplificado. A carga da DLL, procura de funções e libertação da DLL são feitas automaticamente por código disponibilizado numa pequena biblioteca de ligação estática (o .lib) que faz a ponte entre o programa e a DLL. O .lib contém funções com o mesmo nome que as existem no .dll, reencaminhando as chamadas para lá A DLL é carregada automaticamente no início da execução e libertada no fim. Se não estiver presente, o programa não corre de todo. A DLL mantem-se sempre em memória e utilizando assim mais memória Nesta forma, os recursos da DLL aparentam estar no programa tal como se de uma biblioteca de ligação estática se tratasse. Na ligação implícita, se a DLL não estiver presente, todo o programa é impedido de correr. Na ligação implícita, o compilador consegue efectuar algumas validações no uso das funções (pela existência do ficheiro .h). Se existirem muitas DLL com ligação implícita, o processo tem um início demorado pois todas essas DLL são carregadas no início. A ligação implícita requer a biblioteca estática para fazer a ponte com a DLL. Na ligação implícita as DLL mantêm-se mapeadas durante a execução do processo. Ligação explícita A DLL é tratada como um conjunto de recursos que é trazida para memória e usada quando é necessário de uma forma explícita e totalmente controlada pelo programador do programa “cliente” da DLL. O programador decide quando é que precisa de carregar a DLL e carrega-a explicitamente e apenas nessa altura O programador obtém acesso aos recursos da DLL procurando explicitamente as funções que precisa e invoca-as através de um ponteiro O programador liberta a DLL quando já não precisa mais dela, libertando recursos ao processo e ao sistema Nesta forma torna-se óbvio ao programador que está a usar recursos externos ao seu programa faz uso da API WIN32: LoadLibrary / LoadLibraryEx GetProcAddress //aceder a uma função/variavel exportada FreeLibrary //liberta o módulo do espaço do processo Se a DLL não estiver presente, ou se houver algum erro que impeça a sua utilização, o programa não tem que terminar. Apenas a funcionalidade associada à DLL fica inviabilizada Na ligação explícita as DLL são carregadas em qualquer altura. A ligação explícita permite carregar as DLL apenas quando necessário e libertá-las assim que não são necessárias. Isto permite poupar recursos ao processo e sistema ::Slides06 – SO2 – 2022 – Threads_v16.pdf Cada processo tem pelo menos uma thread (thread inicial ou principal) A partir de uma thread pode-se lançar outra thread O facto da thread inicial ser a primeira (ou ser chamada de principal) não lhe confere nenhum privilégio em particular sobre outras threads que venham a existir no processo A criação da thread está associada a uma função. Quando essa função terminar, a thread também termina. API32 threads: CreateThread //Cria uma thread nova ExitThread //Termina a thread GetExitCodeThread //Obtém o código de terminação Terminação de um thread (a partir de outra) Usa-se uma variável de condição acessível às threads envolvidas Para simplicidade vai-se assumir que se trata de uma variável global (mas não precisa de ser assim –> ponteiro passado por parâmetro)

Privilégios de acesso:
SYNCHRONIZE, Permite usar o handle em funções de sincronização
THREAD_ALL_ACCESS, Todos os acessos suportados pelo sistema
THREAD_SUSPEND_RESUME, Permite suspender/des-suspender a thread através do handle obtido

::Slides07 – SO2 – 2022 – Sincronizacao v17.pdf
A sincronização em Win32, aplica-se a:
Serialização de acções (coordenação / cooperação)
Competição por acesso a um recurso
Acesso exclusivo a um recurso (exclusão mútua / secções críticas)

é feita através de espera e sinalização em objectos (através dos seus handles):
O handle de um objecto pode ser usado numa função wait
Os objectos podem estar apenas nos estados assinalado / nãoassinalado

Objectos que podem ser usados em sincronização:
Evento (objectos-evento – não confundir com as mensagens WM_….)
Mutexes
Critical Sections
Semáforos
Waitable Timers

Outros objectos que podem ser usados em sincronização:
Processos
Threads
Input de consola
Notificações de alteração

Objectos de Sincronização no API do Windows:
Semáforos
Ferramenta de sincronização quase universal.
Apropriado a quase todos os casos.
Resolve situações de exlusão mútua, competição e coordenação
Em algumas situações não é o mecanismo mais simples ou mais directo

Mutexes
Resolvem situações de exclusão mútua entre threads e entre processos.
Melhor mecanismo para situações de exclusão mútua que envolvam
processos diferentes.

Critical Section
Versão simplificada de mutexes para uso local a um único processo (com várias threads) em situações de exclusão mútua
O API destes objectos foge à forma habitual usada nos restantes

Waitable Timers
Mecanismos apropriados a acontecimentos relacionados com a passagem de intervalos de tempo

Eventos
Objecto de uso não-específico – o significado e forma de utilização depende muito da lógica da aplicação.
Não são apropriados a situações de exclusão mútua. São mais indicados para cenários de cooperação ou coordenação.

Funções de espera:
WaitForSingleObject / WaitForSingleObjectEx
Espera que um determinado objecto esteja assinalado ou que um determinado timeout se esgote (ou que seja recebido uma notificação de I/O completo)

WaitForMultipleObjects / WaitForMultipleObjectEx
Espera por um conjunto de objectos de uma só vez

WaitForSingleObject retorna:
WAIT_ABANDONED // A thread que detinha o objecto terminou sem o libertar
WAIT_OBJECT_0 //O objecto de sincronização foi libertado
WAIT_TIMEOUT //Ocorreu o tempo máximo de espera

Obtenção dos handles para espera nas funções

Na thread que cria os objectos de sincronização:
CreateEvent //(para eventos)
CreateMutex //(para mutexes)
CreateSemaphore // (para semáforos)

Em outras threads:
OpenEvent //(para eventos)
OpenMutex //(para mutexes)
OpenSemaphore //(para semáforos)

Flags de acesso nas funções open:
Semáforos: SEMAPHORE_MODIFY_STATE (SEMAPHORE_ALL_ACCESS)
Eventos: EVENT_MODIFY_STATE (EVENT_ALL_ACCESS)
Mutexes: MUTEX_MODIFY_STATE (MUTEX_ALL_ACCESS)
Timers: TIMER_MODIFY_STATE (TIMER_ALL_ACCESS)

Sincronização com objectos processo e threads:
A função CreateProcess cria um objecto que representa o novo processo.
Inicialmente o objecto está no estado não-assinalado
O estado passa a assinalado quando o processo termina
Idem para threads

Funções mais úteis para este tipo de objecto de sincronização:
WaitForInputIdle //Espera que o processo esteja bloqueado à espera de input
WaitForSingleObject / WaitForSingleObjectEx // Espera que um processo esteja assinalado (ou seja, tenha terminado)

Sincronização com eventos:
Servem para uma thread indicar que “algo” aconteceu a uma ou mais threads que aguardavam por esse algo.

Um objecto evento é um objecto de sincronização cujo estado pode ser assinalado explicitamente com a função SetEvent

os eventos podem ser usados:
Reset manual (“passam todos até alguém fechar”)
Auto-reset (“só passa o primeiro”)

funções:
HANDLE CreateEvent(..)
HANDLE OpenEvent(..)
BOOL SetEvent(..) //fica assinalado
BOOL ResetEvent(..) //fica não assinalado

Sincronização com mutexes:
Permitem a duas ou mais threads (ou processos) aguardarem por acesso a uma zona de código (”seção crítica”) que manipula um recurso (ex., dados)
Um objecto mutex permite resolver directamente as situações de exclusão mútua
Encontra-se no estado assinalado quando nenhuma thread o possui
Assim que uma thread obtém a sua posse (através das funções de espera), o estado do mutex passa a não-assinalado
Uma thread que tenha a posse de um mutex pode libertá-lo através da função ReleaseMutex. O mutex em questão passa ao estado assinalado

HANDLE CreateMutex(..) //cria ou obtem acesso a um mutex com ou sem nome (NULL), sem nome só ficam restringidos a threads do mesmo processo.
HANDLE OpenMutex(..) //acesso ao mutex
BOOL ReleaseMutex(..) // libterta a possse do mutex

Sincronização com Critical Sections:
podem ser partilhados por processos diferentes. Servem apenas para sincronização de threads dentro do mesmo processo.

Sincronização com semáforos:
Generalização do conceito de mutex – Permite acesso a mais do que um processo/thread em simultâneo
é mantida a contabilização de operações esperar/assinalar que sobre ele são efectuadas
Podem ter nome o que permite que sejam usados por processos diferentes
as funções de espera efectuam a operação de espera sobre o semáforo, decrementando o seu contador interno. Se o contador atinge o valor zero, o estado do semáforo passa a não-assinalado e a thread que que efectuou a espera fica bloqueada
A função ReleaseSemaphore permite efectuar a operação assinalar num semáforo. Uma das thread que estavam bloqueadas à espera nesse semáforo é desbloqueada

funções:
HANDLE CreateSemaphore(..) // cria ou obtem acesso a um semáforo com ou sem nome (NULL)
HANDLE OpenSemaphore(..) //obtem um handle para uma semáforo que existe
BOOL ReleaseSemaphore(..) // Incrementa o contador interno do semáforo na especificada no parâmetro lReleaseCount

::Slides08 – SO2 – 2022 – FicheirosMapeados v16.pdf
Ficheiros mapeados em memória – Memória partilhada
O mecanismo de ficheiros mapeados permite duas funcionalidades:
Mapear (parte de) um ficheiro em memória, operando sobre ele como se se tratasse de uma matriz de bytes
Partilhar um bloco de memória entre processos para situações de comunicação entre processos

Restrições ao uso de ficheiros mapeados:
Manipulação do conteúdo da memória partilhada quando usada para comunicar entre processos (uso em simultâneo em vários processos):
Apenas se pode colocar informação que faça sentido em todos os processos envolvidos.
estão excluídas coisas que só façam sentido no processos que as criou, tais como handles e ponteiros

Uso em simultâneo (concorrente) da mesma memória partilhada por vários processos/threads
Trata-se de um caso típico de acesso concorrente a dados partilhados
Normalmente exigirá o uso de mutexes/semáforos para garantir que duas threads/processos não destroem o trabalho uma da outra e para garantir que o conteúdo dos dados permanece coerente

Uso de ficheiros mapeados:
1 – Obtém handle para o ficheiro
CreateFile //(indica-se o ficheiro)
2 – Criar o objecto FileMapping usando o handle anterior (handle do ficheiro)
CreateFileMapping // (indica-se o handle e o tamanho)
3 – Mapear uma vista do ficheiro no seu espaço de endereçamento
MapViewOfFile //(indica-se a zona pretendida e obtém-se um ponteiro)
4 – Usa a memória partilhada através da vista (sintaxe habitual ponteiros * -> [ ] )
5 – Desmapeia a vista
UnmapViewOfFile //(indica-se o ponteiro)
6 – Fecha o handle do objeto Ficheiro Mapeado
CloseHandle //(handle do filemapping)
7 – Fecha o handle do ficheiro
CloseHandle //(handle do ficheiro)

assim, m processo, inicialmente:
1 – Cria um objecto memória partilhada, obtendo um handle
CreateFileMapping
2 – Mapeia uma vista da memória partilhada no seu espaço de endereçamento
MapViewOfFile
3 – Usa a memória partilhada através da vista (ponteiros ou [ ] )
4 – Desmapeia a vista, eventualmente no final do processo
UnmapViewOfFile
4 – Fecha o handle
CloseHandle

e outro/restantes processos:
1 – Obtém handle para o objecto memória partilhada
OpenFileMapping
2 – Mapeia uma vista da memória partilhada no seu espaço de
endereçamento
MapViewOfFile
3 – Usa a memória partilhada através da vista (ponteiros ou [ ] )
4 – Desmapeia a vista, eventualmente no final do processo
UnmapViewOfFile
5 – Fecha o handle
CloseHandle

funções:
HANDLE WINAPI CreateFileMapping (..) //Esta função é usada para criar um objecto de memória partilhada.
HANDLE WINAPI OpenFileMapping(..) // Esta função é usada para obter acesso ao objecto de memória partilhada (dado o seu nome) criado (normalmente) por outro processo/thread.
LPVOID WINAPI MapViewOfFile (..) //esta função mapeia uma porção da memória partilhada, devolvendo o ponteiro para onde ficou mapeada a vista
LPVOID WINAPI UnmapViewOfFile(..) //Esta função desmapeia uma vista do espaço de endereçamento do processo que a invoca –> o processo deixa de “ver” essa zona de memória partilhada

::Slides09 – SO2 – 2022 – Sincronizacao – Prod-Cons v12.pdf
Produtor/Consumidor – Resolução com recurso a semáforos

Sincronização:
1. Sistemas compostos por mais do que uma entidade activa (processos, threads) que usam recursos e dados partilhados entre si exigem a coordenação do acesso a recursos e dados
2. Sistemas compostos por mais do que uma entidade activa em que uma das entidades é dependente de acontecimentos originados por outra(s) vai exigir mecanismos para coordenar a sua execução em função da execução das outras entidades

Exemplos concretos da vida real
Sistemas cliente servidor envolvendo vários utilizadores ou postos; programas multi-threaded

Situações típicas (exemplos) que envolvem sincronização
Cooperação
Diversas actividades concorrem para a conclusão de uma aplicação comum
Situação que pode ser resolvida algoritmicamente para as aplicações envolvidas com recurso a mecanismos do sistema de suspensão e sinalização
Os processos querem sincronizar as suas acções de forma explícita uns com os outros. Em vez de competição e autorização para avançar, podem voluntariamente auto-suspenderem-se até receberem uma notificação

Competição
Diversos processos competem pela obtenção de um recurso limitado
A competição deve ser resolvida de forma a que o recurso seja utilizado de forma coerente
É complicado e/ou ineficiente resolver esta situação sem ajuda do sistema operativo
Pode ser visto como uma generalização da exclusão mútua: A competição é feita sobre recursos com mais do que uma unidade.
A sincronização é concretizada pelo SO para manter a coerência

Exclusão mútua
A utilização concorrente de uma zona de dados (ou recurso) partilhada pode levar a que os dados fiquem inconsistentes
A utilização desses dados (ou recurso) deve ser feita de uma forma exclusiva: apenas uma entidade activa utiliza o recurso.
A execução de uma secção de código que manipula dados partilhados constitui uma situação típica de acesso em exclusão mútua
Este caso é muito frequente e muito importante. Está também na base da tomada de decisões críticas que podem ter um resultado incorrecto se não for devidamente acautelado

Semáforo:
Constituído por uma estrutura de dados que inclui
Variável de controlo (inteira) = número de autorizações restantes
Fila de espera (de processos / threads bloqueados)
o semáforo bloqueia o processo (thread) e não ocupa o processador
Elimina a espera activa

tipos de semáforos:
Semáforo binário ou MUTEX, Tem apenas uma “autorização” – serve essencialmente para resolver situações de exclusão mútua
Semáforo, Genérico. Permite n “autorizações”, Usado para qualquer tipo de situação (inclusive exclusão mútua). Uso comum em situações de competição e controlo de recursos (por exemplo, cenário produtor/consumidor)

As operações fundamentais sobre semáforos são
Esperar – Requisita o recurso / Requisita uma autorização / (pede acesso a uma secção crítica)
Assinalar – Liberta o recurso / Devolve uma autorização / (liberta a secção crítica)

Método
Cada semáforo tem um contador interno. As operações sobre o semáforo são as de esperar (diminuir o contador) e assinalar (aumentar o contador). Esse contador actua como número de processos que ainda podem passar sem ficarem bloqueados.
Distinguir entre semáforos para exclusão mútua e semáforos para gestão de recursos

Lógica de funcionamento interno de um semáforo
Esperar(Semáforo) //Decrementa a variável de controlo
Assinalar(Semáforo) //Incrementa a variável de controlo

exemplo:
optimizado
Semáforo sem = CriarSemaforo(1);
/* … */
int escreve(tipo_dados valor) {
esperar(sem);
var_partilhada=valor;
assinalar(sem);
}

Produtor/Consumidor – versão com buffer circular, exemplo:
#define DIM … /* tamanho do buffer */
typedef … item; /* a estrutura exacta não é importante */
item buffer[DIM]; /* apenas para simplificação da exposição */
int in, out; /* assumir que DIM cabe em “int” */

/* inicialização (”Semaforo” já definido algures) */
Semaforo sem_mutex_p, sem_mutex_c;
Semaforo sem_itens; // garante que nunca um consumidor tentará utilizar a mesma posição no buffer que um produtor.
Semaforo sem_vazios; // garante que nunca um produtor tentará utilizar a mesma posição no buffer que um consumidor.

/* inicialização */
in = out = 0;
sem_mutex_p = criar_semaforo(1); // 1 -> semáforo de exclusão mútua
sem_mutex_c = criar_semaforo(1); // 1 -> semáforo de exclusão mútua
sem_itens = criar_semaforo(0); // 0 itens produzidos inicialmente
sem_vazios = criar_semaforo(DIM); // DIM elementos disponíveis

Produtor
while (COND) {
item_p = produz_item();
esperar(&sem_vazios); // contabilizar o número de elementos livres no buffer

esperar(&sem_mutex_p); //semáforo de exclusão mútua produtores
buffer[in] = item_p;
in = (in + 1) % DIM;
assinalar(&sem_mutex_p); //semáforo de exclusão mútua produtores

assinalar(&sem_itens); // semáforo para contabilizar o número de itens disponíveis

}

Consumidor
while (COND) {
esperar(&sem_itens); // semáforo para contabilizar o número de itens disponíveis

esperar(&sem_mutex_c); //semáforo de exclusão mútua consumidores
item_c = buffer[out];
out = (out + 1) % DIM;
assinalar(&sem_mutex_c); //semáforo de exclusão mútua consumidores

assinalar(&sem_vazios); // contabilizar o número de elementos livres no buffer
trata_item(item_c);
}

::Slides10 – SO2 – 2022 – NamedPipes v16.pdf
Comunicação interprocesso em Win32 com Named Pipes

Os named pipes podem ser:
De uma só via, O processo ou lê ou escreve (mas não ambos) na sua extremidadedo pipe
De duas vias (pipe duplex), O processo pode ler e escrever na sua extremidade do pipe (mas não ao mesmo tempo: tem que se gerir a sequência de leituras/escritas)

Operações do lado dos servidor:
Criação: CreateNamedPipe
Esperar ligação de um cliente: ConnectNamedPipe
Escrita/leitura: ReadFile / WriteFile

Operações do lado do cliente
Associação a um pipe existente: CreateFile ou CallNamedPipe
Esperar que um servidor esteja à escuta num pipe: WaitNamedPipe
Escrita/leitura: ReadFile / WriteFile

funções:
HANDLE WINAPI CreateNamedPipe(..) //criar namedPipe
BOOL WINAPI ConnectNamedPipe(..) //esperar ligação
BOOL WINAPI DisconnectNamedPipe(..) //terminar ligação

no Servidor:
CreateNamedPipe para criar a primeira instância (e seguintes) do named pipe
ConnectNamedPipe para aguardar um pedido de ligação à instância do named pipe. O atendimento dessa instância pode ser feito numa thread independente, libertando o servidor para criar outra instância e aguardar + processar outro cliente nela
FlushFileBuffers Para desligar do cliente, o servidor aguarda que os dados já tenham sido lidos pelo cliente.
DisconnectNamedPipe Após este passo pode-se desligar a instância do pipe
CloseHandle

no Cliente:
CreateFile para obter um handle (do lado cliente) para uma instância do named pipe
WaitNamedPipe aguardar que exista uma instância do servidor do pipe disponível (= aguardar que o servidor faça um ConnectNamedPipe). Cada cliente que se liga consome (ocupa) uma instância
O cliente interage com o servidor de acordo com um protocolo qualquer predefinido para essa aplicação cliente-servidor
O cliente termina a interação fechando os handles (com reflexo no servidor).

Acerca dos modos
PIPE_READMODE_BYTE
PIPE_READMODE_MESSAGE
Têm que bater certo no lado do servidor e no lado do cliente
PIPE_TYPE_BYTE – PYPE_READMODE_BYTE
PIPE_TYPE_MESSAGE – PYPE_READMODE_MESSAGE

Modo BYTE
O sistema não se preocupa com a fronteira entre mensagens: é tudo uma byte stream
Modo MESSAGE
O sistema percebe que um write corresponde a uma mensagem e ajuda a gerir na leitura. no caso em que se enviam mensagens de tipo e tamanho previamente conhecido

exemplo:
//o servidor
#include
#include
#define BUFSIZE 4096
int _tmain(int argc, TCHAR *argv[]) {
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = TEXT(“\\\\.\\pipe\\mynamedpipe”);
while (1) {
hPipe = CreateNamedPipe(
lpszPipename, // nome do pipe
PIPE_ACCESS_DUPLEX, // acesso read/write (duplex)
PIPE_TYPE_MESSAGE | // pipe to tipo message
PIPE_READMODE_MESSAGE | // modo message-read
PIPE_WAIT, // modo “blocking”
PIPE_UNLIMITED_INSTANCES, // max. instâncias
BUFSIZE, // tam. buffer output
BUFSIZE, // tam. Buffer input
NMPWAIT_USE_DEFAULT_WAIT, // time-out para o cliente
NULL); // atributos segurança default
if (hPipe == INVALID_HANDLE_VALUE) {
_tprintf(TEXT(“CreatePipe falhou”));
return 0;
}
// Aguarda a ligação de um cliente
fConnected = ConnectNamedPipe(hPipe, NULL);
if (!fConnected && (GetLastError() == ERROR_PIPE_CONNECTED) )
fConnected = TRUE;
if (fConnected) {
// atender o cliente (apresentado mais adiante)
AtendeCliente(hPipe) // feita mais adiante
// Este exemplo, não sendo multi-thread
// implica que só trata 1 cliente de cada vez (em série)
}
else
// Este cliente não se conseguiu ligar
// por isso fecha-se o pipe (continua para o prox cliente)
CloseHandle(hPipe);
// Próxima iteração -> mais uma instância e possível cliente
} // ciclo principal (apenas depois de ter atendido este cliente)
return 1;
} // fim do programa servidor

//o cliente
void AtendeCliente (HANDLE hPipe) {
char chRequest[BUFSIZE];
char chReply[BUFSIZE];
DWORD cbBytesRead, cbReplyBytes, cbWritten;
BOOL fSuccess;
while (1) { // ciclo p/ permitir haver N x pergunta-resposta)
// Lê dados do cliente via pipe. // mas cliente só vai fazer 1x
fSuccess = ReadFile(
hPipe, // handle para o pipe
chRequest, // buffer para receber os dados
BUFSIZE * sizeof(char), // tam do buffer (bytes a ler)
& cbBytesRead, // num. de bytes lidos
NULL); // não é overlapped I/O
// se não houver mais dados sai
if (! fSuccess || cbBytesRead == 0) break;
// Escreve a resposta para o pipe
fSuccess = WriteFile(
hPipe, // handle para o pipe
chReply, // buffer com os dados
cbReplyBytes, // num. de bytes a escrever
& cbWritten, // num. de bytes escritos
NULL); // não é overlapped I/O
// verifica operação efectuada
if (! fSuccess || cbReplyBytes != cbWritten)
break;
} // fim do ciclo de escrita
// faz flush ao pipe para garantir que o cliente já leu tudo
// antes de desligar esta instância do pipe
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
} // fim do atendimento deste cliente

#include
#include
#define BUFSIZE 512
int _tmain(int argc, TCHAR *argv[]) {
HANDLE hPipe;
LPTSTR lpvMessage;
TCHAR chBuf[BUFSIZE];
BOOL fSuccess;
DWORD cbRead, cbWritten, dwMode;
LPTSTR lpszPipename = “\\\\.\\pipe\\mynamedpipe”;
lpvMessage = argv[1]; // msg dada na linha de comando
// tenta repetidamente abrir o named pipe (em ciclo)
// espera se for preciso
while (1) { // o ciclo é só para abrir ligação com servidor
hPipe = CreateFile( // tenta obter um handle para o pipe
lpszPipename, // nome do pipe
GENERIC_READ | // acesso read & write
GENERIC_WRITE,
0, // sem “sharing”
NULL, // attributos segurança default
OPEN_EXISTING, // deve abrir um pipe já existente
0, // atributos default
NULL); // sem “ficheiro” “template”
// se obteve um handle válido, sai do ciclo
if (hPipe != INVALID_HANDLE_VALUE)
break; // pipe aberto -> sai do ciclo
// erro = outro que não PIPE_BUSY -> desiste
if (GetLastError() != ERROR_PIPE_BUSY) {
printf(“Não foi possível abrir o pipe”);
return 0; // num programa real: não terminar logo a
} // aplicação. Pode dar para fazer outras tarefas
// ERROR_PIPE_BUSY – todas as instâncias estavam ocup.
// tenta mais 1 (ou N) vezes mas espera entre cada tent.
// (espera 20 segundos “porque sim” – é só um exemplo )
// isto é uma estratégia definida pelo programador
// e podia ser diferente
if (!WaitNamedPipe(lpszPipename, 20000)) {
printf(“não foi possivel abrir o pipe”);
return 0;
}// O ciclo serve para tentar várias vezes porque o serv
// pode estar ocupado. Pode-se sair após N tentativas.
// Com serv multi-threaded este ciclo é menos importante
// o ciclo termina quando consegue abrir o pipe
// sai do ciclo via break (mais atrás)
} // fim do ciclo while em que tenta ligar ao pipe
// Neste momento o pipe está ligado ao servidor
// mudar para modo de leitura de mensagem
// porque foi esse o modo (MESSAGE) usado no servidor
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe, // handle para o pipe
& dwMode, // novo pipe mode
NULL, // maximum bytes = NULL (não altera)
NULL); // maximum time = NULL (não altera)
if (!fSuccess) {
printf(“SetNamedPipeHandleState falhou”);
return 0;
}
// lógica de interação nesta aplicação (servidor é diferente)
// -> cliente escreve 1 msg, servidor responde, fim
// 1º: cliente escreve,
// 2º cliente lê
// 1º Escrever uma mensagem no pipe
fSuccess = WriteFile( // no exemplo: envia sempre chars
hPipe, // handle para o pipe
lpvMessage, // mensagem
(lstrlen(lpvMessage)+1)*sizeof(TCHAR), // tam da msg
&cbWritten, // bytes escritos
NULL); // não é uma operação overlapped
if (!fSuccess) {
_tprintf(TEXT(“WriteFile falhou“));
return 0;
}
// 2º Ler dados do pipe
do {
fSuccess = ReadFile(
hPipe, // handle para o pipe
chBuf, // buffer para receber os dados
BUFSIZE * sizeof(TCHAR), // tam. do buffer
& cbRead, // num. bytes a ler
NULL); // não é overlapped
// se não houver mais dados sai
if (! fSuccess && GetLastError() != ERROR_MORE_DATA)
break;
printf(“%s\n”, chBuf ); // mostra informação lida
} while (!fSuccess); // repete até não haver mais dados
// = “até a resposta terminar” (o serv não envia mais nada)
CloseHandle(hPipe);
return 0;
} // fim do exemplo

::Slides11 – SO2 – 2022 – AppGraficasWin32 v23.pdf
Como chegar aos dados da janela em questão a partir do handle?
Colocar os dados em questão numa variável estruturada.
Colocar na estrutura do Windows que descreve a janela um ponteiro para essa variável estruturada com os dados em questão (reservando espaço para tal na estrutura WNDCLASSEX)

1) Registar o ponteiro para a estrutura com os dados nos bytes extra associados à janela
wcl.cbWndExtra = sizeof(EstruturaDados *);
2) Colocação de um ponteiro (criação da janela / na função main)
EstruturaDados dados; …
SetWindowLongPtr(hwnd, 0, (LONG_PTR) &dados);
3) Obtenção do ponteiro (na função da janela)
EstruturaDados * pont = (EstruturaDados *) GetWindowLongPtr(hwnd, 0);

A função da janela é modificada da seguinte maneira:
WM_CREATE,
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
WM_PAINT

Tags : , , , ,

0 thoughts on “Apontamentos teóricos”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.