ファイルマッピングによるファイル操作

 ファイルのデータを操作する時には、まずバッファを確保してファイルを読み込み、データを編集。そして、編集したデータをまたファイルに書き戻す、という操作が必要でした。しかし、ファイルマッピングを使うと、ファイルのデータをメモリのデータと同じようにポインタで参照・変更できるようになります。これによって、いちいちバッファを確保・開放しないでも良くなるわけで、ちょっとしたデータを扱う時などには便利でしょう。
 今回は、ビットマップファイルをファイルマッピングして編集してみました。

ファイルマッピングの操作

 ファイルマッピングを使うには、まずマップするファイルを用意し、そのファイルのハンドルからCreateFileMappingでファイルマッピングオブジェクトを作成します。

  HANDLE CreateFileMapping(
      HANDLE                 hFile,
      LPSECURITY_ATTRIBUTES  lpFileMappingAttributes,
      DWORD                  flProtect,
      DWORD                  dwMaximumSizeHigh,
      DWORD                  dwMaximumSizeLow,
      LPCTSTR                lpName);

 hFileはファイルのハンドルで、CreateFileなどが返すハンドルを渡す事になります。lpFileMappingAttributesはハンドルの継承などの設定ですが、通常はNULLで良いでしょう。flProtectは保護属性で、読み込み専用ならPAGE_READONLYを、読み書きならPAGE_READWRITEを指定します。ただし、この保護属性はCreateFileで指定したファイルの保護属性と一致している必要があるので注意してください。
 dwMaximumSizeHigh、dwMaximumSizeLowはファイルマッピングオブジェクトのサイズ指定です。dwMaximumSizeHighは上位32ビット、dwMaximumSizeLowは下位32ビットです。まあ、4GB以上のファイルマッピングオブジェクトを使う機会はまずないでしょうから、dwMaximumSizeHighは0、dwMaximumSizeLowに実際のサイズを入れる事になりますね。最後のlpNameは、ファイルマッピングオブジェクトに「名前」をつけるものですが、今回はいらないのでNULLにしておきます。

 ファイルマッピングオブジェクトを作成したら、ファイルをMapViewOfFileでプロセスのアドレス空間にマッピングできるようになります。

  LPVOID MapViewOfFile(

      HANDLE hFileMappingObject,
      DWORD  dwDesiredAccess,
      DWORD  dwFileOffsetHigh,
      DWORD  dwFileOffsetLow,
      DWORD  dwNumberOfBytesToMap);

 hFileMappingObjectは、CreateFileMappingで返されるファイルマッピングオブジェクトのハンドルを指定します。dwDesiredAccessは、アクセスの種類で読み込み専用はFILE_MAP_READ、読み書きはFILE_MAP_WRITEを指定します。ただし、FILE_MAP_WRITEを指定する時はファイルマッピングオブジェクトを作成する時にPAGE_READWRITEを指定しておかなければなりません。
 dwFileOffsetHighdwFileOffsetLowは、マップするファイルのオフセットです。ファイルの先頭から指定した分だけ後ろからファイルがマップされます。最後のdwNumberOfBytesToMapは、マップするバイト数ですが、0を指定するとファイルマッピングオブジェクト全体がマップされるので、今回は0にしましょう。

 以上の処理で、MapViewOfFileが返すポインタでマップされたファイルを操作できるようになりました。後は、通常のバッファと同様の操作でファイルを読み書きできます。
 例として、test.bmpというファイルを読み書き属性でマップし、その先頭アドレスをポインタlpBufに取得してみましょう。

  fh=CreateFile("test.bmp",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,NULL); /* ファイルオープン */

  /* ファイルマッピングオブジェクト作成 */
  hMap=CreateFileMapping(fh,NULL,PAGE_READWRITE,0,0,NULL);

  /* ファイルをマップ */
  lpBuf=(LPBYTE)MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0);

 ファイルマッピングを使い終わったら、まずUnmapViewOfFileでマップしたファイルをアンマップします。次にファイルマッピングオブジェクトとファイルのハンドルをクローズし、ファイルマッピングを閉じます。
 またディスク上のファイルをマップしている時は、書き込みが終わったらFlushViewOfFileで書き込んだ内容をファイルに反映させておくと良いでしょう。

・終了処理の例

  UnmapViewOfFile(lpBuf);
  CloseHandle(hMap);
  CloseHandle(fh);

 なお、ファイルマッピングはディスク上のファイルだけでなくメモリのマッピングも出来ます。これは、他のプロセスとデータを共有する時に使うみたいですね。あるプロセスでメモリを確保し名前付でメモリをマップすれば、他のプロセスでも名前を元にそのファイルマッピングオブジェクトを開いてマップした領域を参照・変更可能になる、という事のようです。

プログラム

 今回のプログラムは、実行ファイルと同じディレクトリ(カレントディレクトリ)にあるtest.bmpというファイル名のファイルをマップする例です。実行ファイルと同じディレクトリに24ビットフルカラービットマップファイルをコピーし、ファイル名をtest.bmpに変更してから実行してください。
 実行すると、自動的にビットマップファイルを読み込んでアドレス空間にマップしビットマップを表示します。その後、「白黒化」ボタンをクリックすると、マップしたビットマップファイルに対して白黒化処理を行い、ファイル内容を書き換えます。

  void toGray(void) { /* ビットマップファイルを白黒化して表示 */

      int i,j;
      BYTE r,g,b,gray;

      for (i=0;i<iHeight;i++) /* 白黒化処理 */
          for (j=0;j<iWidth;j++) {

              b=lpBMP[j*3+i*iLength];
              g=lpBMP[j*3+i*iLength+1];
              r=lpBMP[j*3+i*iLength+2];

              gray=(BYTE)(r*0.3+g*0.59+b*0.11);

              FillMemory(lpBMP+j*3+i*iLength,3,gray);

          }

      FlushViewOfFile(lpBuf,0);

      InvalidateRgn(hwnd,NULL,FALSE);
      UpdateWindow (hwnd);             /* 再描画 */

  }

・警告

 このプログラムは、カレントディレクトリにあるtest.bmpの内容を不可逆的に変更します。ファイルを良く確認して、必ずコピーしたファイルを使用してください。

プログラムソース表示
戻る

APIで学ぶWindows徹底理解―できるプログラマになるための