Click here to Skip to main content
Click here to Skip to main content

Shared memory

By , 28 Sep 2002
 

Motivation

Sometimes you need to share some values across several processes. There are some ways, provided by system, but there is no simple way to share simple values like DWORDs or strings. And of course, synchronization is also required in this case.

Description of solution

I was inspired by PJ Naughter's solution, but I wanted more. I wanted structured shared memory, with variables, synchronization and wait functionality within it.

Internal structure

Variables are shared within an unnamed memory file. This 'file' (or better, block of memory) is allocated in the page file. It is not possible to create growable file of this type, but I did not require it. The internal structure of this memory is as follows:

DWORD
DWORD 
ValueHeader[0]
value 0 
ValueHeader[1]
value 1 
..............
ValueHeader[n]
value n

The first DWORD is the number of values within memory. The second DWORD is the memory size; it includes these two DWORDs too. ValueHeader is a structure defined as:

typedef struct _tagValueHeader {
	WCHAR wszValueName[VAR_NAME_LENGTH];
	DWORD dwLength; 
} ValueHeader;

Member wszValueName is value name, dwLength is length in bytes of the value, which follows this structure. As you can see, the actual value is stored as several bytes. There is no type checking of the value's type. It is not allowed access the variable directly. Instead of that, the class provides several methods to access values. It allows synchronized access, and it is possible to wait for value or memory changes.

CSharedMemory public class members

Construction and destructions
CSharedMemory
~CSharedMemory

General methods
GetMemName
IsCreated
GetMemSize
AmIFirst
GetLastError

Access control methods
SetSdMem
GetSdMem
SetSdSem
GetSdSem
SetSaEvent

Value management methods
AddValue
AddDwordValue
DeleteValue
ExistValue
GetVariablesCount
GetValueInfo

Value access methods
SetValue
GetValue

Wait methods
WaitForMemChange
WaitForValueChange
WaitForMultipleValuesChanges

Interlocked* methods
InterlockedIncrement
InterlockedDecrement
InterlockedExchange
InterlockedTestExchange
InterlockedCompareExchange
InterlockedExchangeAdd

Direct memory access methods
Read
Write

DEBUG methods
AssertValid
Dump

CSharedMemory::CSharedMemory

CSharedMemory(TCHAR *szName, DWORD dwSize = DEF_SHARED_SIZE, PINITMEMORY InitMemoryProcedure = NULL, void *pInitProcParam = NULL, LPSECURITY_ATTRIBUTES lpsaAttributes = NULL);

Remarks

Constructor for the class. It creates shared memory named szName, of dwSize< size. When you want initialize memory at the same place as you declare it, you can define an initializer function, defined as:

void InitMemory(CSharedMemory *pMem, void *pInitProcParam)

And you can pass its address to the constructor. This solution allows you write shorter code like:

...
CSharedMemory sh_mem("MemoryName",1024,InitMemory);
...

'Initialize' means, for example to add some variables. If you need to pass your own initialization value, then you can specify it in the pInitProcParam parameter. If you don't provide this value at the construction time, then constructor will pass NULL to your initializer function. lpsaAttributes defines access control rights and security attributes of the memory mapped file, semaphore and events created inside the CSharedMemory object. For more information see SECURITY_ATTRIBUTES.

CSharedMemory::~CSharedMemory

~CSharedMemory();

Remarks

Destructor for the class. It frees all allocated resources.

CSharedMemory::GetMemName

CString GetMemName(void);

Remarks

Returns name of the shared memory.

CSharedMemory::IsCreated

BOOL IsCreated(void);

Remarks

Returns TRUE if memory was successfully created.

CSharedMemory::GetMemSize

DWORD GetMemSize(void);

Remarks

Returns size of memory.

CSharedMemory::AmIFirst

BOOL AmIFirst(void);

Remarks

Returns TRUE, if caller was first, who created memory.

CSharedMemory::GetLastError

DWORD GetLastError(void);

Remarks

Returns a Win32 error code that describes the best last error inside a call made to the object.

CSharedMemory::SetSdMem

BOOL SetSdMem(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor);

Remarks

This method sets the security descriptor for memory mapped file. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve error code. See also SetKernelObjectSecurity().

CSharedMemory::GetSdMem

BOOL GetSdMem(SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, LPDWORD lpnLengthNeeded);

Remarks

This method gets the security descriptor of the memory mapped file. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve error code. See also GetKernelObjectSecurity().

CSharedMemory::SetSdSem

BOOL SetSdSem(SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor);

Remarks

This method sets the security descriptor of the semaphore, which synchronizes access to shared memory. If it fails, returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code. See also SetKernelObjectSecurity().

CSharedMemory::GetSdSem

BOOL GetSdSem(SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD nLength, LPDWORD lpnLengthNeeded);

Remarks

This method gets security descriptor of the semaphore, which synchronizes access to shared memory. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code. See also GetKernelObjectSecurity().

CSharedMemory::SetSaEvent

BOOL SetSaEvent(LPSECURITY_ATTRIBUTES lpsaAttributes);

Remarks:

This method sets internal variable to security attributes, which are used to protect internal events object. These events are used to 'wait' for changes in the memory or variable. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve error code.

CSharedMemory::AddValue

BOOL AddValue(const TCHAR *szName, DWORD size, void *pDefaultData = NULL);

Remarks

This method adds new value named szName. It creates a new entry within memory and reserves space of size for new value. If it fails, returns FALSE, otherwise TRUE. You can use the last parameter to set its value after it is created. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::AddDwordValue

BOOL AddDwordValue(const TCHAR *szName, DWORD dwDefault = 0);

Remarks

Method adds new DWORD value named szName. It creates a new entry within memory and reserves space of size for new value. If it fails, it returns FALSE, otherwise TRUE. You can use last parameter to set its value after it is created. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::DeleteValue

BOOL DeleteValue(TCHAR *szName);

Remarks

This method deletes the value named szName from memory, it frees used space and freed memory is filled with zeros. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve error code.

CSharedMemory::ExistValue

BOOL ExistValue(TCHAR *szName);

Remarks

This method returns TRUE, if variable szName exists within memory. If it fails, returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code (when constructor failed, this method returns FALSE, but CSharedMemory::GetLastError() returns a different error code).

CSharedMemory::GetVariablesCount

DWORD GetVariablesCount(void);

Remarks

This method returns the number of values stored in the memory. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::GetValueInfo

BOOL GetValueInfo(DWORD dwIndex, ValueHeader *pVarInfo);

Remarks

This method copies value information of the format ValueHeader to memory pointed by pVarInfo. If it fails, returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::SetValue

BOOL SetValue(TCHAR *szName, void *bData, DWORD dwLength);

Remarks

This method sets the variable named szName to value pointed to by bData. Length of the data at address bData is passed as the last parameter. It can be less then allocated length within shared memory. In this case the rest of the allocated buffer is filled with zeros. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::GetValue

BOOL GetValue(TCHAR *szName, void *bData, LPDWORD dwLength);

Remarks

This method retrieves thevalue of the variable szName. Data is stored at address bData. The length stored at bData is returned in dwLength. It is possible to pass in bData NULL, then the method returns in dwLength the required buffer length. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError()to retrieve the error code.

CSharedMemory::WaitForMemChange

DWORD WaitForMemChange(DWORD dwMilliseconds = INFINITE);

Remarks

This method waits for any change in the structure of the shared memory. 'Change' means add or delete a variable. When you do not use variables, but direct access functions, then you can use this method to wait for the changes. For direct access see CSharedMemory::Read() or CSharedMemory::Write(). If it fails, it returns WAIT_FAILED value, see also WaitForSingleObject() .

CSharedMemory::WaitForValueChange

DWORD WaitForValueChange(TCHAR *szName, DWORD dwMilliseconds = INFINITE);

Remarks

This methods waits for changes of value. It also returns, when a variable is deleted. If it fails, itreturns WAIT_FAILED value, see also WaitForSingleObject().

CSharedMemory::WaitForMultipleValuesChanges

DWORD CSharedMemory::WaitForMultipleValuesChanges(CStringArray & str,BOOL bWaitForAll , DWORD dwMilliseconds =INFINITE );

Remarks

This methods waits for changes of values. It also returns when variables are deleted. You can choose to wait for change of one or several values. If it fails, it returns WAIT_FAILED value, see also WaitForMultipleObject().

All Interlocked* functions are described in the MSDN. Methods of CSharedMemory work the same way, except they return TRUE/FALSE as an indicator of success, and the return value of emulated functions is returned through a pointer (usually the last optional parameter).

CSharedMemory::InterlockedIncrement

BOOL InterlockedIncrement(TCHAR *szName, LPLONG plNewVal = NULL);

Remarks

See also InterlockedIncrement().

CSharedMemory::InterlockedDecrement

BOOL InterlockedDecrement(TCHAR *szName, LPLONG plNewVal = NULL);

Remarks

See also InterlockedDecrement().

CSharedMemory::InterlockedExchange

BOOL InterlockedExchange(TCHAR *szTargetName, LONG lNewVal, LPLONG plPrevValue = NULL);

Remarks

See also InterlockedExchange().

CSharedMemory::InterlockedTestExchange

BOOL InterlockedTestExchange(TCHAR *szTargetName, LONG lOldValue, LONG lNewValue, LPLONG plPrevValue = NULL);

Remarks

See also InterlockedTestExchange().

CSharedMemory::InterlockedCompareExchange

BOOL InterlockedCompareExchange(TCHAR *szTargetName, LONG lExchange, LONG lComperand, LPLONG plIntiVal = NULL);

Remarks

See also InterlockedCompareExchange().

CSharedMemory::InterlockedExchangeAdd

BOOL InterlockedExchangeAdd(TCHAR *szTargetName, LONG lIncrement, LPLONG plIntiVal = NULL);

Remarks

See also InterlockedExchangeAdd().

CSharedMemory::Read

BOOL Read(BYTE *pbData, DWORD dwLength, DWORD dwOffset = 0);

Remarks

This method reads dwLength bytes from offset dwOffset within memory. It is allowed to access shared memory this way only when it contains no variables. Method writes data at the address pbData. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::Write

BOOL Write(BYTE *pbData, DWORD dwLength, DWORD dwOffset = 0);

Remarks

Method writes dwLength bytes from pbData at the offset dwOffeset within the shared memory. It is allowed to access shared memory this way only when it contains no variables. If it fails, it returns FALSE, otherwise TRUE. Call CSharedMemory::GetLastError() to retrieve the error code.

CSharedMemory::AssertValid

void AssertValid(void);

Remarks

This methof performs a validity check on this object by checking its internal state.

CSharedMemory::Dump

void Dump(CDumpContext & dc);

Remarks

Dumps the contents of shared memory to a CDumpContext object.

Usage

I have provided a simple demo program to demonstrate usage of the CSharedMemory class. A shared block of the memory exists within the system until the last instance of the CSharedMemory class is destroyed. CSharedMemory is UNICODE enabled; it will work in UNICODE or ANSI programs. It is possible to share the same memory from both types of programs. CSharedMemory uses simple diagnostic functions from Helpers.h and Helpers.cpp

Demo program

CSHaredMemory demo program

The demo program provides an interface to the many methods of CSharedMemory, and you can use more instances of it to play with CSharedMemory and see how it works. It also demonstrates usage of the class.

History

  • 4 Dec 2001 - updated source code
  • 13 Jan 2001 - updated source code and new constructor
  • 24 Sep 2002 - new and improved functionality from Angela Aremu (AddDwordValue(), WaitForMultipleValuesChanges() and new parameter in AddValue())

Credits

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Martin Ziacek
Software Developer (Senior)
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralShared memory reallocationmemberteeekay14 Apr '05 - 11:29 
Hi,
 
not sure if this is the right forum to ask this, but perhaps someone can calrify this one for me.
 
I have a shared memory implementation similar to the one outlined here. I'm sharing data between instances of a DLL mapped into different processes.
 
So I create/open a named shared memory segment, get a handle, and then use the handle to MapViewOfFile and get a pointer to the beginning of the memory segment.
 
Now suppose I need to reallocate the amount of space that's shared in one of the instances of this DLL.
 
Would calling realloc on the pointer to the beginning of shared memory segment suffice? Since the value of this pointer could be different in every process, it seems to me that I would need to re-MapViewOfFile in all other instances, or does the Windows HANDLE abstraction take care of this for me.
 
Any insight would be awesome!
 
Thanks,
 
Timur
Generalnon mfc version for .net addinmemberjeffstheman502 Dec '04 - 20:41 
would this be easy to do?
 
thanks
GeneralNo Readers/Writers Lock support!membercjim26 Jul '04 - 18:42 
I examine your function "SetValue" and "GetValue", and you use a CSingleLock to lock the value before read and write, a better way is to support multiple readers and single writers, viz. Readers/Writers problem.
Big Grin | :-D
GeneralAlso work with picturesmemberMorrowyn27 Nov '03 - 22:08 
Hi,
 
Im wondering does, this also work with pictures and other files?
 
greetz
GeneralblockingmemberWaHaha23 Jun '03 - 4:45 
when I use the class in two process A,B,and I use two shared variable dwA and dwB in the shared memory,the blocking happend.
 
the pseudo codes just like the following
 
first I run process A
process A:
while(true)
{
waitforvaluechage("dwA");
if(dwA==0xff)
{
setvalue("dwB",0xFF);
break;
}
setvalue("dwB",0x12);
}
then I run process B
process B:
 
int count=0;
while(true)
{
setvalue("dwA",count);
waitforvaluechange("dwB")
if(dwB==0xff)
break;
count ++;
}
 
when i run about 10 times,the prg blocking.how can i do?
GeneralRe: blockingsussAnonymous16 Aug '03 - 0:42 
Hi,
 
Your assuming the following:
 
process A Runs..
Process A Waits for change (dwA)
Process B Runs..
Process B changes (dwA)
Process B waits for change (dwB)
Process A resumes..
Process A changes (dwB)
etc..
 
But what could possibly happen is:
 
process A Runs..
Process A Waits for change (dwA)
Process B Runs..
Process B changes (dwA)
OS Scheduler switches to process A
Process A resumes..
Process A changes (dwB)
Process A Waits for change (dwA)
Process B waits for change (dwB)
DEADLOCK
GeneralGreat but.... in win95 there is something wrongmemberjmr00719 Jun '03 - 3:03 
This is a wonderfull code, the only thing I'll change would be the way you detect some versions of win95 (becose not work)
 
In the constructor:
 

...
....
TRACE(_T("osvi.dwMajorVersion = %d\n"),osvi.dwMajorVersion);
TRACE(_T("osvi.dwMinorVersion = %d\n"),osvi.dwMinorVersion);
 
if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) //new line Smile | :)
{
if (osvi.dwMajorVersion >= 3) {
if ((osvi.dwMinorVersion == 51) ||
(osvi.dwMinorVersion == 0) ||
(osvi.dwMinorVersion == 1))
{
m_bSecPres = TRUE;
}
}
}

GeneralSomething odd happenedmemberBin10 Oct '02 - 5:48 
It works great on WIndows2000/XP, but on Windows98, sometimes, I mean seldom, the shared memory gets trespassed by other process, or say that the memory contents get altered with no valid reason. For example, I have process A and process B using the shared memory, none of them did any change to the shared memory, but after a while some of the memory contents get modified mystically.
 
This never happened on Windows 2000/XP, but did from time to time on Windows9x. My 2c, do not use system page file, use a private disk file instead, to do so, give a valid file handle value to the first parameter of "CreateFileMapping".
GeneralRe: Something odd happenedmemberMartin Ziacek30 Oct '02 - 23:10 
Friend of mine uses this class on Win95 and has no problems with altered memory contents.
 
I designed this class only for WinNT like systems and support for different versions has been added later only because of very small and not important project.
 
I think your suggestion about using file for sharing memory on Win95 may help, but the real problem lies somewhere else - you are running process which damages that memory accidentally (assuming from your description).

 
Martin
 
--------------------------------------------
C'mon we all know computers are experimental devices and should only be used for playing games.
Using them for alternative stuff like business, is clearly not using them for what they are intended.
 
Colin Davies
GeneralUse CArchive with this classmemberTed Christiansen20 Sep '02 - 8:20 
Hello,
 
Is there any way to use CArchive with this class? It would be slick to be able to Serialize data in in one process and Serialize it out in another.
 
Ted
GeneralRe: Use CArchive with this classmemberMartin Ziacek30 Oct '02 - 23:41 
There is no direct way to use CArchive with this class, however you can use 'Direct memory access methods' to store any data at any offset in the shared memory.

 
Martin
 
--------------------------------------------
C'mon we all know computers are experimental devices and should only be used for playing games.
Using them for alternative stuff like business, is clearly not using them for what they are intended.
 
Colin Davies
QuestionDoes it support non-MFC?memberjeasonzhao10 Mar '02 - 21:14 
I have not compile it myself,sorry!Big Grin | :-D
 
Please pardon my weak English!
AnswerRe: Does it support non-MFC?memberMartin Ziacek11 Mar '02 - 7:32 
Hi,
 
it does not support non-MFC, however, you might be able to cut out all referencies to MFC and replace 'MFC parts' with SDK versions.
 
Regards,
 
Martin

GeneralRe: Does it support non-MFC?memberjeasonzhao11 Mar '02 - 15:03 
Ok,I am trying!
Thanks a lot!
 
Please pardon my weak English!
GeneralRe: Does it support non-MFC?memberBrankoL30 Aug '02 - 10:31 
Send me your non-MFC version when you're done.]
 
It would be nice to publish it here as well. Many people would like non MFC version.
 

Thanks.
Sleepy | :zzz:
GeneralLooks Great, But I Cant Compile ..memberGarth J Lancaster24 Jan '02 - 13:50 
Hiyah from Sydney, Australia
 
this looks great, but Im having trouble compiling with it - I get C2501 'missing storage class or type specifiers' for each of the 'CSemaphore *m_pSync' and 'CSingleLock *m_pLock' declarations in SharedMemory.h (and it goes downhill from there)
 
My guess would be that I need to tell MFC to include Multithreaded support (I would likely do the same if I was on my other dev platform, Sun Solaris) .. but Im not sure what this means on MFC ...
 
does it mean that I have to :-
 
1) #include <afxmt.h>
2) (where ??) StdAfx.h ???
 
sorry, thats probably a real basic question, maybe by the time Ive spent some time tomorrow (Saturday) on it, I'll have the answer myself ... (just need some backup/reassurance )
 
ps ... (1) #include <afxmt.h> was gleaned from a quick web-scan .. I'll post back here when Ive tried it - one of the problems with where I live is, its GMT +10. This is well ahead of most of you in the USA, so I like to try and get questions out there early, so as maybe by the next day I'll have answers ...
 
Thanks for a great job in the classes, Im learning a lot here ..
 
Garth Blush | :O
GeneralRe: Looks Great, But I Cant Compile ..memberGarth J Lancaster24 Jan '02 - 16:40 
Well, looks like I did manage to answer my own question - sorry if anyone was inconvenienced by this ..
 
the solution was as per :-
 
add a "#include <afxmt>" in StdAfx.h (with all the other 'Afx' includes)
 
I wonder if there's a way of indicating 'Multithreading support required' when one is using the wizard to start building an MFC program, but its a moot point now ..
 
Garth Laugh | :laugh:
GeneralCallback in constructormemberTim Lesher4 Dec '01 - 7:40 
It's great that you have a callback in your constructor to initialize the memory at allocation time. You might want to make this callback more C++ friendly either by adding a "user parameter" (an opaque void *) that is passed to the callback. That way, if my CFoo class is creating the shared memory, I can do this:
 
void CFoo::MakeMemory()
{
    CSharedMemory* pNewBlock = new CSharedMemory("name", 1024, CFoo::Initializer, this);
}
 
/*static*/ void CFoo::Initializeer(CSharedMemory* pMem, void* p)
{
    CFoo* This = (CFoo*) p;
    // "This" now has access to my CFoo object
}
Alternatively, if you want to go the "real" OO C++ route, you can make the callback a functor.
 

 
--
Tim Lesher
http://www.lesher.ws
GeneralRe: Callback in constructormemberMartin Ziacek4 Dec '01 - 10:58 
Tim,
 
Thank you very much for your comment, I thought my CSharedMemory was enough C++ enabled Smile | :) , but probably I was wrong Cry | :(( .
 
It is a lovely idea to pass one more parameter to callback function, however it has nothing to do with C++ at all. I have not done it in my code just because I have never needed to do so.
 
Nevertheless I can imagine that you could be happy with this functionality, therefore I sent to Chris updated article with your suggestion.
 
But there is no functor in the whole article, frankly, I see no reason to use "real C++" for such a simple thing as couple of variables somewhere in the memory Confused | :confused: .
 
Kind regards,
 
Martin

GeneralRe: Callback in constructormemberTim Lesher5 Dec '01 - 4:00 
Martin Ziacek wrote:
It is a lovely idea to pass one more parameter to callback function, however it has nothing to do with C++ at all.
 
Actually, it is a C++ issue, because it's the only way to get a C-style callback to call an object method (as opposed to a standalong function or a static method).
 
It seems like a minor thing, until you run across one of the few callbacks in the Windows API (SetTimer) that doesn't let you pass a user parameter, and you have to resort to hackery to get an object method called in response to it.
 

 
Tim Lesher
http://www.lesher.ws

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 29 Sep 2002
Article Copyright 2001 by Martin Ziacek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid