第114章 メモリマップトファイル その1


プロセス間で通信ができるといろいろ便利なことがあります。 簡単な方法としてメモリマップトファイルがあります。

SDK編ではすでに、第123章で紹介してあるのでそちらも参照してみてください。



プロセス1で見ているメモリは、仮想メモリであり、そのアドレスは 仮想メモリのアドレスです。従って、他のプロセスにとっては意味のない アドレスなのです。

そこで、プロセス1が物理メモリに共有メモリとして、一定の場所を確保したとします。

この時プロセス1では、この物理メモリのアドレスをプロセス1の仮想メモリのアドレスにマッピングしてから、読み書きをしなくてはなりません。

具体的には、まずCreateFileMapping関数でファイルマッピングオブジェクトを作成します。

HANDLE CreateFileMapping(
  HANDLE hFile,                       // ファイルのハンドル
  LPSECURITY_ATTRIBUTES lpAttributes, // セキュリティ
  DWORD flProtect,                    // 保護
  DWORD dwMaximumSizeHigh,            // サイズを表す上位 DWORD
  DWORD dwMaximumSizeLow,             // サイズを表す下位 DWORD
  LPCTSTR lpName                      // オブジェクト名
);
hFileには、作成元ファイルのハンドルを指定しますが、実際にファイルを作らない場合は0xFFFFFFFFを指定します。

lpAttributesにNULLを指定するとデフォルトのセキュリティとなります。

flProtectには、保護を指定しますが、プロセス間級友メモリでは通常PAGE_READWRITEを 指定します。

dwMaximumSizeHighには、マッピングファイルの上位32ビットを指定します。 ファイルサイズが4Gバイト以下の場合は0にします。

dwMaximumSizeLowには、マップするファイルサイズの下位32ビットを指定します。

lpNameには、メモリ領域の名前を指定します。共有メモリを作成する場合は必ず指定します。

関数が成功するとハンドルが返されます。 失敗するとNULLが返されます。 すでに、ファイルマッピングの名前が存在していても成功します。

すでに、その名前のオブジェクトが存在する場合GetLastError関数は、ERROR_ALREADY_EXISTSを返します。

マッピングオブジェクトができたら、自分のプロセスで使用できるようにマッピングします。

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,   // ファイルマッピングオブジェクトのハンドル
  DWORD dwDesiredAccess,       // アクセスモード
  DWORD dwFileOffsetHigh,      // オフセットの上位 DWORD
  DWORD dwFileOffsetLow,       // オフセットの下位 DWORD
  SIZE_T dwNumberOfBytesToMap  // マップ対象のバイト数
);
hFileMappingObjectには、マッピングオブジェクトのハンドルを指定します。

dwDesiredAccessには、アクセスモードを指定しますが、共有メモリの場合は FILE_MAP_ALL_ACCESSを指定します。

dwFileOffsetHighには、マッピングファイルのオフセットの上位32ビットを指定します。
通常は0を指定します。

dwFileOffsetLowには、マッピングファイルのオフセットの下位32ビットを指定しますが、通常は共有領域全体をマッピングするため0を指定します。

dwNumberOfBytesToMapには、マップ対象のバイト数を指定しますが、共有領域全体をマップするときは0を指定します。

成功すると、開始アドレスが返されます。失敗するとNULLが返されます。

すでに存在するファイルマッピングオブジェクトを開くには、OpenFileMapping 関数を使います。

HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,  // アクセスモード
  BOOL bInheritHandle,    // 継承フラグ
  LPCTSTR lpName          // オブジェクト名
);
dwDesiredAccessには、アクセスモードを指定しますが、共有メモリの場合は通常 FILE_MAP_ALL_ACCESSを使います。

bInheritHandleには、継承フラグを指定します。ハンドルを子プロセスに継承しない 場合はFALSEを指定します。

lpNameには、オープンするマッピングオブジェクトの名前を指定します。

成功すると、マッピングオブジェクトのハンドルが返されますので、MapViewOfFile関数でポインタを取得します。

さて、共有領域が不要になったら、アンマップします。そして、マップトオブジェクトハンドルをクローズします。

BOOL UnmapViewOfFile(
  LPCVOID lpBaseAddress   // 開始アドレス
);
アンマップに成功したら0以外が、失敗すると0が返されます。

では、サンプルのプログラムを見てみましょう。

/* filemapping01.c */

#include <stdio.h>
#include <windows.h>
#include <conio.h>

int FRead(LPSTR);
int FWrite(LPSTR);

HANDLE hFMWrite;


int main()
{
    
    static LPSTR lpAddress1, lpAddress2;

    char szNo[8];
    BOOL bEnd = FALSE;

    while (1) {
        printf("***************\n");
        printf("1. Writre\n");
        printf("2. Read\n");
        printf("0. End\n");
        printf("***************\n");
        printf("No. = ");
        gets(szNo);

        switch (szNo[0]) {
            case '1':
                FWrite(lpAddress1);
                break;
            case '2':
                FRead(lpAddress2);
                break;
            case '0':
                bEnd = TRUE;
                break;
            default:
                printf("正しくない番号です\n\n");
                break;
        }
        if (bEnd)
            break;
    }
    

    if (hFMWrite) {
        if (CloseHandle(hFMWrite) == 0)
            printf("書き込みハンドルクローズ失敗\n");
        else
            printf("書き込みハンドルクローズ成功\n");
    }

    return 0;
}

int FWrite(LPSTR lpStr)
{
    char szStr[1024];

    if (!hFMWrite)
        CloseHandle(hFMWrite);
    
    
    hFMWrite = CreateFileMapping(
       (HANDLE)-1, 
       NULL,
       PAGE_READWRITE,
       0, 
       1024,
       "NEKODEMOWAKARU");
    if (hFMWrite == NULL)
        return -1;
    if (GetLastError() == ERROR_ALREADY_EXISTS)
        printf("すでにマッピングオブジェクトが存在しました\n");

    lpStr = (LPSTR)MapViewOfFile(hFMWrite,
       FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if (lpStr == NULL)
        return -2;

    printf("書き込み==");
    gets(szStr);
    strcpy(lpStr, szStr);
    strcat(lpStr, "\n");

    UnmapViewOfFile(lpStr);
    
    printf("書き込みました\n");
    return 0;
}

int FRead(LPSTR lpStr)
{
    HANDLE hFM;

    hFM = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "NEKODEMOWAKARU");
        
    lpStr = (LPSTR)MapViewOfFile(hFM,
            FILE_MAP_ALL_ACCESS, 0, 0, 0);

    if (lpStr == NULL) {
        printf("受信失敗\n");
        CloseHandle(hFM);
        return -1;
    }
    printf(lpStr);
    if (UnmapViewOfFile(lpStr) == 0) {
        printf("読み込み用アドレスアンマップ失敗\n");
    } else {
        printf("読み込み用アドレスアンマップ成功\n");
        lpStr = NULL;
    }

    CloseHandle(hFM);

    return 0;
}
では、実際にこのプログラムを動かしてみましょう。

メニューが出るので、書き込みか、読み出しか、終了かを選択します。



「1.Write」を選択して、実際に書き込みを行います。



今度は「2.Read」を選択して読み出してみます。



無事読み出すことに成功です。



一度書き込んで、再度書き込みをすると 「すでにマッピングオブジェクトが存在しました」と言われますが、 再度書き込みが行われます。

このプログラムを複数起動すると、一つのプログラムで書き込みが起ると、 他のプログラムでもその書き込みを読むことができます。

また、他のプログラムが書き込んだ内容を変更することも可能です。


[Index][総合Index] [Previous Chapter] [Next Chapter]

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