Tag: so2LAB
repo: controlAviao
aviao:
#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; }
control:
#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); } }
repo: messageWINAPI
#include <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() typedef struct { int valor; TCHAR nome[80]; }DATA; 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 DATA data = {10, _T("pedro")}; // ============================================================================ // 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 = sizeof(DATA*); // 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 // ============================================================================ SetWindowLongPtr(hWnd, 0, (LONG_PTR)&data); 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)"&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(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); // Pré-processamento da mensagem (p.e. obter código // ASCII da tecla premida) DispatchMessage(&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) { DATA* pData=(DATA*)GetWindowLongPtr(hWnd, 0); switch (messg) { case WM_CHAR: if (wParam == 'a') { MessageBox(hWnd, pData->nome, TEXT("informa"), MB_OK); } break; 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); }
repo: namedPipes (variosLeitores OverLappedIO) outra versão
escritor:
#include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <windows.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define N 3 // estrutura do named pipe typedef struct { HANDLE hPipe; // handle do pipe OVERLAPPED overlap; BOOL activo; //representa se a instancia do named pipe esta ou nao ativa, se ja tem um cliente ou nao } PipeDados; typedef struct { PipeDados hPipes[N]; HANDLE hEvents[N]; HANDLE hMutex; int terminar; }ThreadDados; //envia mensagem para todos os leitores que estão disponiveis DWORD WINAPI ThreadMensagens(LPVOID param) { ThreadDados* dados = (ThreadDados*)param; TCHAR buf[256]; DWORD n; int i; BOOL ret; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; for (i = 0; i < N; i++) { // este named pipe está ativo? se sim vou escrever nele if (dados->hPipes[i].activo == TRUE) { if (!WriteFile(dados->hPipes[i].hPipe, buf, (DWORD)_tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); } WaitForSingleObject(dados->hMutex, INFINITE); ret=ReadFile(dados->hPipes[i].hPipe, buf, sizeof(TCHAR), &n, NULL); ReleaseMutex(dados->hMutex); //termina corretamente a string buf[n / sizeof(TCHAR)] = '\0'; _tprintf(TEXT("[ESCRITOR] Recebi %d bytes [%s]... \n"), n, buf); } //libertamos o mutex } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } while (_tcscmp(buf, TEXT("fim"))); dados->terminar = 1; //vou assinalar todos os eventos para que nao fique bloqueado na main for (i = 0; i < N; i++) SetEvent(dados->hEvents[i]); return 0; } int _tmain(int argc, LPTSTR argv[]) { HANDLE hPipe, hThread, hEventTemp; ThreadDados dados; int i, numClientes = 0; DWORD offset, nBytes; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif dados.terminar = 0; dados.hMutex = CreateMutex(NULL, FALSE, NULL); //Criação do mutex if (dados.hMutex == NULL) { _tprintf(TEXT("[Erro] ao criar mutex!\n")); return -1; } for (i = 0; i < N; i++) { // aqui passamos a constante FILE_FLAG_OVERLAPPED para o named pipe aceitar comunicações assincronas hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, N, 256 * sizeof(TCHAR), 256 * sizeof(TCHAR), 1000, NULL); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } // criar evento que vai ser associado à esturtura overlaped // os eventos aqui tem de ter sempre reset manual e nao automático porque temos de delegar essas responsabilidades ao sistema operativo hEventTemp = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEventTemp == NULL) { _tprintf(TEXT("[ERRO] ao criar evento\n")); return -1; } dados.hPipes[i].hPipe = hPipe; dados.hPipes[i].activo = FALSE; //temos de garantir que a estrutura overlap está limpa ZeroMemory(&dados.hPipes[i].overlap, sizeof(dados.hPipes[i].overlap)); //preenchemos agora o evento dados.hPipes[i].overlap.hEvent = hEventTemp; dados.hEvents[i] = hEventTemp; _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); // aqui passamos um ponteiro para a estrutura overlap ConnectNamedPipe(hPipe, &dados.hPipes[i].overlap); } //criacao da thread hThread = CreateThread(NULL, 0, ThreadMensagens, &dados, 0, NULL); if (hThread == NULL) { _tprintf(TEXT("[Erro] ao criar thread!\n")); return -1; } while (!dados.terminar && numClientes < N) { //permite estar bloqueado , à espera que 1 evento do array de enventos seja assinalado offset = WaitForMultipleObjects(N, dados.hEvents, FALSE, INFINITE); i = offset - WAIT_OBJECT_0; // devolve o indice da instancia do named pipe que está ativa, aqui sabemos em que indice o cliente se ligou // se é um indice válido ... if (i >= 0 && i < N) { _tprintf(TEXT("[ESCRITOR] Leitor %d chegou\n"), i); if (GetOverlappedResult(dados.hPipes[i].hPipe, &dados.hPipes[i].overlap, &nBytes, FALSE)) { // se entrarmos aqui significa que a funcao correu tudo bem // fazemos reset do evento porque queremos que o WaitForMultipleObject desbloqueio com base noutro evento e nao neste ResetEvent(dados.hEvents[i]); //vamos esperar que o mutex esteja livre WaitForSingleObject(dados.hMutex, INFINITE); dados.hPipes[i].activo = TRUE; // dizemos que esta instancia do named pipe está ativa ReleaseMutex(dados.hMutex); numClientes++; } } } //esperar que a thread termine WaitForSingleObject(hThread, INFINITE); //desligamos todos os clientes que se ligaram for (int i = 0; i < N; i++) { _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); //desliga todas as instancias de named pipes if (!DisconnectNamedPipe(dados.hPipes[i].hPipe)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } exit(0); }
leitor:
#include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <windows.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); //espera que exista um named pipe para ler do mesmo //bloqueia aqui 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")); //ligamo-nos ao named pipe que ja existe nesta altura //1º nome do named pipe, 2ºpermissoes (têm de ser iguais ao CreateNamedPipe do servidor), 3ºshared mode 0 aqui, //4º security atributes, 5ºflags de criação OPEN_EXISTING, 6º o default é FILE_ATTRIBUTE_NORMAL e o 7º é o template é NULL hPipe = CreateFile(PIPE_NAME, PIPE_ACCESS_DUPLEX, 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) { //le as mensagens enviadas pelo servidor ret = ReadFile(hPipe, buf, sizeof(TCHAR), &n, NULL); //termina corretamente a string 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); for (int i = 0; i < (int)n; i++) { if (!WriteFile(hPipe, buf, (DWORD)_tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] escrever no pipe\n")); } else { _tprintf(TEXT("[LEITOR] enviei %d butes ao escritor\n"), n); } } } CloseHandle(hPipe); Sleep(200); return 0; }
repo: namedPipes (variosLeitores OverLappedIO)
escritor:
//escritor || servidor //comunicação assincrona (overlapped) #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define N 10 //numero de clientes typedef struct { HANDLE hPipe; OVERLAPPED overlapped; //estrutura overlapped BOOL activo; //se já tem um cliente (pipe) activo ou nao }PipeDados; typedef struct { PipeDados hPipeDados[N]; HANDLE hEvents[N]; int numeroLeitores; //saber em tempo real a quantidade de leitores HANDLE hMutex;//sincronização: acesso sincronizado ao array int terminar; //controlar o fim da thread }ThreadDados; //thread auxiliar, para enviar para um conjunto de handles mensagens DWORD WINAPI TheadMensagens(LPVOID param) { DWORD n; TCHAR buf[256]; ThreadDados* dados = (ThreadDados*)param; int i; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(dados->hMutex, INFINITE); //região critica este ciclo do for for (i = 0; i < dados->numeroLeitores; i++) { //overlapped if(dados->hPipeDados[i].activo){ if (!WriteFile( dados->hPipeDados[i].hPipe, buf, //a string (DWORD)_tcslen(buf) * sizeof(TCHAR), //tamnho que se vai escrever: quantidade de caracteres * bytes que ocupa &n, //qt de informação que foi escrita NULL )) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } } ReleaseMutex(dados->hMutex); } while (_tcscmp(buf, TEXT("fim"))); dados->terminar = 1; for(i=0; i<dados->numeroLeitores; i++) { SetEvent(dados->hEvents[i]); } return 0; } int _tmain(int argc, LPTSTR argv[]) { HANDLE hPipe; TCHAR buf[256]; //ou 256 * sizeof(TCHAR) ThreadDados dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif dados.numeroLeitores = 0; dados.terminar = 0; dados.hMutex = CreateMutex( NULL, FALSE, NULL ); if (dados.hMutex == NULL) { _tprintf(TEXT("[ERRO] CreateMutex! (CreateMutex)")); exit(-1); } HANDLE hThreadMain; hThreadMain = CreateThread(NULL, 0, TheadMensagens, &dados, 0, NULL); if (hThreadMain == NULL) { _tprintf(TEXT("[ERRO] CreateThread! (CreateThread)")); exit(-1); } while (dados.terminar == 0) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, //nome PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, //modo de abertura do pipe: PIPE_ACCESS_OUTBOUND, o escritor/servidor envia para o cliente!! PIPE_ACCESS_INBOUND, se receber informacoes do cliente e pode ser duplex PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, //modo do funcionamento do pipe. O PIPE_WAIT pipe bloqueante e é tratado como mensagem usando o _MESSAGE N, //numero maximo de instancias sizeof(buf), // tamnho do buffer de entrada sizeof(buf), // tamnho do buffer de saida 1000, // default timeout, tempo de espera de um cliente NULL //atributos de segurança ); 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( //bloqueante, espera que alguém se ligue hPipe, //namepiped NULL // overlaped )) { //devolve 0 se correr mal _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } //novos clientes, e reigão critica WaitForSingleObject(dados.hMutex, INFINITE); dados.hPipeDados[dados.numeroLeitores].hPipe = hPipe; //dados.hPipeDados[dados.numeroLeitores].overlapped; dados.numeroLeitores++; ReleaseMutex(dados.hMutex); } //esperar que a thread termine WaitForSingleObject(hThreadMain, INFINITE); for (int i = 0; i < dados.numeroLeitores; i++) { _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(dados.hEvents[i])) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } // Sleep(2000); // CloseHandle(hPipe); return 0; }
leitor:
//leitor || cliente //comunicação assincrona (overlapped) #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.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); _setmode(_fileno(stderr), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe( PIPE_NAME, //string do nome do namedPipe NMPWAIT_WAIT_FOREVER //timeout para sempre NMPWAIT_WAIT_FOREVER, ou espera os 1000 NMPWAIT_USE_DEFAULT_WAIT )) { _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, //nome do namedPipe GENERIC_READ, //as permissões, ver PIPE_ACCESS_OUTBOUND, mas se fosse PIPE_ACCESS_INBOUND seria GENERIC_WRITE 0, //shared mode NULL, // security OPEN_EXISTING, //flags de criação, é sempre OPEN_EXISTING FILE_ATTRIBUTE_NORMAL, //flags NULL // template nao usamos ); 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( //cliente é só de leitura hPipe, // handle do pipe buf, //ponteiro onde escreve a informação lida sizeof(buf), //qt máxima de informações que cabem, 256 caracteres &n, //ponteiro, a quantidade de bytes que foram escrito NULL //overlaped, nao interessa ); buf[n / sizeof(TCHAR)] = '\0'; //tamanho de caracteres, 1 byte se for ascii ou de 2 bytes 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; }
Comunicação assincrona (overlapped)
_operações que normamente 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
processo, faz:
pedido de operação overlapped (não bloqueante)
e operação feita em background pelo SO, faz:
evento (opcional)
a estrutura do tipo overlapped deve ser inicializada a zero
a estrutura pode levar o handle de evento, depois dele ser criado
OVERLAPPED overlap;
ZeroMemory(&overlap, sizeof(overlap));
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //criar evento
overlap.hEvent = hEvent;
ConnectNamedPipe(hPipe, &overlap);
É possivel determinar o resultado de uma operação assincrona com GetOverlappedResult
BOOL GetOverlappedResult(
HANDLE hFile, //ponteiro para pipe, ficheiro,
LPOVERLAPPED lpOverLapped, // ponteiro para estrutura do tipo OVERLAPPED
LPWORD lpNumberOfBytesTransferred, // variavel para receber n de bytes
BOOl bWait //TRUE = espera que operação termine
);
repo: namedPipes (variosLeitores)
escritor:
//escritor || servidor //(thread secundária para partilhar por todos os leitores em simultaneos) //(mais do que um leitor ao mesmo tempo) //(o acesso à informação de exclusão mutua) //array com handles dos named pipes #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define N 10 //numero de clientes typedef struct { HANDLE hPipes[N]; int numeroLeitores; //saber em tempo real a quantidade de leitores HANDLE hMutex;//sincronização: acesso sincronizado ao array int terminar; //controlar o fim da thread }ThreadDados; //thread auxiliar, para enviar para um conjunto de handles mensagens DWORD WINAPI TheadMensagens(LPVOID param) { DWORD n; TCHAR buf[256]; ThreadDados* dados = (ThreadDados*)param; int i; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(dados->hMutex, INFINITE); //região critica este ciclo do for for (i = 0; i < dados->numeroLeitores; i++) { if (!WriteFile( dados->hPipes[i], buf, //a string (DWORD)_tcslen(buf) * sizeof(TCHAR), //tamnho que se vai escrever: quantidade de caracteres * bytes que ocupa &n, //qt de informação que foi escrita NULL )) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } ReleaseMutex(dados->hMutex); } while (_tcscmp(buf, TEXT("fim"))); dados->terminar = 1; //extra e depois do dados->terminar = 1; //simular a entrada de um novo cliente para desbloquear o ConnectNamedPipe no _main CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return 0; } int _tmain(int argc, LPTSTR argv[]) { HANDLE hPipe; TCHAR buf[256]; //ou 256 * sizeof(TCHAR) ThreadDados dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif dados.numeroLeitores = 0; dados.terminar = 0; dados.hMutex = CreateMutex( NULL, FALSE, NULL ); if (dados.hMutex == NULL) { _tprintf(TEXT("[ERRO] CreateMutex! (CreateMutex)")); exit(-1); } HANDLE hThreadMain; hThreadMain = CreateThread(NULL, 0, TheadMensagens, &dados, 0, NULL); if (hThreadMain == NULL) { _tprintf(TEXT("[ERRO] CreateThread! (CreateThread)")); exit(-1); } while (dados.terminar == 0) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, //nome PIPE_ACCESS_OUTBOUND, //modo de abertura do pipe: PIPE_ACCESS_OUTBOUND, o escritor/servidor envia para o cliente!! PIPE_ACCESS_INBOUND, se receber informacoes do cliente e pode ser duplex PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, //modo do funcionamento do pipe. O PIPE_WAIT pipe bloqueante e é tratado como mensagem usando o _MESSAGE N, //numero maximo de instancias sizeof(buf), // tamnho do buffer de entrada sizeof(buf), // tamnho do buffer de saida 1000, // default timeout, tempo de espera de um cliente NULL //atributos de segurança ); 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")); //mas o fim nao funciona: //1ª solução correta para desbloquear isto //2ª solução simular que surge um cliente novo para desbloquear este ConnectNamedPipe quando é escrito fim if (!ConnectNamedPipe( //bloqueante, espera que alguém se ligue hPipe, //namepiped NULL // overlaped )) { //devolve 0 se correr mal _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } //novos clientes, e reigão critica WaitForSingleObject(dados.hMutex, INFINITE); dados.hPipes[dados.numeroLeitores] = hPipe; dados.numeroLeitores++; ReleaseMutex(dados.hMutex); } //esperar que a thread termine WaitForSingleObject(hThreadMain, INFINITE); for (int i = 0; i < dados.numeroLeitores; i++) { _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(dados.hPipes[i])) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } // Sleep(2000); // CloseHandle(hPipe); return 0; }
leitor:
//leitor || cliente #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.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); _setmode(_fileno(stderr), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe( PIPE_NAME, //string do nome do namedPipe NMPWAIT_WAIT_FOREVER //timeout para sempre NMPWAIT_WAIT_FOREVER, ou espera os 1000 NMPWAIT_USE_DEFAULT_WAIT )) { _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, //nome do namedPipe GENERIC_READ, //as permissões, ver PIPE_ACCESS_OUTBOUND, mas se fosse PIPE_ACCESS_INBOUND seria GENERIC_WRITE 0, //shared mode NULL, // security OPEN_EXISTING, //flags de criação, é sempre OPEN_EXISTING FILE_ATTRIBUTE_NORMAL, //flags NULL // template nao usamos ); 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( //cliente é só de leitura hPipe, // handle do pipe buf, //ponteiro onde escreve a informação lida sizeof(buf), //qt máxima de informações que cabem, 256 caracteres &n, //ponteiro, a quantidade de bytes que foram escrito NULL //overlaped, nao interessa ); buf[n / sizeof(TCHAR)] = '\0'; //tamanho de caracteres, 1 byte se for ascii ou de 2 bytes 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; }
repo: namedPipes
escritor:
//escritor || servidor #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.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); _setmode(_fileno(stderr), _O_WTEXT); #endif _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, //nome PIPE_ACCESS_OUTBOUND, //modo de abertura do pipe: PIPE_ACCESS_OUTBOUND, o escritor/servidor envia para o cliente!! PIPE_ACCESS_INBOUND, se receber informacoes do cliente e pode ser duplex PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, //modo do funcionamento do pipe. O PIPE_WAIT pipe bloqueante e é tratado como mensagem usando o _MESSAGE 1, //numero maximo de instancias sizeof(buf), // tamnho do buffer de entrada sizeof(buf), // tamnho do buffer de saida 1000, // default timeout, tempo de espera de um cliente NULL //atributos de segurança ); 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( //bloqueante, espera que alguém se ligue hPipe, //namepiped NULL // overlaped )) { //devolve 0 se correr mal _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, //a string (DWORD)_tcslen(buf) * sizeof(TCHAR), //tamnho que se vai escrever: quantidade de caracteres * bytes que ocupa &n, //qt de informação que foi escrita 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); return 0; }
leitor:
//leitor || cliente #include <windows.h> #include <tchar.h> #include <stdio.h> #include <io.h> #include <fcntl.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); _setmode(_fileno(stderr), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe( PIPE_NAME, //string do nome do namedPipe NMPWAIT_WAIT_FOREVER //timeout para sempre NMPWAIT_WAIT_FOREVER, ou espera os 1000 NMPWAIT_USE_DEFAULT_WAIT )) { _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, //nome do namedPipe GENERIC_READ, //as permissões, ver PIPE_ACCESS_OUTBOUND, mas se fosse PIPE_ACCESS_INBOUND seria GENERIC_WRITE 0, //shared mode NULL, // security OPEN_EXISTING, //flags de criação, é sempre OPEN_EXISTING FILE_ATTRIBUTE_NORMAL, //flags NULL // template nao usamos ); 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( //cliente é só de leitura hPipe, // handle do pipe buf, //ponteiro onde escreve a informação lida sizeof(buf), //qt máxima de informações que cabem, 256 caracteres &n, //ponteiro, a quantidade de bytes que foram escrito NULL //overlaped, nao interessa ); buf[n / sizeof(TCHAR)] = '\0'; //tamanho de caracteres, 1 byte se for ascii ou de 2 bytes 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; }
repo: graficoMenus
#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(IDI_ICON_APP)); 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_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); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTO, 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_LEVANTAMENTO: 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) { 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; }
e no resource.h
#define IDI_ICON_APP 101 #define IDR_MENU_PRINCIPAL 102 #define IDR_ACCELERATOR 107 #define IDD_DIALOG1 108 #define IDC_LOGIN 1001 #define IDC_PASSWORD 1002 #define ID_CANCEL 1003 #define IDC_LIST_MONTANTES 1004 #define IDC_EDIT1 1006 #define IDS_STR_CONSULTA 1006 #define ID_OPERA40001 40001 #define ID_OPERA40002 40002 #define ID_LOGIN 40003 #define IDD_DIALOG_LOGIN 40003 #define ID_CONSULTA 40004 #define ID_LEVANTAMENTO 40005 #define IDD_DIALOG_LEVANTAMENTO 40005 #define ID_ACCELERATOR_CONSULTA 40006 #define ID_ACCELERATOR_LEVANTAMENTO 40007 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 110 #define _APS_NEXT_COMMAND_VALUE 40009 #define _APS_NEXT_CONTROL_VALUE 1007 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
repo: bufferCircular II
produtor:
//produtor #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> //lidar com o random #define TAM_BUFFER_CIRUCLAR 10 //importante para os semáforos, numero de posicoes do buffer circular typedef struct { int id; int val; } CelulaBufferCircular; //estrutura de cada célula do buffer circular typedef struct { int numeroProdutores; int numeroConsumidores; int proximaPosicaoEscrita; //controlar int proximaPosicaoLeitura; //controlar CelulaBufferCircular buffer[TAM_BUFFER_CIRUCLAR]; } BufferCircular; //representação da memoria partilhada typedef struct { BufferCircular* memoriaPartilhada; HANDLE hSemaforoControlaEscrita; HANDLE hSemaforoControloLeitura; HANDLE hMutexControloExclusaoMutua; //mutex: exclusivo e partilhado por todos os produtores int controloDaThread; //flag, 1 sair, 0 caso contrário int idProdutor; }DadosThreads; //lidar com o random int numero_aleatorio(int min, int max) { return rand() % (max - min + 1) + min; //valor entre min e max } //a trhead produtor DWORD WINAPI ThreadEscritaProdutor(LPVOID param) { DadosThreads* dados = (DadosThreads*)param; CelulaBufferCircular celBC; //teriam que ser vários fosse o caso de enviar para vários consumidores a informação de todos os produtores int contadorItensProduzidos = 0; while (dados->controloDaThread == 0) { celBC.id = dados->idProdutor; //produzir o item celBC.val = numero_aleatorio(10,99); //escrever no buffer circular, WaitForSingleObject( dados->hSemaforoControlaEscrita, //existe uma posição para escrevermos INFINITE ); //temos a garantia que temos uma posicção de escrita WaitForSingleObject( dados->hMutexControloExclusaoMutua, //existe uma posição para escrevermos INFINITE ); //temos a garantia que somos nos a escrever //os dois estão desbloqueados e vamos ser o unico produtor que vai escrever CopyMemory( &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->proximaPosicaoEscrita], //destino, copiar &celBC, sizeof(CelulaBufferCircular) ); //a proxima posicao de escrita é incrementada dados->memoriaPartilhada->proximaPosicaoEscrita++; //cheguei ao limite volto à posicao 0 if (dados->memoriaPartilhada->proximaPosicaoEscrita == TAM_BUFFER_CIRUCLAR) { dados->memoriaPartilhada->proximaPosicaoEscrita = 0; } //libertar o mutex ReleaseMutex(dados->hMutexControloExclusaoMutua); //libtertar o semaforo de leitura //se ocupamos uma posição de escrita libertamos uma posição de leitura //o produtor espera por uma posição de escrita e desbloqueia uma posição de leitura //e o consumidor espera por uma posição de leitura e desbloqueia uma posição de escrita ReleaseSemaphore(dados->hSemaforoControloLeitura, 1, NULL); //apenas uma posição, 1 contadorItensProduzidos++; _tprintf(TEXT("\nProdutor %d produziu valor %d "), dados->idProdutor, celBC.val); Sleep(numero_aleatorio(2, 4)*1000); } _tprintf(TEXT("\nProdutor %d produziu %d itens"), dados->idProdutor, contadorItensProduzidos); return 0; } int _tmain(int argc, LPSTR argv[]) { HANDLE hFileMap; //mapeamento da memoria partilhada DadosThreads dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //lidar com o random srand((unsigned int)time(NULL)); //os semaforos dados.hSemaforoControlaEscrita = CreateSemaphore( NULL, TAM_BUFFER_CIRUCLAR, TAM_BUFFER_CIRUCLAR, TEXT("SO2_MEMORIA_ESCRITA") ); if (dados.hSemaforoControlaEscrita == NULL) { _tprintf(TEXT("\nerro no hSemaforoControlaEscrita")); return 1; } dados.hSemaforoControloLeitura = CreateSemaphore(NULL, 0, //quantidade de posições iniciais, não existe nada para ser lido TAM_BUFFER_CIRUCLAR, //maximo de posições para serem lidas TEXT("SO2_MEMORIA_LEITURA") ); if (dados.hSemaforoControloLeitura == NULL) { _tprintf(TEXT("\nerro no hSemaforoControloLeitura")); return 1; } //exclusao mutua para os produtores dados.hMutexControloExclusaoMutua = CreateMutex(NULL, FALSE, TEXT("SO2_MUTEX_PRODUTOR")); if (dados.hMutexControloExclusaoMutua == NULL) { _tprintf(TEXT("\nerro no hMutexControloExclusaoMutua")); return 1; } hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("SO2_MEMORIA_PARTILHADA")); int primeiroProcesso = FALSE; //se existir é aberto //se nao existe é nulo if (hFileMap == NULL) { //existe um problem é porque se calahar não existe //criar a memoria partilhada primeiroProcesso = TRUE; hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,//INVALID_HANDLE_VALUE ao inves de um ficheiro, faz o sistema isso NULL, PAGE_READWRITE, //nivel de protecão: lieura e escrita); 0, //dimensões da memoria partilhada: mais significativa sizeof(BufferCircular), //dimensões da memoria partilhada: menos significataiva TEXT("SO2_MEMORIA_PARTILHADA")//nome do file mapping: que vai ser criado e aberto pelos outros, nome em MACRO ); if (hFileMap == NULL) { _tprintf(TEXT("\nerro no CreateFileMapping")); return 1; } } dados.memoriaPartilhada = (BufferCircular*)MapViewOfFile( hFileMap, FILE_MAP_ALL_ACCESS, //permissoes, de leitura e escrita 0, //offsets: onde é mapeada 0, //0 para mapear até ao final 0 //0 para mapear até ao final ); if (dados.memoriaPartilhada == NULL) { _tprintf(TEXT("\nerro no MapViewOfFile")); return 1; } //inicialização: só deve ser feita quando a mem partilhada é criada, pois podemos ter N produtores. if (primeiroProcesso == TRUE) { dados.memoriaPartilhada->numeroConsumidores = 0; dados.memoriaPartilhada->numeroProdutores = 0; dados.memoriaPartilhada->proximaPosicaoEscrita = 0; dados.memoriaPartilhada->proximaPosicaoLeitura = 0; } //criar a thread dados.controloDaThread = 0; //exclusão mutua proque queremos aceder a algo que está em memoria partilhada WaitForSingleObject(dados.hMutexControloExclusaoMutua, INFINITE); //ir à memoria partilhada dados.memoriaPartilhada->numeroProdutores++; dados.idProdutor = dados.memoriaPartilhada->numeroProdutores; //liberta ro mutex ReleaseMutex(dados.hMutexControloExclusaoMutua); HANDLE hThread; TCHAR comando[100]; hThread = CreateThread(NULL, 0, ThreadEscritaProdutor, &dados, 0, NULL); if (hThread != NULL) { _tprintf(TEXT("escreve qualquer coisa para sair")); _getts_s(comando, 100); dados.controloDaThread = 1; WaitForSingleObject(hThread, INFINITE); } UnmapViewOfFile(dados.memoriaPartilhada); //CloseHandle(); return 0; }
consumidor:
//consumidor #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> //lidar com o random #define TAM_BUFFER_CIRUCLAR 10 //importante para os semáforos, numero de posicoes do buffer circular typedef struct { int id; int val; } CelulaBufferCircular; //estrutura de cada célula do buffer circular typedef struct { int numeroProdutores; int numeroConsumidores; int proximaPosicaoEscrita; //controlar int proximaPosicaoLeitura; //controlar CelulaBufferCircular buffer[TAM_BUFFER_CIRUCLAR]; } BufferCircular; //representação da memoria partilhada typedef struct { BufferCircular* memoriaPartilhada; HANDLE hSemaforoControlaEscrita; HANDLE hSemaforoControloLeitura; HANDLE hMutexControloExclusaoMutua; //mutex: exclusivo e partilhado por todos os produtores int controloDaThread; //flag, 1 sair, 0 caso contrário int idConsumidor; }DadosThreads; //a thread consumidor DWORD WINAPI ThreadLeituraConsumidor(LPVOID param) { DadosThreads* dados = (DadosThreads*)param; CelulaBufferCircular celBC; //teriam que ser vários fosse o caso de enviar para vários consumidores a informação de todos os produtores int contadorItensProduzidos = 0; int soma = 0; while (dados->controloDaThread == 0) { //ler no buffer circular, WaitForSingleObject( dados->hSemaforoControloLeitura, //existe uma posição para ler INFINITE ); //temos a garantia que temos uma posicção de escrita WaitForSingleObject( dados->hMutexControloExclusaoMutua, //existe uma posição para escrevermos INFINITE ); //temos a garantia que somos nos a ler //os dois estão desbloqueados e vamos ser o unico consumdior que vai ler CopyMemory( &celBC, //copiar &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->proximaPosicaoLeitura], //destino sizeof(CelulaBufferCircular) ); //a proxima posicao de escrita é incrementada dados->memoriaPartilhada->proximaPosicaoLeitura++; //cheguei ao limite volto à posicao 0 if (dados->memoriaPartilhada->proximaPosicaoLeitura == TAM_BUFFER_CIRUCLAR) { dados->memoriaPartilhada->proximaPosicaoLeitura = 0; } //libertar o mutex quando terminamos a leitura ReleaseMutex(dados->hMutexControloExclusaoMutua); //libtertar o semaforo de escrita //se ocupamos uma posição de leitura libertamos uma posição de escrita //e o consumidor espera por uma posição de leitura e desbloqueia uma posição de escrita ReleaseSemaphore(dados->hSemaforoControlaEscrita, 1, NULL); //apenas uma posição, 1 contadorItensProduzidos++; soma = soma + celBC.val; _tprintf(TEXT("\nConsumidor %d consumiu valor %d "), dados->idConsumidor, celBC.val); //Sleep(numero_aleatorio(2, 4) * 1000); } _tprintf(TEXT("\nConsumidor %d consumiu %d acumulado"), dados->idConsumidor, soma); return 0; } //a thread consumidor DWORD WINAPI ThreadEscritaConsumidor(LPVOID param) { DadosThreads* dados = (DadosThreads*)param; CelulaBufferCircular celBC; //teriam que ser vários fosse o caso de enviar para vários consumidores a informação de todos os produtores int contadorItensProduzidos = 0; while (dados->controloDaThread == 0) { celBC.id = dados->idConsumidor; //produzir o item celBC.val = numero_aleatorio(10, 99); //escrever no buffer circular, WaitForSingleObject( dados->hSemaforoControlaEscrita, //existe uma posição para escrevermos INFINITE ); //temos a garantia que temos uma posicção de escrita WaitForSingleObject( dados->hMutexControloExclusaoMutua, //existe uma posição para escrevermos INFINITE ); //temos a garantia que somos nos a escrever //os dois estão desbloqueados e vamos ser o unico produtor que vai escrever CopyMemory( &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->proximaPosicaoEscrita], //destino, copiar &celBC, sizeof(CelulaBufferCircular) ); //a proxima posicao de escrita é incrementada dados->memoriaPartilhada->proximaPosicaoEscrita++; //cheguei ao limite volto à posicao 0 if (dados->memoriaPartilhada->proximaPosicaoEscrita == TAM_BUFFER_CIRUCLAR) { dados->memoriaPartilhada->proximaPosicaoEscrita = 0; } //libertar o mutex ReleaseMutex(dados->hMutexControloExclusaoMutua); //libtertar o semaforo de leitura //se ocupamos uma posição de escrita libertamos uma posição de leitura //o produtor espera por uma posição de escrita e desbloqueia uma posição de leitura //e o consumidor espera por uma posição de leitura e desbloqueia uma posição de escrita ReleaseSemaphore(dados->hSemaforoControloLeitura, 1, NULL); //apenas uma posição, 1 contadorItensProduzidos++; _tprintf(TEXT("\Consumdior %d produziu valor %d "), dados->idConsumidor, celBC.val); Sleep(numero_aleatorio(2, 4) * 1000); } _tprintf(TEXT("\Consumdior %d produziu %d itens"), dados->idConsumidor, contadorItensProduzidos); return 0; } int _tmain(int argc, LPSTR argv[]) { HANDLE hFileMap; //mapeamento da memoria partilhada DadosThreads dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //lidar com o random srand((unsigned int)time(NULL)); //os semaforos dados.hSemaforoControlaEscrita = CreateSemaphore( NULL, TAM_BUFFER_CIRUCLAR, TAM_BUFFER_CIRUCLAR, TEXT("SO2_MEMORIA_ESCRITA") ); if (dados.hSemaforoControlaEscrita == NULL) { _tprintf(TEXT("\nerro no hSemaforoControlaEscrita")); return 1; } dados.hSemaforoControloLeitura = CreateSemaphore(NULL, 0, //quantidade de posições iniciais, não existe nada para ser lido TAM_BUFFER_CIRUCLAR, //maximo de posições para serem lidas TEXT("SO2_MEMORIA_LEITURA") ); if (dados.hSemaforoControloLeitura == NULL) { _tprintf(TEXT("\nerro no hSemaforoControloLeitura")); return 1; } //exclusao mutua para os produtores dados.hMutexControloExclusaoMutua = CreateMutex(NULL, FALSE, TEXT("SO2_MUTEX_CONSUMIDOR")); if (dados.hMutexControloExclusaoMutua == NULL) { _tprintf(TEXT("\nerro no hMutexControloExclusaoMutua")); return 1; } hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("SO2_MEMORIA_PARTILHADA")); int primeiroProcesso = FALSE; //se existir é aberto //se nao existe é nulo if (hFileMap == NULL) { //existe um problem é porque se calahar não existe //criar a memoria partilhada primeiroProcesso = TRUE; hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,//INVALID_HANDLE_VALUE ao inves de um ficheiro, faz o sistema isso NULL, PAGE_READWRITE, //nivel de protecão: lieura e escrita); 0, //dimensões da memoria partilhada: mais significativa sizeof(BufferCircular), //dimensões da memoria partilhada: menos significataiva TEXT("SO2_MEMORIA_PARTILHADA")//nome do file mapping: que vai ser criado e aberto pelos outros, nome em MACRO ); if (hFileMap == NULL) { _tprintf(TEXT("\nerro no CreateFileMapping")); return 1; } } dados.memoriaPartilhada = (BufferCircular*)MapViewOfFile( hFileMap, FILE_MAP_ALL_ACCESS, //permissoes, de leitura e escrita 0, //offsets: onde é mapeada 0, //0 para mapear até ao final 0 //0 para mapear até ao final ); if (dados.memoriaPartilhada == NULL) { _tprintf(TEXT("\nerro no MapViewOfFile")); return 1; } //inicialização: só deve ser feita quando a mem partilhada é criada, pois podemos ter N produtores. if (primeiroProcesso == TRUE) { dados.memoriaPartilhada->numeroConsumidores = 0; dados.memoriaPartilhada->numeroProdutores = 0; dados.memoriaPartilhada->proximaPosicaoEscrita = 0; dados.memoriaPartilhada->proximaPosicaoLeitura = 0; } //criar a thread dados.controloDaThread = 0; //exclusão mutua proque queremos aceder a algo que está em memoria partilhada WaitForSingleObject(dados.hMutexControloExclusaoMutua, INFINITE); //ir à memoria partilhada dados.memoriaPartilhada->numeroConsumidores++; dados.idConsumidor = dados.memoriaPartilhada->numeroConsumidores; //libertar o mutex ReleaseMutex(dados.hMutexControloExclusaoMutua); HANDLE hThreadArray[2]; TCHAR comando[100]; hThreadArray[0] = CreateThread(NULL, 0, ThreadLeituraConsumidor, &dados, 0, NULL); hThreadArray[1] = CreateThread(NULL, 0, ThreadEscritaConsumidor, &dados, 0, NULL); if (hThreadArray[0] == NULL || hThreadArray[1] == NULL) { _tprintf(TEXT("nada de threads")); ExitProcess(3); } WaitForMultipleObjects(2, hThreadArray, TRUE, INFINITE); UnmapViewOfFile(dados.memoriaPartilhada); //CloseHandle(); return 0; }
repo: bufferCircular
consumidor
//consumidor #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> //lidar com o random #define TAM_BUFFER_CIRUCLAR 10 //importante para os semáforos, numero de posicoes do buffer circular typedef struct { int id; int val; } CelulaBufferCircular; //estrutura de cada célula do buffer circular typedef struct { int numeroProdutores; int numeroConsumidores; int proximaPosicaoEscrita; //controlar int proximaPosicaoLeitura; //controlar CelulaBufferCircular buffer[TAM_BUFFER_CIRUCLAR]; } BufferCircular; //representação da memoria partilhada typedef struct { BufferCircular* memoriaPartilhada; HANDLE hSemaforoControlaEscrita; HANDLE hSemaforoControloLeitura; HANDLE hMutexControloExclusaoMutua; //mutex: exclusivo e partilhado por todos os produtores int controloDaThread; //flag, 1 sair, 0 caso contrário int idConsumidor; }DadosThreads; //a thread consumidor DWORD WINAPI ThreadConsumidor(LPVOID param) { DadosThreads* dados = (DadosThreads*)param; CelulaBufferCircular celBC; //teriam que ser vários fosse o caso de enviar para vários consumidores a informação de todos os produtores int contadorItensProduzidos = 0; int soma = 0; while (dados->controloDaThread == 0) { //ler no buffer circular, WaitForSingleObject( dados->hSemaforoControloLeitura, //existe uma posição para ler INFINITE ); //temos a garantia que temos uma posicção de escrita WaitForSingleObject( dados->hMutexControloExclusaoMutua, //existe uma posição para escrevermos INFINITE ); //temos a garantia que somos nos a ler //os dois estão desbloqueados e vamos ser o unico consumdior que vai ler CopyMemory( &celBC, //copiar &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->proximaPosicaoLeitura], //destino sizeof(CelulaBufferCircular) ); //a proxima posicao de escrita é incrementada dados->memoriaPartilhada->proximaPosicaoLeitura++; //cheguei ao limite volto à posicao 0 if (dados->memoriaPartilhada->proximaPosicaoLeitura == TAM_BUFFER_CIRUCLAR) { dados->memoriaPartilhada->proximaPosicaoLeitura = 0; } //libertar o mutex quando terminamos a leitura ReleaseMutex(dados->hMutexControloExclusaoMutua); //libtertar o semaforo de escrita //se ocupamos uma posição de leitura libertamos uma posição de escrita //e o consumidor espera por uma posição de leitura e desbloqueia uma posição de escrita ReleaseSemaphore(dados->hSemaforoControlaEscrita, 1, NULL); //apenas uma posição, 1 contadorItensProduzidos++; soma = soma + celBC.val; _tprintf(TEXT("\nConsumidor %d consumiu valor %d "), dados->idConsumidor, celBC.val); //Sleep(numero_aleatorio(2, 4) * 1000); } _tprintf(TEXT("\nConsumidor %d consumiu %d acumulado"), dados->idConsumidor, soma); return 0; } int _tmain(int argc, LPSTR argv[]) { HANDLE hFileMap; //mapeamento da memoria partilhada DadosThreads dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //lidar com o random srand((unsigned int)time(NULL)); //os semaforos dados.hSemaforoControlaEscrita = CreateSemaphore( NULL, TAM_BUFFER_CIRUCLAR, TAM_BUFFER_CIRUCLAR, TEXT("SO2_MEMORIA_ESCRITA") ); if (dados.hSemaforoControlaEscrita == NULL) { _tprintf(TEXT("\nerro no hSemaforoControlaEscrita")); return 1; } dados.hSemaforoControloLeitura = CreateSemaphore(NULL, 0, //quantidade de posições iniciais, não existe nada para ser lido TAM_BUFFER_CIRUCLAR, //maximo de posições para serem lidas TEXT("SO2_MEMORIA_LEITURA") ); if (dados.hSemaforoControloLeitura == NULL) { _tprintf(TEXT("\nerro no hSemaforoControloLeitura")); return 1; } //exclusao mutua para os produtores dados.hMutexControloExclusaoMutua = CreateMutex(NULL, FALSE, TEXT("SO2_MUTEX_CONSUMIDOR")); if (dados.hMutexControloExclusaoMutua == NULL) { _tprintf(TEXT("\nerro no hMutexControloExclusaoMutua")); return 1; } hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("SO2_MEMORIA_PARTILHADA")); int primeiroProcesso = FALSE; //se existir é aberto //se nao existe é nulo if (hFileMap == NULL) { //existe um problem é porque se calahar não existe //criar a memoria partilhada primeiroProcesso = TRUE; hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,//INVALID_HANDLE_VALUE ao inves de um ficheiro, faz o sistema isso NULL, PAGE_READWRITE, //nivel de protecão: lieura e escrita); 0, //dimensões da memoria partilhada: mais significativa sizeof(BufferCircular), //dimensões da memoria partilhada: menos significataiva TEXT("SO2_MEMORIA_PARTILHADA")//nome do file mapping: que vai ser criado e aberto pelos outros, nome em MACRO ); if (hFileMap == NULL) { _tprintf(TEXT("\nerro no CreateFileMapping")); return 1; } } dados.memoriaPartilhada = (BufferCircular*)MapViewOfFile( hFileMap, FILE_MAP_ALL_ACCESS, //permissoes, de leitura e escrita 0, //offsets: onde é mapeada 0, //0 para mapear até ao final 0 //0 para mapear até ao final ); if (dados.memoriaPartilhada == NULL) { _tprintf(TEXT("\nerro no MapViewOfFile")); return 1; } //inicialização: só deve ser feita quando a mem partilhada é criada, pois podemos ter N produtores. if (primeiroProcesso == TRUE) { dados.memoriaPartilhada->numeroConsumidores = 0; dados.memoriaPartilhada->numeroProdutores = 0; dados.memoriaPartilhada->proximaPosicaoEscrita = 0; dados.memoriaPartilhada->proximaPosicaoLeitura = 0; } //criar a thread dados.controloDaThread = 0; //exclusão mutua proque queremos aceder a algo que está em memoria partilhada WaitForSingleObject(dados.hMutexControloExclusaoMutua, INFINITE); //ir à memoria partilhada dados.memoriaPartilhada->numeroConsumidores++; dados.idConsumidor = dados.memoriaPartilhada->numeroConsumidores; //libertar o mutex ReleaseMutex(dados.hMutexControloExclusaoMutua); HANDLE hThread; TCHAR comando[100]; hThread = CreateThread(NULL, 0, ThreadConsumidor, &dados, 0, NULL); if (hThread != NULL) { _tprintf(TEXT("escreve qualquer coisa para sair")); _getts_s(comando, 100); dados.controloDaThread = 1; WaitForSingleObject(hThread, INFINITE); } UnmapViewOfFile(dados.memoriaPartilhada); //CloseHandle(); return 0; }
produtor
//produtor #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> //lidar com o random #define TAM_BUFFER_CIRUCLAR 10 //importante para os semáforos, numero de posicoes do buffer circular typedef struct { int id; int val; } CelulaBufferCircular; //estrutura de cada célula do buffer circular typedef struct { int numeroProdutores; int numeroConsumidores; int proximaPosicaoEscrita; //controlar int proximaPosicaoLeitura; //controlar CelulaBufferCircular buffer[TAM_BUFFER_CIRUCLAR]; } BufferCircular; //representação da memoria partilhada typedef struct { BufferCircular* memoriaPartilhada; HANDLE hSemaforoControlaEscrita; HANDLE hSemaforoControloLeitura; HANDLE hMutexControloExclusaoMutua; //mutex: exclusivo e partilhado por todos os produtores int controloDaThread; //flag, 1 sair, 0 caso contrário int idProdutor; }DadosThreads; //lidar com o random int numero_aleatorio(int min, int max) { return rand() % (max - min + 1) + min; //valor entre min e max } //a trhead produtor DWORD WINAPI ThreadProdutor(LPVOID param) { DadosThreads* dados = (DadosThreads*)param; CelulaBufferCircular celBC; //teriam que ser vários fosse o caso de enviar para vários consumidores a informação de todos os produtores int contadorItensProduzidos = 0; while (dados->controloDaThread == 0) { celBC.id = dados->idProdutor; //produzir o item celBC.val = numero_aleatorio(10,99); //escrever no buffer circular, WaitForSingleObject( dados->hSemaforoControlaEscrita, //existe uma posição para escrevermos INFINITE ); //temos a garantia que temos uma posicção de escrita WaitForSingleObject( dados->hMutexControloExclusaoMutua, //existe uma posição para escrevermos INFINITE ); //temos a garantia que somos nos a escrever //os dois estão desbloqueados e vamos ser o unico produtor que vai escrever CopyMemory( &dados->memoriaPartilhada->buffer[dados->memoriaPartilhada->proximaPosicaoEscrita], //destino, copiar &celBC, sizeof(CelulaBufferCircular) ); //a proxima posicao de escrita é incrementada dados->memoriaPartilhada->proximaPosicaoEscrita++; //cheguei ao limite volto à posicao 0 if (dados->memoriaPartilhada->proximaPosicaoEscrita == TAM_BUFFER_CIRUCLAR) { dados->memoriaPartilhada->proximaPosicaoEscrita = 0; } //libertar o mutex ReleaseMutex(dados->hMutexControloExclusaoMutua); //libtertar o semaforo de leitura //se ocupamos uma posição de escrita libertamos uma posição de leitura //o produtor espera por uma posição de escrita e desbloqueia uma posição de leitura //e o consumidor espera por uma posição de leitura e desbloqueia uma posição de escrita ReleaseSemaphore(dados->hSemaforoControloLeitura, 1, NULL); //apenas uma posição, 1 contadorItensProduzidos++; _tprintf(TEXT("\nProdutor %d produziu valor %d "), dados->idProdutor, celBC.val); Sleep(numero_aleatorio(2, 4)*1000); } _tprintf(TEXT("\nProdutor %d produziu %d itens"), dados->idProdutor, contadorItensProduzidos); return 0; } int _tmain(int argc, LPSTR argv[]) { HANDLE hFileMap; //mapeamento da memoria partilhada DadosThreads dados; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif //lidar com o random srand((unsigned int)time(NULL)); //os semaforos dados.hSemaforoControlaEscrita = CreateSemaphore( NULL, TAM_BUFFER_CIRUCLAR, TAM_BUFFER_CIRUCLAR, TEXT("SO2_MEMORIA_ESCRITA") ); if (dados.hSemaforoControlaEscrita == NULL) { _tprintf(TEXT("\nerro no hSemaforoControlaEscrita")); return 1; } dados.hSemaforoControloLeitura = CreateSemaphore(NULL, 0, //quantidade de posições iniciais, não existe nada para ser lido TAM_BUFFER_CIRUCLAR, //maximo de posições para serem lidas TEXT("SO2_MEMORIA_LEITURA") ); if (dados.hSemaforoControloLeitura == NULL) { _tprintf(TEXT("\nerro no hSemaforoControloLeitura")); return 1; } //exclusao mutua para os produtores dados.hMutexControloExclusaoMutua = CreateMutex(NULL, FALSE, TEXT("SO2_MUTEX_PRODUTOR")); if (dados.hMutexControloExclusaoMutua == NULL) { _tprintf(TEXT("\nerro no hMutexControloExclusaoMutua")); return 1; } hFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, TEXT("SO2_MEMORIA_PARTILHADA")); int primeiroProcesso = FALSE; //se existir é aberto //se nao existe é nulo if (hFileMap == NULL) { //existe um problem é porque se calahar não existe //criar a memoria partilhada primeiroProcesso = TRUE; hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,//INVALID_HANDLE_VALUE ao inves de um ficheiro, faz o sistema isso NULL, PAGE_READWRITE, //nivel de protecão: lieura e escrita); 0, //dimensões da memoria partilhada: mais significativa sizeof(BufferCircular), //dimensões da memoria partilhada: menos significataiva TEXT("SO2_MEMORIA_PARTILHADA")//nome do file mapping: que vai ser criado e aberto pelos outros, nome em MACRO ); if (hFileMap == NULL) { _tprintf(TEXT("\nerro no CreateFileMapping")); return 1; } } dados.memoriaPartilhada = (BufferCircular*)MapViewOfFile( hFileMap, FILE_MAP_ALL_ACCESS, //permissoes, de leitura e escrita 0, //offsets: onde é mapeada 0, //0 para mapear até ao final 0 //0 para mapear até ao final ); if (dados.memoriaPartilhada == NULL) { _tprintf(TEXT("\nerro no MapViewOfFile")); return 1; } //inicialização: só deve ser feita quando a mem partilhada é criada, pois podemos ter N produtores. if (primeiroProcesso == TRUE) { dados.memoriaPartilhada->numeroConsumidores = 0; dados.memoriaPartilhada->numeroProdutores = 0; dados.memoriaPartilhada->proximaPosicaoEscrita = 0; dados.memoriaPartilhada->proximaPosicaoLeitura = 0; } //criar a thread dados.controloDaThread = 0; //exclusão mutua proque queremos aceder a algo que está em memoria partilhada WaitForSingleObject(dados.hMutexControloExclusaoMutua, INFINITE); //ir à memoria partilhada dados.memoriaPartilhada->numeroProdutores++; dados.idProdutor = dados.memoriaPartilhada->numeroProdutores; //liberta ro mutex ReleaseMutex(dados.hMutexControloExclusaoMutua); HANDLE hThread; TCHAR comando[100]; hThread = CreateThread(NULL, 0, ThreadProdutor, &dados, 0, NULL); if (hThread != NULL) { _tprintf(TEXT("escreve qualquer coisa para sair")); _getts_s(comando, 100); dados.controloDaThread = 1; WaitForSingleObject(hThread, INFINITE); } UnmapViewOfFile(dados.memoriaPartilhada); //CloseHandle(); return 0; }
repo: atende cliente OverLappedIO
Cliente:
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #include "..\cliente\util.h" DWORD WINAPI leResposta(LPVOID lpData) { HANDLE hPipe = (HANDLE)lpData; TCHAR szStr[TAM]; DWORD dwLidos, res; OVERLAPPED ov; HANDLE hEvento; hEvento = CreateEvent(NULL, TRUE, FALSE, NULL); //ultimo NULL evento local à thread não peciso de nome do { ZeroMemory(&ov, sizeof(ov)); ov.hEvent = hEvento; //ResetEvent(hEvent); res = ReadFile(hPipe, szStr, TAM, &dwLidos, &ov); //nao fico à espera agora, fica agendada a operação if (res) { _tprintf(_T("foi realizada de imediato...\n")); } if (!res && GetLastError() == ERROR_IO_PENDING) { //proteção _tprintf(_T("a aguardar informação...\n")); /* .... */ WaitForSingleObject(ov.hEvent, INFINITE); GetOverlappedResult(hPipe, &ov, &dwLidos, FALSE); } _tprintf(_T("Recebi: %s (%d)\n"), szStr, dwLidos); } while (_tcscmp(szStr, _T("SAIR")) != 0); return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hPipe; TCHAR szStr[TAM]; DWORD dwEscritos; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif while (1) { //ligar ao servidor hPipe = CreateFile(NOME_PIPE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (hPipe != INVALID_HANDLE_VALUE) { break; } if (!WaitNamedPipe(NOME_PIPE, 10000)) { //espera 10 segundos _tprintf(_T("[erro] named pipe nao existe ")); } } //thread para ler resposta HANDLE hThread; DWORD dwTid; hThread = CreateThread(NULL, 0, leResposta, (LPVOID)hPipe, 0, &dwTid); OVERLAPPED ov; HANDLE hEvento; hEvento = CreateEvent(NULL, TRUE, FALSE, NULL); //thread inicial do { _tprintf(_T("TEXTO: ")); if (_fgetts(szStr, TAM, stdin) == NULL) { break; } szStr[_tcslen(szStr) - 1] = _T('\0'); ZeroMemory(&ov, sizeof(ov)); //limpar ov.hEvent = hEvento; //atribuir //ResetEvent(hEvent)//nao é necessário WriteFile(hPipe, szStr, ((DWORD)_tcslen(szStr) + 1) * sizeof(TCHAR), &dwEscritos, &ov); //envia info para o servidor, e não bloqueia. dwEscritos total de bytes escritos //devia verificar e //true correu bem //true: correu bem //false: correu mal //getpasteerror, que a oepração foi agendada /* .. podemos fazer o que quiseremos não é tão critico.. */ WaitForSingleObject(ov.hEvent, INFINITE);//esperar que o evento seja assinalado GetOverlappedResult(hPipe, &ov, &dwEscritos, FALSE); //esperar que o write foi concluido. dwEscritos verificar se o numero de bytes foram escritos _tprintf(_T("Enviei: %s (%d)\n"), szStr, dwEscritos); } while (_tcscmp(szStr, _T("SAIR")) != 0); return 0; }
Servidor:
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #include "..\cliente\util.h" DWORD WINAPI atendeCliente(LPVOID lpData) { HANDLE hPipe = (HANDLE)lpData; TCHAR szStr[TAM]; DWORD dwLidos, dwEscritos; _tprintf(_T("Cliente ligado...\n")); while (1) { if (!ReadFile(hPipe, szStr, TAM, &dwLidos, NULL)) { //lê pedido.. break; } _tprintf(_T("Recebi: %s (%d)\n"), szStr, dwLidos); CharUpperBuff(szStr, dwLidos/sizeof(TCHAR)); //processa WriteFile(hPipe, szStr, dwLidos, &dwEscritos, NULL); //envia resposta _tprintf(_T("Enviei: %s (%d)\n"), szStr, dwEscritos); } _tprintf(_T("O cliente desligou...\n")); FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); CloseHandle(hPipe); return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hPipe; DWORD dwTid; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif while (1) { //cada vez que um cliente se ligar é criada uma nova thread hPipe = CreateNamedPipe( NOME_PIPE, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFSIZE, BUFFSIZE, 10000, NULL); if (ConnectNamedPipe(hPipe, NULL)) { CreateThread(NULL, 0, atendeCliente, (LPVOID)hPipe, 0, &dwTid); } } return 0; }
Util:
#define TAM 100 #define NOME_PIPE TEXT("\\\\.\\pipe\\teste") #define BUFFSIZE 1024
repo: atende cliente
Cliente:
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #include "..\cliente\util.h" int _tmain(int argc, TCHAR* argv[]) { HANDLE hPipe; TCHAR szStr[TAM]; DWORD dwLidos, dwEscritos; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif while (1) { //cada vez que um cliente se ligar é criada uma nova thread hPipe = CreateFile(NOME_PIPE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hPipe != INVALID_HANDLE_VALUE) { break; } WaitNamedPipe(NOME_PIPE, 10000); //espera 10 segundos } do{ _tprintf(_T("TEXTO: ")); if (_fgetts(szStr, TAM, stdin) == NULL) { break; } szStr[_tcslen(szStr) - 1] = _T('\0'); WriteFile(hPipe, szStr, ((DWORD)_tcslen(szStr) + 1) * sizeof(TCHAR), &dwEscritos, NULL); _tprintf(_T("Enviei: %s (%d)\n"), szStr, dwEscritos); ReadFile(hPipe, szStr, TAM, &dwLidos, NULL); _tprintf(_T("Recebi: %s (%d)\n"), szStr, dwLidos); } while (_tcscmp(szStr, _T("SAIR")) != 0); return 0; }
Servidor:
#include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #include "..\cliente\util.h" DWORD WINAPI atendeCliente(LPVOID lpData) { HANDLE hPipe = (HANDLE)lpData; TCHAR szStr[TAM]; DWORD dwLidos, dwEscritos; _tprintf(_T("Cliente ligado...\n")); while (1) { if (!ReadFile(hPipe, szStr, TAM, &dwLidos, NULL)) { //lê pedido.. break; } _tprintf(_T("Recebi: %s (%d)\n"), szStr, dwLidos); CharUpperBuff(szStr, dwLidos/sizeof(TCHAR)); //processa WriteFile(hPipe, szStr, dwLidos, &dwEscritos, NULL); //envia resposta _tprintf(_T("Enviei: %s (%d)\n"), szStr, dwEscritos); } _tprintf(_T("O cliente desligou...\n")); FlushFileBuffers(hPipe); DisconnectNamedPipe(hPipe); CloseHandle(hPipe); return 0; } int _tmain(int argc, TCHAR* argv[]) { HANDLE hPipe; DWORD dwTid; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); _setmode(_fileno(stderr), _O_WTEXT); #endif while (1) { //cada vez que um cliente se ligar é criada uma nova thread hPipe = CreateNamedPipe( NOME_PIPE, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFSIZE, BUFFSIZE, 10000, NULL); if (ConnectNamedPipe(hPipe, NULL)) { CreateThread(NULL, 0, atendeCliente, (LPVOID)hPipe, 0, &dwTid); } } return 0; }
Util:
#define TAM 100 #define NOME_PIPE TEXT("\\\\.\\pipe\\teste") #define BUFFSIZE 1024
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
Sessão 2/2 – Named Pipes, overlapped IO, duplex
Comunicação assíncrona – overlapped I/O
operações que normalmente bloqueiam até completarem (read, write) podem ser completadas em background pelo SO
isto consegue-se passando um ponteiro não nulo para uma estrutura do tipo OVERLAPPED
se na estrutura for passado um handle para um evento, esse evento é assinalado quando a operação for completada pelo SO
//escritor.c 2ª versao #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define TAM 256 #define PIPE_NMAX_INSTANCIAS 10 typedef struct { HANDLE hInstance; OVERLAPPED overlap; BOOL active; //se ja tem um cliente activo ou nao } PIPEDATA; PIPEDATA hPipes[PIPE_NMAX_INSTANCIAS]; HANDLE hEvents[PIPE_NMAX_INSTANCIAS], hMutex; int termina = 0; DWORD WINAPI ThreadMensagens(LPVOID param) { DWORD n; int i; TCHAR buf[TAM]; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(hMutex, INFINITE); for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { if (hPipes[i].active) { if (!WriteFile(hPipes[i].hInstance, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe %d! (WriteFile)\n"),i); exit(-1); }else{ _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } } } ReleaseMutex(hMutex); } while (_tcscmp(buf, TEXT("fim"))); //terminar termina = 1; for (i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { SetEvent(hEvents[i]); } return 0; } int _tmain(int argc, LPTSTR argv[]) { int i; int numClientes = 0; HANDLE hEventTemp, hPipeTemp, hThread; DWORD waitOffset, nbytes; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif hMutex = CreateMutex(NULL, FALSE, NULL); if(hMutex == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n")); exit(-1); } for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia %d do pipe '%s' ... (CreateNamedPipe)\n"), i, PIPE_NAME); hPipeTemp = CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_NMAX_INSTANCIAS, //mais clientes 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 1000, NULL // ); if (hPipeTemp == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Esperar ligação overlapped do leitor %d... (ConnectNamedPipe)\n"), i); hPipes[i].hInstance = hPipeTemp; hEventTemp = CreateEvent(NULL, TRUE, FALSE, NULL); if (hEventTemp == NULL) { _tprintf(TEXT("[ERRO] Criar Event! (CreateEvent)")); exit(-1); } ZeroMemory(&hPipes[i].overlap, sizeof(hPipes[i].overlap)); hEvents[i] = hEventTemp; hPipes[i].overlap.hEvent = hEvents[i]; if (ConnectNamedPipe(hPipeTemp, &hPipes[i].overlap) != 0) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } hPipes[i].active = FALSE; } hThread = CreateThread(NULL, 0, ThreadMensagens, NULL, 0, NULL); if (hThread == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n")); exit(-1); } while(!termina && numClientes < PIPE_NMAX_INSTANCIAS) { _tprintf(TEXT("[ESCRITOR] Esperar pela ligçaão de leitor %d (WaitForMultipleObjects + GetOverlappedResult)\n"), numClientes); i = waitOffset - WAIT_OBJECT_0; _tprintf(TEXT("[ESCRITOR] Novo i = %d\n"), i); if(i>= 0 && i < PIPE_NMAX_INSTANCIAS) { ResetEvent(hEvents[i]); WaitForSingleObject(hMutex, INFINITE); _tprintf(TEXT("Ativar pipe %d\n"), i); hPipes[i].active = TRUE; ReleaseMutex(hMutex); numClientes++; } } WaitForSingleObject(hThread, INFINITE); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); for (int i = 0; i < PIPE_NMAX_INSTANCIAS; i++) { if (!DisconnectNamedPipe(hPipes[i].hInstance)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } CloseHandle(hPipes[i].hInstance); } exit(0); }
encontrei mais informações sobre este exemplo em:
https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipe-server-using-overlapped-i-o
e a Comunicação bidirecional (duplex)
Sessão 1/2 – Named Pipes
Interprocessos (processo a falar uns com os outros):
Memoria partilhada
Named pipes
Threads do mesmo processo:
espaço de endereçamento do processo
Named pipes:
paradigma cliente <–> servidor
Um pipe com múltiplas instâncias, para resolver o problema de cinco clientes e um servidor
Nos namedPipes permitem comunicação bidirecional (se tiverem permissões de escrita e leitura para o cliente e servidor, mas não pode ser em simultâneo)
Servidor:
Tem uma thread para cada cliente
1º CreateNamedPipe
2º ConnectNamedPipe
3º Thread Secundária para atender os clientes
4º (fechar) FlushFileBuffers
5º (fechar) DisconnecNamedPipes
6º (fechar) CloseHandle
Cliente:
1º (ligar) CreateFile ou CallNamedPipe
2º (aguarda pela instância) WaitNamedPipe
3º SetNamedPipeHandleState
4º fechar os handles do processo
exemplos base
//escritor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { DWORD n; HANDLE hPipe; TCHAR buf[256]; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, sizeof(buf), sizeof(buf), 1000, NULL); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } while (1) { _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n); } while (_tcscmp(buf, TEXT("fim"))); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(hPipe)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } Sleep(2000); CloseHandle(hPipe); exit(0); }
//leitor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> // ... #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { TCHAR buf[256]; HANDLE hPipe; int i = 0; BOOL ret; DWORD n; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n")); hPipe = CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hPipe == NULL) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Liguei-me...\n")); while (1) { ret = ReadFile(hPipe, buf, sizeof(buf), &n, NULL); buf[n / sizeof(TCHAR)] = '\0'; if (!ret || !n) { _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n); break; } _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf); } CloseHandle(hPipe); Sleep(200); return 0; }
Nova versão com comnetários:
//escritor.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { DWORD n; HANDLE hPipe; TCHAR buf[256]; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, //nome pipe PIPE_ACCESS_OUTBOUND, //modo de abertura do pipe só o servidor escreve PIPE_WAIT | // bloqueante: bloqueio ate escrever PIPE_TYPE_MESSAGE | // organização da informação: agrupa bytes em mensagens na escrita PIPE_READMODE_MESSAGE, // organização da informação: agrupa bytes em mensagens na leitura 1, // numero maximo de instancias sizeof(buf), // tamanho da mensagem sizeof(buf), // tamanho da mensagem 1000, // tempo de espera do cliente para usar no: waitNamedPipe (cliente) NULL // ); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } while (1) { _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; if (!WriteFile(hPipe, buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor... (WriteFile)\n"), n); } while (_tcscmp(buf, TEXT("fim"))); _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(hPipe)) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } Sleep(2000); CloseHandle(hPipe); exit(0); }
//leitor.c ou cliente.c #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") int _tmain(int argc, LPTSTR argv[]) { TCHAR buf[256]; HANDLE hPipe; int i = 0; BOOL ret; DWORD n; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif _tprintf(TEXT("[LEITOR] Esperar pelo pipe '%s' (WaitNamedPipe)\n"), PIPE_NAME); if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER)) { //garantir que existe do lado do servidor algo ou definir um tempo ms _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (WaitNamedPipe)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Ligação ao pipe do escritor... (CreateFile)\n")); hPipe = CreateFile( PIPE_NAME, // no do ficheiro, nome do namedPipe GENERIC_READ, // (1) 0, // nao se aplica NULL, // nao interessa OPEN_EXISTING, // sempre OPEN, pois já esta criada FILE_ATTRIBUTE_NORMAL, // por default NULL // nao interessa ); //(1) //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_OUTBOUND então aqui tem que ser GENERIC_READ //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_INBOUND então aqui tem que ser GENERIC_WRITE //permissoes de acordo com o CreateNamedPipe, se no servidor foi PIPE_ACCESS_DUPLEX então aqui tem que ser GENERIC_READ | GENERIC_WRITE if (hPipe == NULL) { _tprintf(TEXT("[ERRO] Ligar ao pipe '%s'! (CreateFile)\n"), PIPE_NAME); exit(-1); } _tprintf(TEXT("[LEITOR] Liguei-me...\n")); while (1) { ret = ReadFile( hPipe, // handle para o pipe buf, // onde vai colocar a informação lida sizeof(buf), // a quantidade máxima que pode ler &n, // escreve o numero de bytes que foi lida, inclui o \0 NULL // overllaped nao estamos a usar ); buf[n / sizeof(TCHAR)] = '\0'; //ler o \0 no fim da string if (!ret || !n) { //quando surge o disconnect por parte do servidor _tprintf(TEXT("[LEITOR] %d %d... (ReadFile)\n"), ret, n); break; } _tprintf(TEXT("[LEITOR] Recebi %d bytes: '%s'... (ReadFile)\n"), n, buf); } CloseHandle(hPipe); Sleep(200); return 0; }
uma versão nova do escritor/servidor para aceitar vários clientes:
//escritor.c 2ª versao #include <Windows.h> #include <tchar.h> #include <math.h> #include <stdio.h> #include <fcntl.h> #include <io.h> #include <time.h> #define PIPE_NAME TEXT("\\\\.\\pipe\\teste") #define PIPE_NMAX_INSTANCIAS 10 typedef struct{ HANDLE hPipesCliente[PIPE_NMAX_INSTANCIAS]; int numeroClientes; HANDLE hMutex; //mutexs acesso int terminar; //controlar o fim da thread } ThreadDados; //a thread DWORD WINAPI ThreadMensagens(LPVOID param) { ThreadDados* dados = (ThreadDados*)param; TCHAR buf[256]; DWORD n; do { _tprintf(TEXT("[ESCRITOR] Frase: ")); _fgetts(buf, 256, stdin); buf[_tcslen(buf) - 1] = '\0'; WaitForSingleObject(dados->hMutex, INFINITE); //região critica.. dados->numeroClientes, dados->hPipesCliente[i] for(int i = 0; i < dados->numeroClientes; i++){ if (!WriteFile(dados->hPipesCliente[i], buf, _tcslen(buf) * sizeof(TCHAR), &n, NULL)) { _tprintf(TEXT("[ERRO] Escrever no pipe! (WriteFile)\n")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Enviei %d bytes ao leitor [%d]... (WriteFile)\n"), n, i); } //fim da região cirtica ReleaseMutex(dados->hMutex); } while (_tcscmp(buf, TEXT("fim"))); //terminar dados->terminar = 1; //i) simular o criar um novo cliente, para o "fim" funcionar CreateFile(PIPE_NAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); return 0; } int _tmain(int argc, LPTSTR argv[]) { HANDLE hPipe; //thread ThreadDados dados; //f) HANDLE hThread; #ifdef UNICODE _setmode(_fileno(stdin), _O_WTEXT); _setmode(_fileno(stdout), _O_WTEXT); #endif dados.numeroClientes = 0; dados.terminar = 0; //criar mutex dados.hMutex = CreateMutex(NULL, FALSE, NULL); if(dados.hMutex == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o mutex\n")); exit(-1); } //g) hThread = CreateThread(NULL, 0, ThreadMensagens, &dados, 0, NULL); if(hThread == NULL) { _tprintf(TEXT("[ESCRITOR] Erro a criar o CreateThread\n")); exit(-1); } //e) while (!dados.terminar) { _tprintf(TEXT("[ESCRITOR] Criar uma cópia do pipe '%s' ... (CreateNamedPipe)\n"), PIPE_NAME); hPipe = CreateNamedPipe( PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, PIPE_NMAX_INSTANCIAS, //mais clientes 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 256 * sizeof(TCHAR), //update ou usar outro TCHAR buf[256]; 1000, NULL // ); if (hPipe == INVALID_HANDLE_VALUE) { _tprintf(TEXT("[ERRO] Criar Named Pipe! (CreateNamedPipe)")); exit(-1); } _tprintf(TEXT("[ESCRITOR] Esperar ligação de um leitor... (ConnectNamedPipe)\n")); if (!ConnectNamedPipe(hPipe, NULL)) { //operação bloqueante até que aja um cliente _tprintf(TEXT("[ERRO] Ligação ao leitor! (ConnectNamedPipe\n")); exit(-1); } //b) WaitForSingleObject(dados.hMutex, INFINITE); //a) ciclo de aceitação dos novos clientes dados.hPipesCliente[dados.numeroClientes] = hPipe; dados.numeroClientes++; //b) ReleaseMutex(dados.hMutex); } //h) esperar que a thread termine WaitForSingleObject(hThread, INFINITE); //c) encerrar com todos os clientes for (int i = 0; i < dados.numeroClientes; i++) { _tprintf(TEXT("[ESCRITOR] Desligar o pipe (DisconnectNamedPipe)\n")); if (!DisconnectNamedPipe(dados.hPipesCliente[i])) { _tprintf(TEXT("[ERRO] Desligar o pipe! (DisconnectNamedPipe)")); exit(-1); } } exit(0); }
sessão 11 – Programação gráfica orientada a eventos (2ª versão)
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Esta é a segunda revisão destes conteúdos, usar os recursos do win32 GDI:
1) adicionar recurso
2) escolher Icon
e apagar todos.. e ficar só com um.
3) aceder ao ID do recurso, e alterar para IDI_ICON_APP
4) adicionar um menu, com duas opções fixas e uma opção com submenus
o login, com popup a FALSE
e atribuir os ID a cada uma das opções.
5) as caixas de dialogo
e os componentes para a caixa de diálogo:
6) atalhos de teclas, aceleradores no windows
7) string table
ter um texto configurável, num recurso à parte. Utilidade: internacionalização da aplicação (tradução)
7) pointer
8) os comportamentos, usar o seguinte:
#include <windows.h> #include <Windowsx.h> #include <tchar.h> #include "resource.h" #define NUM_CLIENTES 3 #define LIST_SIZE 8 TCHAR* LIST_ITENS[] = { TEXT("10€"), TEXT("20€"), TEXT("40€"), TEXT("60€"), TEXT("80€"), TEXT("100€"), TEXT("150€"), TEXT("200€") }; LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLogin(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLevantar(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Ficha8"); typedef struct { unsigned int ID; TCHAR username[16]; TCHAR password[16]; unsigned int saldo; } cliente; typedef struct { unsigned int tipo; // 1 = depósito, 2 = levantamento unsigned int quantia; unsigned int ID; } operacao; typedef struct { cliente clientes[NUM_CLIENTES]; operacao historico[200]; unsigned int numOperacoes; } dados; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; HANDLE hAccel; dados dadosPartilhados; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); wcApp.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_PRINCIPAL); wcApp.cbClsExtra = sizeof(dados); wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(220, 220, 220)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("SO2 - Ficha 8"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 150, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); dadosPartilhados.numOperacoes = 5; // Apenas para testar... LONG_PTR x = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&dadosPartilhados); ShowWindow(hWnd, nCmdShow); hAccel = LoadAccelerators(NULL, MAKEINTRESOURCE(IDR_ACCELERATOR)); while (GetMessage(&lpMsg, NULL, 0, 0)) { if (!TranslateAccelerator(hWnd, hAccel, &lpMsg)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR str1[512], str2[512]; dados* dadosPartilhados; dadosPartilhados = (dados*)GetWindowLongPtr(hWnd, GWLP_USERDATA); switch (messg) { case WM_CREATE: EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_DISABLED | MF_GRAYED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_DISABLED | MF_GRAYED); break; case WM_COMMAND: switch (LOWORD(wParam)) { case ID_LOGIN: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LOGIN), hWnd, TrataEventosLogin); EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_ENABLED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_ENABLED); break; case ID_CONSULTA: case ID_ACCELERATOR_CONSULTA: LoadString(NULL, IDS_STR_CONSULTA, str1, 512); _stprintf_s(str2, 512, TEXT("%s (%d)"), str1, dadosPartilhados->numOperacoes); MessageBox(hWnd, str2, TEXT("String Table"), MB_OK | MB_ICONINFORMATION); break; case ID_ACCELERATOR_LEVANTAMENTOS: case ID_LEVANTAMENTOS: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LEVANTAMENTO), NULL, TrataEventosLevantar); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, messg, wParam, lParam); break; } return(0); } LRESULT CALLBACK TrataEventosLogin(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR username[16]; switch (messg) { case WM_COMMAND: if (LOWORD(wParam) == IDOK) { GetDlgItemText(hWnd, IDC_EDIT_LOGIN, username, 16); MessageBox(hWnd, username, TEXT("Username"), MB_OK | MB_ICONINFORMATION); } else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hWnd, 0); return TRUE; } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; } LRESULT CALLBACK TrataEventosLevantar(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { int i; switch (messg) { case WM_INITDIALOG: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); SendMessage(hwndList, LB_RESETCONTENT, 0, 0); for (i = 0; i < LIST_SIZE; i++) SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)LIST_ITENS[i]); break; case WM_COMMAND: if (LOWORD(wParam) == IDC_LIST_MONTANTES) { switch (HIWORD(wParam)) { case LBN_DBLCLK: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); i = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); MessageBox(hWnd, LIST_ITENS[i], TEXT("ListBox"), MB_OK | MB_ICONINFORMATION); break; } } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; }
Alguns comentários:
#include <windows.h> #include <Windowsx.h> #include <tchar.h> #include "resource.h" //aceder aos ids, macros simbólicas #define NUM_CLIENTES 3 #define LIST_SIZE 8 TCHAR* LIST_ITENS[] = { TEXT("10€"), TEXT("20€"), TEXT("40€"), TEXT("60€"), TEXT("80€"), TEXT("100€"), TEXT("150€"), TEXT("200€") }; //tratamento de eventos das janelas LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLogin(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosLevantar(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TrataEventosAcerca(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Ficha8"); typedef struct { unsigned int ID; TCHAR username[16]; TCHAR password[16]; unsigned int saldo; } cliente; typedef struct { unsigned int tipo; // 1 = depósito, 2 = levantamento unsigned int quantia; unsigned int ID; } operacao; //partilhar informação entre o mains e as funções de tratamentos de eventos typedef struct { cliente clientes[NUM_CLIENTES]; operacao historico[200]; unsigned int numOperacoes; } dados; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; HANDLE hAccel; dados dadosPartilhados; //partilha de recursos wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; //para poder usar as macros fazer uso do MAKEINTRESOURCE wcApp.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_APP)); wcApp.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_POINTER)); wcApp.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_PRINCIPAL); wcApp.cbClsExtra = sizeof(dados); wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(220, 220, 220)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("SO2 - Ficha 8"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 150, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //partilha de recursos dadosPartilhados.numOperacoes = 5; // Apenas para testar... LONG_PTR x = SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)&dadosPartilhados); ShowWindow(hWnd, nCmdShow); //ciclo de processamentos das mensagens, dos atalhos hAccel = LoadAccelerators(NULL, MAKEINTRESOURCE(IDR_ACCELERATOR)); while (GetMessage(&lpMsg, NULL, 0, 0)) { if (!TranslateAccelerator(hWnd, hAccel, &lpMsg)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR str1[512], str2[512]; dados* dadosPartilhados; dadosPartilhados = (dados*)GetWindowLongPtr(hWnd, GWLP_USERDATA); //partilha de recursos switch (messg) { case WM_CREATE: //código de inicialização do sistema EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_DISABLED | MF_GRAYED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_DISABLED | MF_GRAYED); break; case WM_COMMAND: //a parte menos significativa trás o ID da opção da janela que deu origem ao WM_COMMAND switch (LOWORD(wParam)) { case ID_LOGIN: //contexto, ID da macro da dialog box, o handler da janela principal, função para tratar o evento DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LOGIN), hWnd, TrataEventosLogin); EnableMenuItem(GetMenu(hWnd), ID_CONSULTA, MF_ENABLED); EnableMenuItem(GetMenu(hWnd), ID_LEVANTAMENTOS, MF_ENABLED); break; //a parte menos significativa trás o ID da opção da janela que deu origem ao WM_COMMAND case ID_CONSULTA: case ID_ACCELERATOR_CONSULTA: LoadString(NULL, IDS_STR_CONSULTA, str1, 512); _stprintf_s(str2, 512, TEXT("%s (%d)"), str1, dadosPartilhados->numOperacoes); MessageBox(hWnd, str2, TEXT("String Table"), MB_OK | MB_ICONINFORMATION); break; case ID_ACCELERATOR_LEVANTAMENTOS: case ID_LEVANTAMENTOS: DialogBox(NULL, MAKEINTRESOURCE(IDD_DIALOG_LEVANTAMENTO), NULL, TrataEventosLevantar); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, messg, wParam, lParam); break; } return(0); } LRESULT CALLBACK TrataEventosLogin(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { TCHAR username[16]; switch (messg) { case WM_COMMAND: if (LOWORD(wParam) == IDOK) { GetDlgItemText(hWnd, IDC_EDIT_LOGIN, username, 16); MessageBox(hWnd, username, TEXT("Username"), MB_OK | MB_ICONINFORMATION); } else if (LOWORD(wParam) == IDCANCEL) { EndDialog(hWnd, 0); return TRUE; } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; } LRESULT CALLBACK TrataEventosLevantar(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { int i; switch (messg) { case WM_INITDIALOG: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); SendMessage(hwndList, LB_RESETCONTENT, 0, 0); for (i = 0; i < LIST_SIZE; i++) SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)LIST_ITENS[i]); break; case WM_COMMAND: if (LOWORD(wParam) == IDC_LIST_MONTANTES) { switch (HIWORD(wParam)) { case LBN_DBLCLK: HWND hwndList = GetDlgItem(hWnd, IDC_LIST_MONTANTES); i = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); MessageBox(hWnd, LIST_ITENS[i], TEXT("ListBox"), MB_OK | MB_ICONINFORMATION); break; } } break; case WM_CLOSE: EndDialog(hWnd, 0); return TRUE; } return FALSE; }
sessão 10 – Programação gráfica orientada a eventos (2ª versão)
Referências bibliográficas:
Capítulos 2-5 e 7-9 do Livro Windows NT 4 Programming
MSDN:
Introduction to Windows Programming in C++
https://msdn.microsoft.com/en-us/library/ff381398(v=vs.85).aspx
Mouse Input
https://msdn.microsoft.com/en-us/library/gg153549(v=vs.85).aspx
Keyboard Input
https://msdn.microsoft.com/en-us/library/gg153546(v=vs.85).aspx
Mouse Movement
https://msdn.microsoft.com/en-us/library/gg153550(v=vs.85).aspx
Dialog Boxes
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632588(v=vs.85).aspx
Menus
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646977(v=vs.85).aspx
Windows Graphics Device Interface (GDI)
https://msdn.microsoft.com/en-us/library/dd145203(v=vs.85).aspx
Tutorial online: Win32 Fundamentals
http://www.functionx.com/win32/Lesson01.htm
Events
http://www.functionx.com/win32/Lesson05.htm
Object-Oriented Win32
http://www.functionx.com/win32/Lesson06.htm
Esta é a segunda revisão destes conteúdos (desenhar completo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //função que vai ser chamada pelo windows sempre que acontece alguma coisa: eventos.. LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //nome da janela //winMain: main na parte gráfica int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; // hWnd é o handler da janela, gerado mais abaixo por CreateWindow() MSG lpMsg; // MSG é uma estrutura definida no Windows para as mensagens WNDCLASSEX wcApp; // WNDCLASSEX definir as características da classe da janela //1. caracteristicas da janela wcApp.cbSize = sizeof(WNDCLASSEX); // Tamanho da estrutura WNDCLASSEX wcApp.hInstance = hInst;// Instância da janela actualmente exibida wcApp.lpszClassName = szProgName; // Nome da janela (neste caso = nome do programa) wcApp.lpfnWndProc = TrataEventos; //indicar a a função que trata eventos wcApp.style = CS_HREDRAW | CS_VREDRAW; // Estilo da janela: Fazer o redraw se for // modificada horizontal ou verticalmente wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); // "NULL" = Icon definido no Windows wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); // "hCursor" = handler do cursor (rato) // "NULL" = Forma definida no Windows // "IDC_ARROW" Aspecto "seta" wcApp.lpszMenuName = NULL; // (NULL = não tem menu) wcApp.cbClsExtra = 0; // Livre, para uso particular wcApp.cbWndExtra = 0; // Livre, para uso particular wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); //cor de fundo // 2. Registar a classe "wcApp" no Windows if (!RegisterClassEx(&wcApp)) return(0); // 3. Criar a janela hWnd = CreateWindow( szProgName, // Nome da janela (programa) definido acima TEXT("Estudo para o exame"),// Texto que figura na barra do título WS_OVERLAPPEDWINDOW, // Estilo da janela (WS_OVERLAPPED= normal) CW_USEDEFAULT, // Posição x pixels (CW_USEDEFAULT=à direita da última) CW_USEDEFAULT, // Posição y pixels (CW_USEDEFAULT=abaixo da última) 800, // Largura da janela (em pixels) 600, // Altura da janela (em pixels) (HWND)HWND_DESKTOP, // handle da janela pai ou HWND_DESKTOP se a janela for a primeira, criada a partir do "desktop" (HMENU)NULL, // handle do menu da janela (se tiver menu) (HINSTANCE)hInst, 0); // 4. Mostrar a janela ShowWindow(hWnd, nCmdShow); // "hWnd"= handler da janela, devolvido por // "CreateWindow"; "nCmdShow"= modo de exibição (p.e. // normal/modal); é passado como parâmetro de WinMain() UpdateWindow(hWnd); // Refrescar a janela (Windows envia à janela uma mensagem para pintar, mostrar dados, (refrescar)… // 5. Loop de Mensagens while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); // pre processar os eventos DispatchMessage(&lpMsg); // } //GetMessage se recebe zero, fechar a janela, sai do ciclo // 6. Fim do programa return((int)lpMsg.wParam); // Retorna sempre o parâmetro wParam da estrutura lpMsg } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { // UINT messg: código do evento de carregar numa tecla // valor da tecla que foi carregada é do WPARAM wParam // lParam: posixaoX, posicaoY do rato na janela static TCHAR c = '?'; //ou global //static int posX = 0; //ou static ou global, com o BeginPaint //static int posY = 0; //ou static ou global, com o BeginPaint HDC hdc; //para a escrita em janela!, handle device content RECT rect; //espaço de desenho static TCHAR charValue = '?'; PAINTSTRUCT ps; //array dos histórico das posicções static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: //evento para refresh da janela //operação de escrita REAL na janela é aqui hdc= BeginPaint(hWnd, &ps); //substitui GetDC(hWnd); // a posição da escrita GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); //pintar funto controlado SetTextColor(hdc, RGB(0, 123, 0)); //fundo transparente SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; //escrita do texto DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); //substitui ReleaseDC(hWnd, hdc); break; //case WM_ERASEBKGND: //somos nós a tratar o BKRND // return TRUE; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); //macro GET_X_LPARAM posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam);//macro GET_Y_LPARAM posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); //janela invalida, pinta de novo } //limpar a janela com o beginpaint //inicio processo desenhar/escrever //hdc = GetDC(hWnd); //fim processo desenhar/escrever //ReleaseDC(hWnd, hdc); break; case WM_CLOSE: //fechar a janela if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) //combinar flags | == IDYES) { DestroyWindow(hWnd); //fechar a janela } break; case WM_DESTROY: // Destruir a janela e terminar o programa PostQuitMessage(0); // "PostQuitMessage(Exit Status)" break; default: //mt importante, para lidar com o que não foi feito tratamento return(DefWindowProc(hWnd, messg, wParam, lParam)); break; // break tecnicamente desnecessário por causa do return } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar completo e limpo):
#include <windows.h> #include <tchar.h> #include <windowsx.h> LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0,255,0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } int contaCliques =0; typedef struct { int xPos, yPos; TCHAR c; } PosicaoCaracter; LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { static TCHAR c = '?'; HDC hdc; RECT rect; static TCHAR charValue = '?'; PAINTSTRUCT ps; static PosicaoCaracter posicoes[500]; static int totoalPosicoes = 0; int i; switch (messg) { case WM_PAINT: hdc= BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hWnd, &rect, CreateSolidBrush(RGB(0, 255, 0))); SetTextColor(hdc, RGB(0, 123, 0)); SetBkColor(hdc, TRANSPARENT); for(i=0; i < totoalPosicoes; i++){ rect.left = posicoes[i].xPos; rect.top = posicoes[i].yPos; DrawText(hdc, &posicoes[i].c, 1, &rect, DT_SINGLELINE | DT_NOCLIP); } EndPaint(hWnd, &ps); break; case WM_CHAR: c = (TCHAR)wParam; break; case WM_LBUTTONDOWN: contaCliques++; charValue = contaCliques + '0'; break; case WM_RBUTTONDOWN: if(totoalPosicoes < 500){ posicoes[totoalPosicoes].xPos = GET_X_LPARAM(lParam); posicoes[totoalPosicoes].yPos = GET_Y_LPARAM(lParam); posicoes[totoalPosicoes].c = charValue; totoalPosicoes++; InvalidateRect(hWnd, NULL, FALSE); } break; case WM_CLOSE: if( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar uma BMP na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Usar um BMP animado na janela (segunda versão):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animção int limDireito; HWND hWndGlobalJanela; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência):
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); //lidar com o bitmap, globais: têm que ser acedidas por várias funções, ou uma struct 1 uma variavel global HBITMAP hBMP; //acesso ao bitmap HDC bmpDC; BITMAP bmp; //estrutura do bitmap int xBitmap; int yBitmap; //lidar com a animação int limDireito; HWND hWndGlobalJanela; //concorrencia, problema de sincronização HANDLE hMutex; DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; // 1: direita, -1: esquerda int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); //nova pintua da janela: avisar o SO InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); //lidar com o bitmap hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); //preciso da informação da imagem (metadados) GetObject(hBMP, sizeof(bmp), &bmp); //passar a informação para o device content HDC hdc; hdc = GetDC(hWnd); //criar a copia bmpDC = CreateCompatibleDC(hdc); //aplicar o bitmap ao DC SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); //definir as posições iniciais da imagem: ao centro RECT rect; //largura da janela actual do cliente GetClientRect(hWnd, &rect); //xBitmap = (rect.right / 2) - (bmp.bmWidth / 2); //yBitmap = (rect.bottom / 2) - (bmp.bmHeight / 2); //para o MovimentaImagem limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); //SetTextColor(hdc, RGB(0, 123, 0)); //SetBkColor(hdc, TRANSPARENT); //desenhar WaitForSingleObject(hMutex, INFINITE); //problema de sincronização BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); EndPaint(hWnd, &ps); break; case WM_SIZE: //lidar com o resize da janela //LOWORD(lParam) -> parte menos significativa, largura WaitForSingleObject(hMutex, INFINITE); //problema de sincronização xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); ReleaseMutex(hMutex); //LOWORD(lParam) -> parte mais significativa, altura yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); //a ver com a animação, update limDireito = LOWORD(lParam) - bmp.bmWidth; break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
Esta é a segunda revisão destes conteúdos (desenhar animado e lidar com a concorrência e lidar com a flickering cintilação) com uso do doubleBuffering:
#include <windows.h> #include <tchar.h> #include <windowsx.h> //exercicio 6) LRESULT CALLBACK TrataEventos(HWND, UINT, WPARAM, LPARAM); TCHAR szProgName[] = TEXT("Base"); HBITMAP hBMP; HDC bmpDC; BITMAP bmp; int xBitmap; int yBitmap; int limDireito; HWND hWndGlobalJanela; HANDLE hMutex; //1) lidar com o flickering HDC memDC = NULL; HBITMAP hCopiaBMP; //copiar as caracteristicas DWORD WINAPI MovimentaImagem(LPVOID lParam) { int direcaoMovimento = 1; int saltoPixeis = 2; while (1) { WaitForSingleObject(hMutex, INFINITE); xBitmap = xBitmap + (direcaoMovimento)*saltoPixeis; if (xBitmap <= 0) { xBitmap = 0; direcaoMovimento = 1; } else if (xBitmap >= limDireito) { xBitmap = limDireito; direcaoMovimento = -1; } ReleaseMutex(hMutex); InvalidateRect(hWndGlobalJanela, NULL, FALSE); Sleep(10); } } int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG lpMsg; WNDCLASSEX wcApp; wcApp.cbSize = sizeof(WNDCLASSEX); wcApp.hInstance = hInst; wcApp.lpszClassName = szProgName; wcApp.lpfnWndProc = TrataEventos; wcApp.style = CS_HREDRAW | CS_VREDRAW; wcApp.hIcon = LoadIcon(NULL, IDI_INFORMATION); wcApp.hIconSm = LoadIcon(NULL, IDI_INFORMATION); wcApp.hCursor = LoadCursor(NULL, IDC_ARROW); wcApp.lpszMenuName = NULL; wcApp.cbClsExtra = 0; wcApp.cbWndExtra = 0; wcApp.hbrBackground = CreateSolidBrush(RGB(0, 255, 0)); if (!RegisterClassEx(&wcApp)) return(0); hWnd = CreateWindow( szProgName, TEXT("Estudo para o exame"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, (HWND)HWND_DESKTOP, (HMENU)NULL, (HINSTANCE)hInst, 0); hBMP = (HBITMAP)LoadImage(NULL, TEXT("imagem.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); GetObject(hBMP, sizeof(bmp), &bmp); HDC hdc; hdc = GetDC(hWnd); bmpDC = CreateCompatibleDC(hdc); SelectObject(bmpDC, hBMP); ReleaseDC(hWnd, hdc); RECT rect; GetClientRect(hWnd, &rect); limDireito = rect.right - bmp.bmWidth; hWndGlobalJanela = hWnd; hMutex = CreateMutex(NULL, FALSE, NULL); CreateThread(NULL, 0, MovimentaImagem, NULL, 0, NULL); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while (GetMessage(&lpMsg, NULL, 0, 0)) { TranslateMessage(&lpMsg); DispatchMessage(&lpMsg); } return((int)lpMsg.wParam); } LRESULT CALLBACK TrataEventos(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam) { HDC hdc; RECT rect; PAINTSTRUCT ps; switch (messg) { case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rect); //2) lidar com o flickering if(memDC == NULL) { //primeiro vez que estou a passar pela memoria //criar a cópia memDC = CreateCompatibleDC(hdc); hCopiaBMP = CreateCompatibleBitmap(hdc, rect.right, rect.bottom); SelectObject(memDC, hCopiaBMP); DeleteObject(hCopiaBMP); } //3) usar a copia em memoria e nao na principal //FillRect(hdc, &rect, CreateSolidBrush(RGB(0, 255, 0))); FillRect(memDC, &rect, CreateSolidBrush(RGB(0, 255, 0))); WaitForSingleObject(hMutex, INFINITE); //BitBlt(hdc, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); BitBlt(memDC, xBitmap, yBitmap, bmp.bmWidth, bmp.bmHeight, bmpDC, 0, 0, SRCCOPY); ReleaseMutex(hMutex); //4) escrever a copia em memoria e aplicar na totalidade da janela BitBlt(hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY); EndPaint(hWnd, &ps); break; case WM_SIZE: WaitForSingleObject(hMutex, INFINITE); xBitmap = (LOWORD(lParam) / 2) - (bmp.bmWidth / 2); yBitmap = (HIWORD(lParam) / 2) - (bmp.bmHeight / 2); limDireito = LOWORD(lParam) - bmp.bmWidth; //5) para obrigar a entrar no if no WM_PAINT ReleaseDC(hWnd, memDC); memDC = NULL; ReleaseMutex(hMutex); break; case WM_CLOSE: if ( MessageBox( hWnd, TEXT("tem a certeza que quer sair?"), TEXT("Confirmação"), MB_YESNO | MB_ICONQUESTION) == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return(DefWindowProc(hWnd, messg, wParam, lParam)); break; } return(0); }
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
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..
#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; }
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)"&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(&lpMsg,NULL,0,0)) { TranslateMessage(&lpMsg); // Pré-processamento da mensagem (p.e. obter código // ASCII da tecla premida) DispatchMessage(&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); }
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; }
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; }
#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; }
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; }
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; }
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; }
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; 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);
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; }