Search code examples
cwinapifile-mapping

named shared memory between two processes


I'm trying to build a client/server in C using file mapping, it's still very early in development but i'm having some trouble understanding how file mapping works exactly.

I create a file mapping of a struct on my server and put some data on it, then my client opens the file mapping a reads the data ok. Then my client writes data for the server to read but the server can't read the clients data and i can't understand why since the file mapping should be synchronized across both processes. I'm still not using event at this stage but i don't think they are required for this to work (are they?)

Her's the code i've got.

Server:

struct _HBACKUPSERVICE{
    DWORD           dwServerProcessID;
    TCHAR           szWork[MAX_PATH];
    HANDLE          hMapFile;
};

PHBACKUPSERVICE pBuf = NULL;
HANDLE hMapFile;

hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,    // use paging file
    NULL,                    // default security
    PAGE_READWRITE,          // read/write access
    0,                       // maximum object size (high-order DWORD)
    sizeof(HBACKUPSERVICE),  // maximum object size (low-order DWORD)
    _T("MyService"));        // name of mapping object

if (/*phBackupService->*/hMapFile == NULL){
    _tprintf(TEXT("Could not create file mapping object (%d).\n"),
        GetLastError());
    return *pBuf;
}

pBuf = (PHBACKUPSERVICE)MapViewOfFile(
    hMapFile,               // handle to map object
    FILE_MAP_ALL_ACCESS,    // read/write permission
    0,
    0,
    sizeof(HBACKUPSERVICE));

if (pBuf == NULL){
    _tprintf(TEXT("Could not map view of file (%d).\n"),
        GetLastError());
    return *pBuf;
}


// Populate backup service structure
pBuf->hMapFile = hMapFile;
pBuf->dwServerProcessID = GetCurrentProcessId();

// Wait for client
do{
    _tprintf(_T("\nServer: Waiting for work."));
    pBuf = (PHBACKUPSERVICE)MapViewOfFile(
        _BackupService.hMapFile,    // handle to map object
        FILE_MAP_ALL_ACCESS,        // read/write permission
        0,
        0,
        sizeof(HBACKUPSERVICE));
    if (StringCbLength(pBuf->szWork, 1 * sizeof(TCHAR), NULL) == S_OK){ Sleep(500); }
} while (StringCbLength(pBuf->szWork, 1 * sizeof(TCHAR), NULL) == S_OK); // ERROR: pBuf->szWork is always empty...

_tprintf(_T("Work from client: %s"), pBuf->szWork);

Client:

HBACKUPSERVICE _BackupService;
HANDLE hMapFile;

hMapFile = OpenFileMapping(
    FILE_MAP_ALL_ACCESS,   // read/write access
    FALSE,                 // do not inherit the name
    _T("MyService"));          // name of mapping object

if (hMapFile == NULL)
{
    _tprintf(TEXT("Could not open file mapping object (%d).\n"),
        GetLastError());
}

BackupService= (PHBACKUPSERVICE)MapViewOfFile(
    hMapFile,               // handle to map object
    FILE_MAP_ALL_ACCESS,    // read/write permission
    0,
    0,
    sizeof(HBACKUPSERVICE));

_tprintf(_T("Server process id: %d"), _BackupService.dwServerProcessID);
_tprintf(_T("send work to server"));
StringCchCopy(_BackupService.szWork, STRSAFE_MAX_CCH, _T("Do work for me!!!!!!!!!!")); //ERROR: the server never sees this

Thanks!


Solution

  • Your server is calling MapViewOfFile() in its reading loop, so you are mapping more and more pointers and not unmapping them. Eventually, you will run out of available addresses to map. Get rid of that. You should be using the pBuf pointer you already obtained from the first MapViewOfFile() before entering the loop. You need to map the view only once.

    Your client is not writing data to the mapped view at all, it is writing to a local HBACKUPSERVICE variable instead of to the mapped view. That is why the server does not see the data.

    Try this:

    Common:

    typedef struct _HBACKUPSERVICE {
        DWORD           dwServerProcessID;
        TCHAR           szWork[MAX_PATH];
        HANDLE          hMapFile;
    } HBACKUPSERVICE, *PHBACKUPSERVICE;
    

    Server:

    PHBACKUPSERVICE pBuf = NULL;
    HANDLE hMapFile;
    
    hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // use paging file
        NULL,                    // default security
        PAGE_READWRITE,          // read/write access
        0,                       // maximum object size (high-order DWORD)
        sizeof(HBACKUPSERVICE),  // maximum object size (low-order DWORD)
        _T("MyService"));        // name of mapping object
    
    if (hMapFile == NULL){
        _tprintf(TEXT("Could not create file mapping object (%d).\n"),
            GetLastError());
        return NULL;
    }
    
    pBuf = (PHBACKUPSERVICE)MapViewOfFile(
        hMapFile,               // handle to map object
        FILE_MAP_ALL_ACCESS,    // read/write permission
        0,
        0,
        sizeof(HBACKUPSERVICE));
    
    if (pBuf == NULL){
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());
        return NULL;
    }
    
    // Populate backup service structure
    pBuf->hMapFile = hMapFile;
    pBuf->dwServerProcessID = GetCurrentProcessId();
    ZeroMemory(pBuf->szWork, sizeof(pBuf->szWork));
    
    // Wait for client
    _tprintf(_T("\nServer: Waiting for work."));
    while (pBuf->szWork[0] == 0){ Sleep(500); }
    
    _tprintf(_T("Work from client: %s"), pBuf->szWork);
    

    Client:

    PHBACKUPSERVICE BackupService = NULL;
    HANDLE hMapFile;
    
    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        FALSE,                 // do not inherit the name
        _T("MyService"));          // name of mapping object
    
    if (hMapFile == NULL)
    {
        _tprintf(TEXT("Could not open file mapping object (%d).\n"),
            GetLastError());
    }
    
    BackupService = (PHBACKUPSERVICE)MapViewOfFile(
        hMapFile,               // handle to map object
        FILE_MAP_ALL_ACCESS,    // read/write permission
        0,
        0,
        sizeof(HBACKUPSERVICE));
    
    if (BackupService == NULL){
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());
    }
    
    _tprintf(_T("Server process id: %d"), BackupService->dwServerProcessID);
    _tprintf(_T("send work to server"));
    StringCchCopy(BackupService->szWork, MAX_PATH, _T("Do work for me!!!!!!!!!!"));
    

    Lastly, TCHAR is dangerous for interop across process boundaries. Imagine what would happen if an ANSI app tried to communicate with a UNICODE app. They would not agree on the format of your szWork field, and thus not agree on the byte size of your HBACKUPSERVICE structure. You should replace TCHAR with CHAR or WCHAR instead, depending on your needs, and be consistent with it on both ends.