.Net(C# WPF)アプリとWin32アプリ(C++)でファイルマッピングを使って構造体データをプロセス間通信させる

# 前置き
　とあるアプリを作成するのにWin32フックDLLと.Netのクライアントアプリ間でデータを連携させる必要があって、このデータ連携にファイルマッピングを採用することにしました。  
　ネットの断片的な資料を漁って、別の切り口の断片的な情報を浮かべようと思います(ぉぃ)  

# ファイルマッピング今昔
　Win32APIだとCreateFileMapping / OpenFileMappingで得たハンドルからMapViewOfFileを使ってポインタを得て構造体にキャストして使えて簡単です。  
　.Net4.0未満の場合はそれらAPIをマーシャリングする所から始めるメンドクサーメソッドだったけど、.Net4.0からはMemoryMappedFileで多少イージーになったようでありがたいです。  

# サンプル
C#のアプリ(WPF)とCのアプリ(今回はWin32SDKをベタで使うタイプ)でサンプルを作ったものを抜粋します。  

```csharp:WPFアプリ
namespace IPCwithCSandCPPSample
{
    /// <summary>
    /// テストクラス：ファイルマッピングへマッピングするためのクラス
    /// C++側で閲覧するためデータ構造が同一になるように配慮すること
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
    public class Test1
    {
        public int A;
        public bool B;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string C;
        public Test2 D = new Test2();
    }
    
    /// <summary>
    /// テストクラス２
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
    public class Test2
    {
        public int AA;
    }

    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private MemoryMappedFile mmf;
        private MemoryMappedViewStream mmvs;
        private Test1 testclass = new Test1();

        public MainWindow()
        {
            InitializeComponent();

            // 頻繁に開け閉めしなくてもよいので起動時に作成
            mmf = MemoryMappedFile.CreateOrOpen("TestMap", (uint)Marshal.SizeOf(typeof(Test1)));
            mmvs = mmf.CreateViewStream();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // UIからデータを取り込む(右辺は全部WPFのテキストボックスです)
            testclass.A = int.Parse(this.A.Text);
            testclass.B = bool.Parse(this.B.Text);
            testclass.C = this.C.Text;
            testclass.D.AA = int.Parse(this.D.Text);

            // 構造体の体裁を整えたデータクラスをファイルマッピングのストリームに流すためにバイト列に変換する
            int size = Marshal.SizeOf(typeof(Test1));
            byte[] bytes = new byte[size];
            IntPtr ptr = Marshal.AllocCoTaskMem(size);
            Marshal.StructureToPtr(testclass, ptr, false);
            Marshal.Copy(ptr, bytes, 0, size);
            Marshal.FreeCoTaskMem(ptr);

            // ファイルマッピングに書く
            mmvs.Write(bytes, 0, size);
            mmvs.Seek(0, System.IO.SeekOrigin.Begin);
        }
    }
}
```

```CPP:Win32APIアプリ

/// <summary>
/// テストクラス２
/// </summary>
typedef struct {
	int AA;
}Test2;

/// <summary>
/// テストクラス：ファイルマッピングへマッピングされているクラス
/// </summary>
typedef struct {
	int A;
	BOOL B;
	wchar_t C[256];
	Test2 D;
}Test1;

HANDLE g_Handle = NULL;
Test1 *g_pTest = NULL;

/// <summary>
/// Opens the mapping.
/// </summary>
void OpenMapping()
{
	if (g_Handle != NULL)
	{
		return;
	}

	// C#で作るのでこちらは開くだけ
	g_Handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, L"TestMap");
	if (g_Handle == NULL)
	{
		return;
	}

	// マップファイルのアクセスはキャストして構造体から行える
	g_pTest = (Test1*)MapViewOfFile(g_Handle, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(Test1));
	if (g_pTest == NULL)
	{
		CloseHandle(g_Handle);
		return;
	}
}

/// <summary>
/// Closes the mapping.
/// </summary>
void CloseMapping()
{
	if (g_Handle == NULL)
	{
		return;
	}

	CloseHandle(g_Handle);
}

/// <summary>
/// Gets the mapping structure to string.
/// </summary>
/// <param name="string">The string.</param>
void GetMappingStructToString(wchar_t * string)
{
	if (g_pTest == NULL)
	{
		return;
	}

	wsprintf(string, L"%d %d %s %d\n", g_pTest->A, g_pTest->B, g_pTest->C, g_pTest->D.AA);
}

/// <summary>
/// 200msに一回共有メモリを読んで表示を更新するよウィンドウ
/// </summary>
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static int timerID;
	static wchar_t string[256];

	switch (message)
	{
	case WM_CREATE:
		timerID = SetTimer(hwnd, -1, 200, NULL);
		OpenMapping();
		break;
	case WM_TIMER:
		OpenMapping();
		GetMappingStructToString(string);
		InvalidateRect(hwnd, NULL, true);
		break;
	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);
		// TODO: 描画コードをここに追加してください...
		{
			RECT temp;
			GetClientRect(hwnd, &temp);
			DrawText(hdc, string, lstrlen(string), &temp, DT_TOP | DT_LEFT | DT_WORDBREAK);
		}
		EndPaint(hwnd, &ps);
		break;
	case WM_DESTROY:
		CloseMapping();
		KillTimer(hwnd, timerID);
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
	return 0;
}
```

# 共有する構造体に関して
- ファイルマッピングの中身はバイト列だ、と言うことを頭に入れて構造体を構成する
- C#ではクラスでもStructLayout出来るが上記は考慮すること

　一つ目は当然のことですね。アドレス渡されても渡された側は見られません。

```CPP
typedef struct {
	int A;
	BOOL B;
	wchar_t C[256];
	Test2 D;
}Test1;
```

こんな感じの構造体にした場合は

```csharp
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public class Test1
{
	public int A;
	public bool B;
	[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
	public string C;
	public Test2 D = new Test2();
}
```

こんな感じで大丈夫。

## 注意点

- C#のboolをStructureToPtrするとレイアウト情報にPack=1と設定しても4バイトになるので、C99のboolで受けるならアライメント調整が必要
- 多分2バイトや3バイトも強制4バイトアライメントになる予感（知らんけど）
- CharSet = CharSet.Unicode (ここ数年のWin32SDKベタアプリ側はデフォでUnicode)
- 配列、文字列なんかは上限値必須

# C#側の書き込み側の肝

```csharp
// 構造体の体裁を整えたデータクラスをファイルマッピングのストリームに流すためにバイト列に変換する
int size = Marshal.SizeOf(typeof(Test1));
byte[] bytes = new byte[size];
IntPtr ptr = Marshal.AllocCoTaskMem(size);
Marshal.StructureToPtr(testclass, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
Marshal.FreeCoTaskMem(ptr);

// ファイルマッピングに書く
mmvs.Write(bytes, 0, size);
mmvs.Seek(0, System.IO.SeekOrigin.Begin);
```

- 構造体→バイト列→ストリームWrite の手順が必要
- Marshal.AllocCoTaskMemはMarshal.AllocHGlobalでも速度的にはあまり変化がない模様

　mmvs.Seek(testclass.ToBytes(), 0, testclass.Length)と出来るように組んでおくと良いかも

# 参考：
[C#でレガシーな事をする方向けのまとめ]( http://qiita.com/hmuronaka/items/619f8889e36c7b5db92d)