ファイルのデータを操作する時には、まずバッファを確保してファイルを読み込み、データを編集。そして、編集したデータをまたファイルに書き戻す、という操作が必要でした。しかし、ファイルマッピングを使うと、ファイルのデータをメモリのデータと同じようにポインタで参照・変更できるようになります。これによって、いちいちバッファを確保・開放しないでも良くなるわけで、ちょっとしたデータを扱う時などには便利でしょう。
今回は、ビットマップファイルをファイルマッピングして編集してみました。
ファイルマッピングを使うには、まずマップするファイルを用意し、そのファイルのハンドルから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を指定しておかなければなりません。
dwFileOffsetHighとdwFileOffsetLowは、マップするファイルのオフセットです。ファイルの先頭から指定した分だけ後ろからファイルがマップされます。最後の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の内容を不可逆的に変更します。ファイルを良く確認して、必ずコピーしたファイルを使用してください。