Introduction
This is a sample of how to use a shared memory in SetData/GetData pattern like a map.
void SetData(TCHAR* key, TCHAR* value)
TCHAR* GetData(TCHAR* key)
This pattern can be used to store and retrieve binary data of variable lengths (like those of images) with slight improvement to the code.
Background
For basic tutorial on CreateFileMapping
, please refer here.
Using the code
The code and the samples explain how to use this shared memory from VB.NET, C#, and C++.
In the code attached, a C++ DLL (named SharedMem.DLL) mimics a map<T,T>
(Dictionary
/Hashtable
) so that it can be used across processes. The data sharing is based on an MMF.
A C# sample will look as shown below:
[DllImport("sharedmem.dll")]
extern static int GetRecordCount();
[DllImport("sharedmem.dll")]
extern static void SetValue(
[MarshalAs(UnmanagedType.LPTStr)] string key,
[MarshalAs(UnmanagedType.LPTStr)] string value);
[DllImport("sharedmem.dll")]
extern static IntPtr GetValue([MarshalAs(UnmanagedType.LPTStr)]string key);
private void OnSetClick(object sender, EventArgs e)
{
SetValue(keyText, valueText);
}
private void OnGetClick(object sender, EventArgs e)
{
IntPtr intPtr = GetValue(keyText);
textBox.Text = Marshal.PtrToStringUni(intPtr);
}
The code is very much self explanatory.
The MMF is created/opened in DLLMain.cpp.
#include "stdafx.h"
#include "sharedmem.h"
#include "tchar.h"
#include "stdio.h"
extern HANDLE m_hFileMMF, m_pViewMMFFile, hMutex;
class CMutex
{
public:
CMutex()
{
if(!hMutex){
hMutex = CreateMutex(
NULL,
FALSE,
L"Global\\MMFMutex");
}
WaitForSingleObject( hMutex, INFINITE);
}
~CMutex()
{
ReleaseMutex(hMutex);
}
};
void SetRecordCount(int value)
{
TCHAR* record = (TCHAR*)m_pViewMMFFile;
swprintf_s(record, MAX_PATH, L"RECCNT=%d#", value);
}
extern "C" int GetRecordCount()
{
TCHAR* record = (TCHAR*)m_pViewMMFFile;
TCHAR temp[MAX_PATH];
int recordCount = -1;
TCHAR seps[] = L"=#";
TCHAR *token1 = NULL;
TCHAR *next_token1 = NULL;
_tcscpy_s(temp, MAX_PATH, record);
token1 = _tcstok_s ( temp, seps, &next_token1);
if(token1 && _tcscmp(token1, _T("RECCNT")) == 0)
{
token1 = _tcstok_s ( NULL, seps, &next_token1);
recordCount = _ttoi(token1);
}else
{
recordCount = 1;
SetRecordCount(1);
}
return recordCount;
}
int nRecordCount = -1;
void RemoveValue(TCHAR* key)
{
TCHAR* record = (TCHAR*)m_pViewMMFFile;
TCHAR temp[MAX_PATH];
nRecordCount = GetRecordCount();
record+=MAX_PATH;
bool isRecordFound = false;
int i;
for(i= 1; i< nRecordCount; i++,record+=MAX_PATH)
{
TCHAR seps[] = L"=#";
TCHAR *token1 = NULL;
TCHAR *next_token1 = NULL;
_tcscpy_s(temp, MAX_PATH, record);
token1 = _tcstok_s ( temp, seps, &next_token1);
if(_tcscmp(token1, key) == 0)
{
isRecordFound = true;
break;
}
}
for(; i< nRecordCount-1; i++, record+=MAX_PATH)
{
TCHAR* nextRecord = record + MAX_PATH;
_tcscpy_s(record, MAX_PATH, nextRecord);
}
}
TCHAR* IfExists(TCHAR* key, TCHAR** value = NULL)
{
TCHAR* record = (TCHAR*)m_pViewMMFFile;
TCHAR temp[MAX_PATH];
nRecordCount = GetRecordCount();
record+=MAX_PATH;
for(int i=1; i< nRecordCount; i++,record+=MAX_PATH)
{
TCHAR seps[] = L"=#";
TCHAR *token1 = NULL;
TCHAR *next_token1 = NULL;
_tcscpy_s(temp, MAX_PATH, record);
token1 = _tcstok_s ( temp, seps, &next_token1);
if(_tcscmp(token1, key) == 0)
{
token1 = _tcstok_s ( NULL, seps, &next_token1);
if(value!=NULL)
{
int len = _tcslen(token1)+1;
*value = new TCHAR(len);
_tcscpy_s(*value, len, token1);
}
return record;
}
}
return NULL;
}
extern "C" TCHAR* GetValue(TCHAR* key)
{
TCHAR* sRetVal = new TCHAR[MAX_PATH];
CMutex mutex;
TCHAR* data = NULL;
if(m_pViewMMFFile)
{
IfExists(key, &data);
}
return data;
}
extern "C" void SetValue(TCHAR* key, TCHAR* value)
{
CMutex mutex;
if(m_pViewMMFFile )
{
if(value == NULL)
{
RemoveValue(key);
}
else
{
TCHAR* data = IfExists(key);
if(data == NULL)
{
data = new TCHAR[MAX_PATH];
swprintf_s(data, MAX_PATH, L"%s=%s#", key, value);
TCHAR* record = (TCHAR*)m_pViewMMFFile;
record += MAX_PATH*nRecordCount;
nRecordCount++;
SetRecordCount(nRecordCount);
_tcscpy_s(record, MAX_PATH, data);
delete data;
}
else
{
swprintf_s(data, MAX_PATH, L"%s=%s#", key, value);
}
}
}
}
Points of interest
This code needs some update before it can be readily used in production code.
- Deleting a key from the MMF (and the resultant memory compaction).
- Variable lengths of data. (Hint: Set the size of the data in the MMF.)
- Automatic resizing of MMF.
I intend to post the improvements over time.