内存映射文件(Memory Mapped File,MMF)是一种允许多个进程共享内存区域的技术,这使得进程间的数据共享变得简单而高效。本文将介绍如何使用内存映射文件实现跨进程通信,并提供C++和C#的示例代码。
内存映射文件是一种文件系统对象,它允许文件内容直接映射到进程的地址空间。这样,进程就可以像访问内存一样访问文件内容,而不需要进行常规的文件I/O操作。内存映射文件不仅可以提高文件访问速度,还可以实现进程间的数据共享。
以下是一个使用内存映射文件实现跨进程通信的示例。这个示例包括一个C++编写的DLL库和一个C#的客户端程序。
C++DLL库定义了两个函数:SetValue和GetValue,分别用于设置和获取内存映射文件中的数据。
#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;
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)
{
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);
}
}
}
}
C#客户端程序通过P/Invoke调用C++DLL库中的函数,实现跨进程通信。
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("sharedmem.dll")]
extern static int GetRecordCount();
[DllImport("sharedmem.dll")]
extern static void SetValue(string key, string value);
[DllImport("sharedmem.dll")]
extern static IntPtr GetValue(string key);
static void Main(string[] args)
{
Console.WriteLine("Setting value...");
SetValue("key1", "value1");
Console.WriteLine("Getting value...");
IntPtr intPtr = GetValue("key1");
string value = Marshal.PtrToStringUni(intPtr);
Console.WriteLine($"Value: {value}");
}
}