Shared memory segment in a C++ Class Library (DLL) for .NET applications

Environment: Visual C++ (VS 2008) .NET   (Show Win32 C++ Example)

Applications sometimes need to share memory for interprocess communication on the same computer.  This example provides a managed code C++ .NET Class Library that can be consumed from .NET applications for interprocess communications. This example is coded in VS 2008, and a VC6 Win32 example can be found here. The following example defines a .SHAREDMEMORY data segment that can be shared across multiple process on the same computer.  This is accomplished by setting the _Xml buffer from an access method, and retrieving the XML document buffer from another application by calling the same access method. A module scoped named Mutex is used to gain a lock the shared memory segment so that each process consuming the DLL is granted exclusive read/write access.

Using the #pragma data_seg (".SHAREDMEMORY") you must make sure that the variables declared in the segment are initialized when they are declared.  During construction of the CSharedMemory class the variables are set to whatever values you require to begin using the object.  The shared memory segment will only be initialized when when the first instance of the object is created because _Initialized will be set to true when the next application creates an instance of the object.

While there are other ways to share memory between applications like Memory Mapped Files, simply sharing a common storage buffer is sufficient for many applications.  An XML document (or whatever you want) can be used to transfer data back and forth between processes.  This example stores string data but the access methods and buffer can be defined to store BYTE data if you just want to store raw data.

To download this project click here (VS 2008 SharedMemory.zip) or you can download the compiled dll class library (32K buffer size) by clicking here (Compiled Class Library DLL).

First the C++ header file that defines the shared memory buffer and access methods for getting and setting the data. An important item to note is the XML_BUFFER_SIZE definition, and in this case it is set at 32K. This needs to be defined large enough to accomodate the largest document that you might pass between applications. Shared memory segments must be defined when declared to insure a block of memory is available for your applications, so you may need to set this value to your needs before compiling the dll.


// SharedMemory.h

// Code provided as is with no warranty or support

#pragma once

#include < string.h >
 
using namespace System::Threading;
using namespace System;
using namespace System::Runtime::InteropServices;

namespace SharedMemory {

    
	/* Define the shared memory segment **************************************************/
	//define the largest required document size, memory must be defined and initialized
	//when declared
    
    
	#define XML_BUFFER_SIZE 32769	// 32K + 1 buffer size
    

	#pragma data_seg (".SHAREDMEMORY")
		bool	_Initialized = false;	
		char	_Xml[XML_BUFFER_SIZE] = {0};
	#pragma data_seg() 

	#pragma comment(linker,"/SECTION:.SHAREDMEMORY,RWS")
	
    
	/* DLL Interface ********************************************************************/
	public ref class CSharedMemory
	{
	protected: //Data
	
		//Named mutex for locking read/write access to memory across all processes
		static Mutex^ _Lock = gcnew Mutex(false, "CSharedMemory-19B33A40-CEDA-477e-A688-504C105256E6");
	
	public: //Methods
	
		CSharedMemory();
		
		bool       SetData(String^ data, bool autoLock);
		String ^   GetData(bool clearOnRead, bool autoLock);
		void       Lock();
		void       UnLock();
		const int  BufferSize();
	};
	
}

Now the CPP file containing the implementation of this class. Data is shared using the GetData() and SetData() access methods. You can explicitly set a lock on the memory segment by calling the .Lock() method prior to calling GetData() or SetData(). Once you get/set data you must call the .Unlock() method if you called the .Lock() method. You can also set autoLock parameter = true when calling these access methods and the lock will be supplied automatically during the call.

Important: If you explicitly call .Lock() and .Unlock() before and after using the access methods you must set the autoLock parameter = false.

Type Method/Property Description
Method .SetData() Sets data to the shared memory segment. (String)
Method .GetData() Gets data from shared memory segment (String)
Method .Lock() Locks the shared memory segment for exclusive access.
Method .UnLock() Releases the lock on the shared memory segment.
Method .BufferSize() Gets size of buffer as defined

#include "SharedMemory.h"

namespace SharedMemory {

	
	//Code is provided as is with no warranty or support

	//Construction 
	CSharedMemory::CSharedMemory()
	{
		//first consumer of this class initializes the shared memory
		if(!_Initialized)
		{
			_Lock->WaitOne();

			memset(_Xml, 0, XML_BUFFER_SIZE);
			_Initialized = true;

			_Lock->ReleaseMutex();
		}
	}
	
	/**************************************************************************
	* SetData()	Sets data to shared memory segment
	*
	* PARAMS	[string] data - string data to set to shared memory segment
	* PARAMS	[bool] autoLock - locks shared memory if true
	* RETURNS	[bool] true if successful
	**************************************************************************/
	bool CSharedMemory::SetData(String^ data, bool autoLock)
	{
		bool result = true;

		if(autoLock)
		{
			//wait for a lock on the memory segment
			_Lock->WaitOne();	
		}
        
		//clear buffer
		memset(_Xml, 0, XML_BUFFER_SIZE); 
		
		//make sure our string isn't larger than buffer
		if(data->Length < XML_BUFFER_SIZE)  
		{
			//copy the data into the shared memory segment
			memcpy((char*)_Xml, (char*)Marshal::StringToHGlobalAnsi(data).ToPointer(), data->Length);
		}
		else
		{
			result = false;
		}
		
		if(autoLock)
		{
			//release lock on memory segment
			_Lock->ReleaseMutex();
		}

		return result;
	}
	
	/**************************************************************************
	* GetData()	Returns data in shared memory segment
	* 
	* PARAMS	[bool] clearOnRead - if true clears buffer on read
	* PARAMS	[bool] autoLock - locks shared memory if true
	* RETURNS	[String] data currently in buffer
	**************************************************************************/
	String^ CSharedMemory::GetData(bool clearOnRead, bool autoLock)
	{
		if(autoLock)
		{
			//wait for a lock on the memory segment
			_Lock->WaitOne();
		}
		
		//copy the data from the shared memory segment into a new string
		String^ data = gcnew String(_Xml);
		
		if(clearOnRead)
		{
			memset(_Xml, 0, XML_BUFFER_SIZE); 
		}

		if(autoLock)
		{
			//release lock on memory segment
			_Lock->ReleaseMutex();
		}

		return data;
	}
	
    
	/**************************************************************************
	* Lock()	Manually locks queue across all instances of consumers
	*					
	**************************************************************************/
	void CSharedMemory::Lock()
	{
		_Lock->WaitOne();
	}
	
    
	/**************************************************************************
	* UnLock()	Manually unlocks queue across all instances of consumers
	*					
	**************************************************************************/
	void CSharedMemory::UnLock()
	{
		_Lock->ReleaseMutex();
	}
	
    
	/**************************************************************************
	* BufferSize()	Returns size of defined shared memory segment
	*					
	* RETURNS		[const int] maximum buffer size
	**************************************************************************/
	const int CSharedMemory::BufferSize()
	{
		return (const int)XML_BUFFER_SIZE - 1;
	}
}


To use this class simply add a reference to the SharedMemory.Dll and use the SharedMemory namespace.  To set data use the SetData("Your Data") method and to retrieve document use the String GetData() method from your application that uses the Class Library.

Important: You should declare the CSharedMemory object at a module or global scope in your application so that the global shared memory segment will persist while your application is running.

Following is a simple C# Form that demonstrates how to access the shared memory segment.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SharedMemory;

namespace SharedMemTest
{
    public partial class Form1 : Form
    {

        CSharedMemory m_Mem = new CSharedMemory();

        
        /// Sets a simple text string document to the shared memory segment
        private void SetMem()
        {
            m_Mem.Lock();

            m_Mem.SetData("Test Data 1234", false);

            m_Mem.UnLock();
        }
        
        
        /// Gets the shared memory segment
        private string GetMem()
        {
            m_Mem.Lock();

            string data = m_Mem.GetData(false, false);

            m_Mem.UnLock();

            return data;
        }
    }
}


To download this project click here (VS 2008 SharedMemory.zip) or you can download the compiled dll class library (32K buffer size) by clicking here (Compiled Class Library DLL).

Hope this helps!