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!
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.