Search code examples
c++windowswinapimemory-mapped-files

MapViewOfFile() Returns only the Last Word in the Buffer (See the Example)


I wrote a simple inter-process communication with a memory-mapped file. The code works relatively well, but I have a problem with the buffer that I'll explain shortly. Here is the code (C++, Windows):

#define UNICODE
#define _UNICODE

#include <iostream>
#include <tchar.h>
#include <Windows.h>

int wmain(int argc, wchar_t** argv)
{
    if (argc != 2)
    {
        std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
        return -1;
    }

    HANDLE hMapFile;
    HANDLE hEvent;
    HANDLE isOpened = CreateEvent(NULL, true, false, L"IsOpened"); // To check if a `win32mmap w` runs

    if (wcscmp(argv[1], L"w") == 0)
    {
        SetEvent(isOpened);

        hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"mmapFile");
        if (hMapFile == NULL)
        {
            std::cout << "CreateFileMapping() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        hEvent = CreateEvent(NULL, true, false, L"mmapEvent");
        if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL)
        {
            std::cout << "CreateEvent() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        char* buff = (char*)MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0);
        if (!buff)
        {
            std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        while (buff[0] != L'.')
        {
            std::cin >> buff;
            SetEvent(hEvent);
        }

        UnmapViewOfFile(buff);
    }

    else if (wcscmp(argv[1], L"r") == 0)
    {
        if (WaitForSingleObject(isOpened, 0) == WAIT_TIMEOUT)
        {
            std::cout << "Waiting for `win32mmap w`...";
            WaitForSingleObject(isOpened, INFINITE);
            std::cout << "\n";
        }

        hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, L"mmapFile");
        if (hMapFile == NULL)
        {
            std::cout << "CreateFileMapping() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        hEvent = OpenEvent(EVENT_ALL_ACCESS, false, L"mmapEvent");
        if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL)
        {
            std::cout << "CreateFile() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        char* buff = (char*)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
        if (!buff)
        {
            std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        if (!buff)
        {
            std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
            return GetLastError();
        }

        while (true)
        {
            WaitForSingleObject(hEvent, INFINITE);
            ResetEvent(hEvent);

            if (buff[0] == '.')
            {
                break;
            }

            std::cout << buff << "\n";
        }

        UnmapViewOfFile(buff);
    }

    else
    {
        std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
        return -1;
    }

    CloseHandle(hMapFile);

    return 0;
}

The program is a simple inter-process communication "chat" that relies on memory-mapped files. To use the program, you need to make two executable instance of the program: win32mmap w and win32mmap r. The first instance is used to type text that is displayed in the second instance. When you type . in the first instance, both of them are terminated.

My problem is when I run the 2 instances of the program, and I type the world Hello in the first instance (win32mmap w), the second instance shows Hello as expected. But when I type Hello World in the first instance, the second instance shows only the word World instead of Hello World. How can I fix the code that the buffer will get the whole text?


Solution

  • Your writer is not waiting for the reader to consume the data before overwriting it with new data.

    You need 2 events - one for the reader to wait on signaling when the buffer has data to read, and one for the writer to wait on signaling when the buffer needs data.

    Try this instead:

    #define UNICODE
    #define _UNICODE
    
    #include <iostream>
    #include <tchar.h>
    #include <Windows.h>
    
    const DWORD BufSize = 1024;
    
    int wmain(int argc, wchar_t** argv)
    {
        if (argc != 2)
        {
            std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
            return -1;
        }
    
        HANDLE hMapFile;
        char* buff;
    
        HANDLE hNeedDataEvent;
        HANDLE hHasDataEvent;
        DWORD dwError;
    
        HANDLE isOpened = CreateEvent(NULL, TRUE, FALSE, L"IsOpened"); // To check if a `win32mmap w` runs
        if (isOpened == NULL)
        {
            dwError = GetLastError();
            std::cout << "CreateEvent() Error: " << dwError << "\n";
            return dwError;
        }
    
        if (wcscmp(argv[1], L"w") == 0)
        {
            hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BufSize, L"mmapFile");
            if (hMapFile == NULL)
            {
                dwError = GetLastError();
                std::cout << "CreateFileMapping() Error: " << dwError << "\n";
                SetEvent(isOpened);
                return dwError;
            }
    
            buff = (char*) MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, BufSize);
            if (!buff)
            {
                dwError = GetLastError();
                std::cout << "MapViewOfFile() Error: " << dwError << "\n";
                SetEvent(isOpened);
                return dwError;
            }
    
            hNeedDataEvent = CreateEvent(NULL, TRUE, TRUE, L"mmapNeedDataEvent");
            if (hNeedDataEvent == NULL)
            {
                dwError = GetLastError();
                std::cout << "CreateEvent() Error: " << dwError << "\n";
                SetEvent(isOpened);
                return dwError;
            }
    
            hHasDataEvent = CreateEvent(NULL, TRUE, FALSE, L"mmapHasDataEvent");
            if (hHasDataEvent == NULL)
            {
                dwError = GetLastError();
                std::cout << "CreateEvent() Error: " << dwError << "\n";
                SetEvent(isOpened);
                return dwError;
            }
    
            SetEvent(isOpened);
    
            while (WaitForSingleObject(hNeedDataEvent, INFINITE) == WAIT_OBJECT_0)
            {
                std::cin.get(buff, BufSize);
    
                ResetEvent(hNeedDataEvent);
                SetEvent(hHasDataEvent);
    
                if (buff[0] == L'.') break;
            }
        }
    
        else if (wcscmp(argv[1], L"r") == 0)
        {
            if (WaitForSingleObject(isOpened, 0) == WAIT_TIMEOUT)
            {
                std::cout << "Waiting for `win32mmap w`...";
                WaitForSingleObject(isOpened, INFINITE);
                std::cout << "\n";
            }
    
            hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, L"mmapFile");
            if (hMapFile == NULL)
            {
                dwError = GetLastError();
                std::cout << "CreateFileMapping() Error: " << dwError << "\n";
                return dwError;
            }
    
            char* buff = (char*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, BufSize);
            if (!buff)
            {
                dwError = GetLastError();
                std::cout << "MapViewOfFile() Error: " << dwError << "\n";
                return dwError;
            }
    
            hNeedDataEvent = OpenEvent(SYNCHRONIZE, FALSE, L"mmapNeedDataEvent");
            if (hNeedDataEvent == NULL)
            {
                dwError = GetLastError();
                std::cout << "OpenEvent() Error: " << dwError << "\n";
                return dwError;
            }
    
            hHasDataEvent = OpenEvent(SYNCHRONIZE, FALSE, L"mmapHasDataEvent");
            if (hHasDataEvent == NULL)
            {
                dwError = GetLastError();
                std::cout << "OpenEvent() Error: " << dwError << "\n";
                return dwError;
            }
    
            do
            {
                SetEvent(hNeedDataEvent);
    
                if (WaitForSingleObject(hHasDataEvent, INFINITE) != WAIT_OBJECT_0)
                    break;
    
                std::cout << buff << "\n";
    
                ResetEvent(hHasDataEvent);
            }
            while (buff[0] != '.');
        }
    
        else
        {
            std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
            return -1;
        }
    
        UnmapViewOfFile(buff);
        CloseHandle(hMapFile);
        CloseHandle(hNeedDataEvent);
        CloseHandle(hHasDataEvent);
        CloseHandle(isOpened);
    
        return 0;
    }