CMclSharedMemory

Our shared memory class, which is presented in Examples 7-1 and 7-2, provides a simple wrapper around the Win32 facility to create named regions of memory which are shared between processes. This is a multi-step operation using the API calls. By using the wrapper class, a region of shared memory can be created with one constructor call, passing it the size and name of the shared region. This class provides no thread synchronization for accessing the shared memory; that has to be provided by the programmer. The CMclSharedMemory class can be used as a base class for more complex objects which do provide internal access synchronization, or the class can be used as-is as a member object, and the code that uses the object can take care of the inter-process synchronization. Another class in the CMcl library, CMclMailbox, uses the second method, and creates an internal named mutex to serialize access to the mailbox buffers, which are created with a CMclSharedMemory object.

Example 7-1. CMclSharedMemory.h, the shared memory object header file

//

// FILE: CMclSharedMemory.h

//

// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring

//

/////////////////////////////////////////////////////////////////////////

#ifndef __CMCLSHAREDMEMORY_H__

#define __CMCLSHAREDMEMORY_H__

#include "CMclGlobal.h"

class CMclSharedMemory {

private:

void *m_lpSharedMemory;

HANDLE m_hMapping;

BOOL m_bIsCreator;

DWORD m_dwStatus;

public:

// constructor requires a size with an optional name, it will

// create the shared memory area if it doesn't already exist,

// if it does exist and the requested size is larger than the current

// size, the size of the shared memory area will grow to the requested size...

CMclSharedMemory( LONG lSize, LPCTSTR lpName, LPSECURITY_ATTRIBUTES lpSharedMemoryAttributes = NULL);

// constructor requires just a name, opens the shared memory

// area if it exists, you must check the status of this object to

// see if it succeeded or failed, it will NOT throw an exception if

// the named shared memory area does not exist...

CMclSharedMemory( LPCTSTR lpName);

// destructor is virtual to allow easy use of

// derived classes...

virtual ~CMclSharedMemory();

// query for status after construction if exceptions are disabled...

DWORD Status(void) const;

// basic access function returns a void pointer

// to the shared memory, derived classes can add functions

// to return pointers to higher level structures or structure

// members by first getting the base pointer through this function...

void *GetPtr(void);

// function to tell if this object created the shared memory area...

BOOL IsCreator(void);

};

#endif

Example 7-2. CMclSharedMemory.cpp, the shared memory object implementation file

//

// FILE: CMclSharedMemory.cpp

//

// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring

//

/////////////////////////////////////////////////////////////////////////

#include "CMclSharedMemory.h"

CMclSharedMemory::CMclSharedMemory( LONG lSize, LPCTSTR lpName,

LPSECURITY_ATTRIBUTES lpSharedMemoryAttributes) :

m_lpSharedMemory(NULL),

m_bIsCreator(FALSE),

m_dwStatus(NO_ERROR) {

m_hMapping = ::CreateFileMapping( (HANDLE) 0xFFFFFFFF, lpSharedMemoryAttributes, PAGE_READWRITE, 0, lSize, lpName);

if (CMclIsValidHandle(m_hMapping)) {

m_dwStatus = GetLastError();

if (m_dwStatus == ERROR_ALREADY_EXISTS) {

m_bIsCreator = FALSE;

}

else {

m_bIsCreator = TRUE;

}

}

else {

m_dwStatus = ::GetLastError();

CMclThrowError(m_dwStatus);

}

m_lpSharedMemory = (void *) ::MapViewOfFile( m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

if (m_lpSharedMemory == NULL) {

// unable to map memory into address space, perhaps our address space is full?

// this is a fatal error, close handle and throw exeption

m_dwStatus = ::GetLastError();

CMclThrowError(m_dwStatus);

}

}

CMclSharedMemory::CMclSharedMemory( LPCTSTR lpName) : m_lpSharedMemory(NULL),

m_bIsCreator(FALSE),

m_dwStatus(NO_ERROR) {

m_hMapping = ::OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, lpName);

if (CMclIsValidHandle(m_hMapping)) {

m_dwStatus = NO_ERROR;

}

else {

m_dwStatus = ::GetLastError();

return;

}

m_lpSharedMemory = (void *) ::MapViewOfFile( m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

if (m_lpSharedMemory == NULL) {

// unable to map memory into address space, perhaps our address space is full?

m_dwStatus = ::GetLastError();

}

}

CMclSharedMemory::~CMclSharedMemory() {

if (m_lpSharedMemory) {

::UnmapViewOfFile(m_lpSharedMemory);

m_lpSharedMemory = NULL;

}

// close the file mapping handle...

if (m_hMapping) {

::CloseHandle(m_hMapping);

}

}

DWORD CMclSharedMemory::Status(void) const {

return m_dwStatus;

}

void * CMclSharedMemory::GetPtr(void) {

return m_lpSharedMemory;

}

BOOL CMclSharedMemory::IsCreator(void) {

return m_bIsCreator;

}

Notice that all shared memory objects require a name. To use this class, the users of a region of shared memory need to agree on a name. The first constructor requires a size in addition to the name. This will create a region of the requested size if there is no shared memory region with the given name. Otherwise the Win32 CreateFileMapping call will grow the current region to the given size if it is larger than the current size and return a handle to the file mapping.

The second constructor is like the open calls for the kernel wrapper objects. It will open an existing region by the given name if one exists, but it will fail if no shared region of that name is currently on the system. When using the second constructor, it is important to call the Status member function and check for NO_ERROR. Any other value is a Win32 error code describing why the region could not be opened. Code using the second constructor would look something like this:

CMclSharedMemory cShared("SharedSegmentNamedFoo");

if (cShared.Status()== NO_ERROR) {

// shared memory area named "SharedSegmentNamedFoo"

// exists and has been opened.

// use the shared memory...

}

else {

// shared memory area named "SharedSegmentNamedFoo"

// does not exist and thus could not be opened.

// handle error...

}

Once the CMclSharedMemory object has been created, the user can get a void pointer to the region’s base address with the GetPtr member function. This pointer is only valid until the CMclSharedMemory object is destroyed. After that, the file mapping object is closed and the address may or may not be accessible.

One more small thing to notice. The IsCreator member function is an easy way to check if the current object is the creator of the shared memory region. However, this does not necessarily mean that the creator of the shared memory region has completed any programmer-defined initialization of the shared memory. It simply means that this object is accessing a region of shared address space that it did not initially create. You must prevent clients from accessing regions of shared memory until the creator has properly set it up.