C#のMemoryMappedFileで作成した共有メモリをC++(ネイティブ)から利用する

C#でMemoryMappedFileを使って作成した共有メモリをVC++(あるいはC言語)から利用する方法です。

条件

  • .NET Framework
  • VC++
  • Windows限定(WINAPIを利用)

.NET Core & Linuxとかでは全然使えないのでご了承ください。

C#側のコード

まずはこんな感じでC#上で共有メモリを作成してデータを登録しておきます。

string mapName = "Global\\hoge.map";
int length = 100;

// 共有メモリの作成
var mapFile = MemoryMappedFile.CreateNew(mapName, length/*byte*/);

// アクセス権限を誰でもアクセス可能に変更する
MemoryMappedFileSecurity permission = info.MapFile.GetAccessControl();
permission.AddAccessRule(new AccessRule<MemoryMappedFileRights>("Everyone", 
    MemoryMappedFileRights.FullControl, AccessControlType.Allow));
info.MapFile.SetAccessControl(permission);

// 書き込むデータの作成
byte[] array = new byte[100];
for(int i = 0; i < length; i++)
{
    array[i] = i;
}

// データを書き込む
using (MemoryMappedViewAccessor accesor = MemoryMappedFile.OpenExisting(mapName).CreateViewAccessor())
{
    accesor.WriteArray(0, array, 0, array.Length); // 0~99までのデータを書き込んでる
}

このAddAccessRuleは.NETのほかの権限設定系でも頻出するイデオムなので覚えておいて損はないと思います。xxxRightsをAddAcessRuleするみたいな。

C++側のコード

Win32APIを使って既存の共有メモリを読み取ります。いつものアレです。

#include <Windows.h>

// C#で作成した共有メモリの名前
LPCWSTR lpName = L"Global\\hoge.map";
HANDLE hMap = OpenFileMapping(FILE_MAP_WRITE, true, lpName);
if (hMap == nullptr)
{
    return -1;
}

// Win32APIで共有メモリを開く
LPVOID lvptr = MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 100);
unsigned char* bytes = reinterpret_cast<unsigned char*>(lvptr);
if (bytes == nullptr)
{
    return -2;
}

for (int i = 0; i < 100; i+=4)
{
    std::cout << "[" << i << "]" << (int)bytes[i] << std::endl;
}

// 出力結果
// > [0] 0
// > [1] 1
// > ...
// > [98] 98
// > [99] 99

// 開放する
UnmapViewOfFile(lvptr);
CloseHandle(hMap);

クソ雑エラー処理ですがだいたいこんな感じです。

C#側でintとかfloat型で登録した場合C++側でデータサイズを合わせてforを回すようにしてください。(intの場合4バイト幅など

そこはかとなくメモリ上にビッグエンディアンで配置されているような気がしますが、きっと何か癖のようなものがあるのだと思いますが相互運用はbyte単位でしかしないので詳しくは調べていません。

以上です。