共有メモリを使用する

複数のプロセスからアクセスすることが可能なメモリを共有メモリという。 この共有メモリを使えばプロセス間でのデータのやりとりが簡単に、かつ高速に実行できる。
ただし共有メモリは複数のプロセスから同時アクセスされることが考えられるので、 同期処理が必要な点だけは注意しなければならない。

新規プロジェクトUKFileMappingTestを、ダイアログベースとして作成する。 プロジェクトを作成したら、ダイアログリソースにエディットコントロールを1つと、 ボタンを2つ追加する。ボタンのキャプションはそれぞれ「書き込み」「読み込み」とする。 これらのコントロールについてはClassWizardを使用し、以下のような設定をおこなうこと。
コントロールコントロールIDDDX変数
エディットコントロールIDC_EDIT1CString m_strEdit1
「書き込み」ボタンIDC_BUTTON_WRITE(なし)
「読み込み」ボタンIDC_BUTTON_READ(なし)

ダイアログリソースの編集が終わったら、ダイアログクラスのヘッダーファイルを以下のように記述する。

	// CMutexのヘッダーをインクルード
	#include <afxmt.h>

	/////////////////////////////////////////////////////////////////////////////
	// CUKFileMappingTestDlg ダイアログ

	class CUKFileMappingTestDlg : public CDialog
	{
	……
	// インプリメンテーション
	protected:
		HANDLE m_hMapping;     // メモリマップドファイルのハンドル
		void *m_pMappingView;  // ファイルのビューへのポインタ
		CMutex *m_pMutex;      // ミューテックスオブジェクト
	……
	};
ヘッダーに続いて、ソースファイルへの記述もおこなう。
ClassWizardを使用して、あらかじめ必要なメッセージハンドラのスケルトンを作成しておくこと。
	BOOL CUKFileMappingTestDlg::OnInitDialog()
	{
	……
		// TODO: 特別な初期化を行う時はこの場所に追加してください。

		// メモリマップドファイルの生成
		m_hMapping = ::CreateFileMapping(
			(HANDLE)0xffffffff,     // 共有メモリの場合は0xffffffffを指定
			NULL,                   // セキュリティ属性。NULLでよい
			PAGE_READWRITE,         // プロテクト属性を読み書き可能に指定
			0,                      // ファイルサイズの上位32ビット
			1024,                   // ファイルサイズの下位32ビット
			"SharedMemory" );       // メモリマップドファイルの名前

		// プロセス内のアドレス空間にファイルのビューをマップ
		m_pMappingView = ::MapViewOfFile(m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 1024);

		// ミューテックスオブジェクトの生成
		m_pMutex = new CMutex(FALSE, "UKFileMappingTest_Mutex");

		return TRUE;  // TRUE を返すとコントロールに設定したフォーカスは失われません。
	}

	// ClassWizardを使用して作成した、IDC_BUTTON_READのBN_CLICKメッセージハンドラ
	void CUKFileMappingTestDlg::OnButtonRead() 
	{
		// ★共有メモリの内容を取得。このときミューテックスオブジェクトを
		// 使用して同期処理をおこなう
		m_pMutex->Lock(INFINITE);
		m_strEdit1 = (LPTSTR)m_pMappingView;
		m_pMutex->Unlock();

		UpdateData(FALSE);
	}

	// ClassWizardを使用して作成した、IDC_BUTTON_WRITEのBN_CLICKメッセージハンドラ
	void CUKFileMappingTestDlg::OnButtonWrite() 
	{
		UpdateData(TRUE);

		// ★共有メモリへ書き込む。このときミューテックスオブジェクトを
		// 使用して同期処理をおこなう
		m_pMutex->Lock(INFINITE);
		memcpy(m_pMappingView, (LPCTSTR)m_strEdit1, m_strEdit1.GetLength() + 1);
		m_pMutex->Unlock();
	}

	// WM_DESTROYメッセージハンドラ
	void CUKFileMappingTestDlg::OnDestroy() 
	{
		CDialog::OnDestroy();
	
		// ★ファイルのビューをアンマップし、マップのハンドルをクローズ
		BOOL b = ::UnmapViewOfFile(m_pMappingView);
		::CloseHandle(m_hMapping);

		// ★ミューテックスオブジェクトの破棄
		delete m_pMutex;
	}
これでテスト用のアプリケーションは完成である。ビルドしたら、このアプリケーションを2つ実行すること。 一方のダイアログでエディットコントロールに文字列を入力し「書き込み」ボタンを押す。 そしてもう一方のダイアログで「読み込み」ボタンを押すと、 「書き込み」をおこなったダイアログで入力した文字列がエディットコントロールに表示されるはずである。

この手法について簡単に解説をすると・・・。
まずCreateFileMapping APIを使用してメモリマップドファイルを作成する。 もし同じ名前のメモリマップドファイルがすでに作成されている場合、 この関数は既存のメモリマップドファイルのハンドルを返す。 これにより2つ目のアプリケーションを起動したときには、 1つ目のアプリケーション起動時に生成されたメモリマップドファイルのハンドルを取得することになる。

メモリマップドファイルのハンドルを取得できたら、 MapViewOfFile APIを使ってプロセス内のアドレス空間へファイルのビューをマップする。 この関数の戻り値はビューへのポインタなので、 以後はこのポインタを通じて共有メモリへアクセスすることが可能になる。

共有メモリへアクセスをおこなう際、必ず行わなくてはならないのが同期処理である。 これを行わないと複数のプロセスから同時にアクセスがおこなわれ、 共有メモリの内容が予期せぬ結果になる可能性があるからだ。 ここでは同期処理にミューテックスを使用している。

アプリケーション終了時には、UnmapViewOfFile APIでビューをアンマップし、 メモリマップドファイルのハンドルをクローズする。 アプリケーションを1つ終了するたびにCloseHandle APIでハンドルがクローズされ、 ハンドルの参照カウントは1減る。 すべてのアプリケーションが終了すると参照カウントは0になり、 メモリマップドファイルは破棄される。 なおこれはミューテックスについても同様である。

目次へ