第123章 メモリマップト・ファイル


第121章でdll内の変数(メモリ)を 複数のアプリケーションで共有する方法があると書きました。 この1つの方法がメモリマップト・ファイルという手法です。 ファイルの内容をプロセスのアドレス空間にコピーすることを 「ファイルマッピング」などといいます。また、他のプロセスが 自分のアドレス空間に自分と同じ内容のファイルマッピング・オブジェクトを 持つこともできます。(要するにプロセス間でデータを共有できるという ことです)

今回はメモリマップト・ファイルをdllではなく普通の(?) プログラムで解説します。

まず、必要な関数から解説します。

HANDLE CreateFileMapping( HANDLE hFile, // ファイルマッピングオブジェクトを作成する元のファイルハンドル LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // セキュリティ DWORD flProtect, // ページ保護の指定 DWORD dwMaximumSizeHigh, // 4G以下のファイルはここを0にする DWORD dwMaximumSizeLow, // ファイルサイズの下位32ビット LPCTSTR lpName // マッピングオブジェクトの名前

hFileはCreateFile関数で作ったファイルのハンドルがきます。 実際にファイルを作らなくても大丈夫です。このときここの値は 0xFFFFFFFFにします(−1です)。実際のファイルを作らなかったときは dwMaximumSizeHigh, dwMaximumSizeLowの値を必ず指定します。

lpFileMappingAttributesはSECURITY_ATTRIBUTES構造体への ポインタを指定しますが、NULLにしておけばデフォルトのセキュリティになります。

flProtectはマッピングされるときのページ保護の指定です。
PAGE_READWRITEは読みとり、書き込みアクセスを許可します。
他の指定についてはヘルプを参照してください。

dwMaximumSizeHighはファイルマッピングオブジェクトのサイズの上位32ビットです。 4G以下のファイルでは0になります。

dwMaximumSizeLowはファイルマッピングオブジェクトのサイズの下位32ビットです。 dwMaximumSizeHighとこの引数の両方を0にするとhFileで指定した ファイルのサイズと等しくなります。

lpNameにはマッピングオブジェクトの名前を指定します。すでに存在する 名前を付けてしまった場合はflProtectで指定した保護でそのマッピングオブジェクト へのアクセスがなされます。

戻り値は成功したときはマッピングオブジェクトへのハンドル、失敗したときは NULLが返されます。

さて、マッピングオブジェクトのハンドルを取得したならば次に ファイルの内容(ファイルビューなどといいます)をアドレス空間に MapViewOfFileでマッピングします。

LPVOID MapViewOfFile( HANDLE hFileMappingObject, // ファイルマッピングオブジェクトのハンドル DWORD dwDesiredAccess, // アクセスモード DWORD dwFileOffsetHigh, // マッピングするファイルのオフセット(上位32ビット) DWORD dwFileOffsetLow, // 同下位32ビット DWORD dwNumberOfBytesToMap // マッピングするバイト数、0だとファイル全体 );

hFileMappingObjectはファイルマッピングオブジェクトハンドルを指定します。

dwDesiredAccessはアクセスモードを指定します。
FILE_MAP_WRITEは読みとり、書き込みアクセスを指定します。
FILE_MAP_READは読みとり専用です。
FILE_MAP_ALL_ACCESSはFILE_MAP_WRITEと同じです。

dwFileOffsetHigh, dwFileOffsetLowはマッピングするファイルのオフセットを指定します。 要するにどこからマッピングするかです。ファイルの最初からならどちらも0です。

dwNumberOfBytesToMapにはマッピングするバイト数を指定します。0を指定すると ファイル全体をマッピングします。

成功したらマップビューの開始アドレスを取得できます。 実際にファイルを作らなかった場合はこのアドレスにデータをコピーします。

ファイルマッピングオブジェクトからデータを読み出すには、 OpenFileMapping関数でオブジェクトのオープンハンドルを取得して、 MapViewOfFileでマッピングをします。

HANDLE OpenFileMapping( DWORD dwDesiredAccess,// アクセスモード BOOL bInheritHandle,// 継承の有無 LPCTSTR lpName // マッピングオブジェクトの名前 );

アクセスモードはMapViewOfFileと同じです。

bInheritHandleはプロセス作成時に新しいプロセスにハンドルを引き継ぐかどうか を指定します。

lpNameには、ファイルマッピングオブジェクトの名前を指定します。

不要になったオブジェクトハンドルはCloseHandle関数で閉じます。 また、マップビューはUnmapViewOfFileで解放します。

BOOL UnmapViewOfFile( LPCVOID lpBaseAddress // マップビュー開始アドレス );

では、プログラムを作ってみましょう。


メニューの「オプション」から「ファイルに書き込む」を 選択すると


メモリマップトファイルに書き込むデータ入力のためのダイアログボックスが出てきます。


「ファイルから読みだす」を選択するとデータが表示されます。 このプログラムを複数起動しておくとAから書き込んだデータも Bから読み出すことができます。


// memmap01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "オプション(&O)" BEGIN MENUITEM "ファイルに書き込む(&W)", IDM_WRITE MENUITEM "ファイルから読み出す(&R)", IDM_READ END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 127, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メッセージ入力" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,72,50,14 PUSHBUTTON "キャンセル",IDCANCEL,70,72,50,14 EDITTEXT IDC_EDIT1,7,7,112,55,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN END

これは、普通のメニューとダイアログボックスのリソース・スクリプトです。

// memmap01.cpp #define STRICT #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "memmap01"; //ウィンドウクラス char szMessage[1024]; //メモリマップトファイルに書き込まれる文字列 int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメモリマップトファイル",//タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 260,//幅 110,//高さ NULL,//親ウィンドウのハンドル、親を作るときはNULL NULL,//メニューハンドル、クラスメニューを使うときはNULL hInst,//インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HANDLE hMapFile1, hMapFile2; static LPSTR lpMapAddress1, lpMapAddress2; HINSTANCE hInst; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_WRITE: hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); if(DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc) == IDOK) { hMapFile1 = CreateFileMapping( (HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 1024, "Kumei's Object"); if (!hMapFile1) return -1; lpMapAddress1 = (LPSTR)MapViewOfFile(hMapFile1, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (lpMapAddress1 == NULL) { MessageBox(hWnd, "MapViewOfFile失敗", "Error", MB_OK); return -1; } strcpy(lpMapAddress1, szMessage); if(CloseHandle(hMapFile1) == 0) MessageBox(hWnd, "ハンドルのクローズ失敗", "Error", MB_OK); } break; case IDM_READ: hMapFile2 = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "Kumei's Object"); if (!hMapFile2) { MessageBox(hWnd, "ファイルマッピングのオープン失敗", "Error", MB_OK); return -1; } lpMapAddress2 = (LPSTR)MapViewOfFile(hMapFile2, FILE_MAP_ALL_ACCESS, 0, 0, 0); if (lpMapAddress2 == NULL) { MessageBox(hWnd, "MapViewOfFilw 失敗", "Error", MB_OK); return -1; } MessageBox(hWnd, lpMapAddress2, "読み出し", MB_OK); CloseHandle(hMapFile2); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: if (lpMapAddress1) { if(UnmapViewOfFile(lpMapAddress1) == 0) MessageBox(NULL, "失敗", "OK", MB_OK); } if (lpMapAddress2) { if (UnmapViewOfFile(lpMapAddress2) == 0) MessageBox(NULL, "失敗", "OK", MB_OK); } PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; } LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), szMessage, sizeof(szMessage)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } break; default: return FALSE; } return FALSE; }

ソース・ファイルを一気に示しました。使う関数がわかっていれば それほど難しくないですね。

[SDK第2部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 03/May/1998 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。