Win32になってから、プロセスが異なるとお互いのメモリを見ることができなくなっています。これは安全性のためなんでしょうね。よそのプログラムからメモリをいじられるというのはマズイだろうと。しかし、プログラムによっては、同じメモリ領域を異なるプロセスから参照したい場合もあるわけです。
プロセスが異なっても見えるデータというのは「ファイル」ですよね。しかしファイルは基本的にハードディスクなどの外部記憶装置に置かれているものです。これにいちいちアクセスしていたのでは処理速度が低下してしまいます。なんとかそのファイルをメモリ上につくれないか?つくれるんですな、実は。これを「メモリマップドファイル(Memory Mapped File)」といって、その名の通り、メモリ上にマップされたファイルなのです(以降、MMFと呼びます)。
では、その使い方。MMFをつくるには CreateFileMapping() Win32API関数を使用します。これで一旦できたMMFは、破棄されるまで何度でも開いたり閉じたりすることができます。また、既に他のプロセスによって同じMMFがつくられている場合は、Windows内部で参照数がカウントアップされて、同じMMFのハンドルが得られます。
MMFを開くには OpenFileMapping() Win32API関数を使用します。これでファイルハンドルが得られます。開いただけでは見えないので、これを見るためのビューを割り当てます。これは MapViewOfFile() で行います。見終わったら、まず UnmapViewOfFile() でビューを解放。で、CloseHandle() でファイルハンドルを閉じます。
使い終わったMMFは、プログラム終了時などに必ず破棄します。破棄は CloseHandle() でMMFのハンドルを渡せば良いです。その一連のコード例を書いてみます。
{
共有するデータの型 } type PMMFData = ^TMMFData; TMMFData = packed record // ここは任意に変更 valwrd: WORD; valint: Integer; valdbl: Double; end; { MMFを作成してそのハンドルを返す } function CreateMMF(MMFName: String): THandle; // MMFName はMMFの名前 begin // MMF作成 Result:= CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(TMMFData), PChar(MMFName)); end; { MMFのビューを開く } procedure OpenMMF(var h: THandle; var P: Pointer; MMFName: String); begin // MMFを開く h := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, MMFName); // MMFのビューを取得 if h <> 0 then P := MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0); end; { MMFのビューを閉じる } procedure CloseMMF(h: THandle; P: Pointer); begin // MMFのビューを解放 UnmapViewOfFile(P); // MMFを閉じる CloseHandle(h); end; { MMFを解放 } procedure FreeMMF(h: THandle); begin // MMFの解放 CloseHandle(h); end; |
ここで TMMFData というのは何でもいいです。ただ、共有するプロセス同士で同じ型である必要があります。
では、上記の関数を使って実際にデータの読み書きをしてみます。
const MMF_NAME = 'MEMORY_MAPPED_FILE_NAME'; // MMFの名前 何でもいい type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private 宣言 } hMMF: THandle; public { Public 宣言 } end; { フォーム生成時 } procedure TForm1.Create(Sender: TObject); begin // MMF作成 hMMF:= CreateMMF(MMF_NAME); end; { フォーム破棄時 } procedure TForm1.FormDestroy(Sender: TObject); begin // MMF解放 FreeMMF(hMMF); end; { ボタンクリックで TData の valint を表示してみる } procedure TForm1.Button1Click(Sender: TObject); var h : THandle; p : Pointer; buf : Integer; begin // ビューを開く OpenMMF(h, p, MMF_NAME); if h <> 0 then begin // MMFに保持されている valint を取得 buf:= PMMFData(p)^.valint; // ポップアップ表示 ShowMessage(Format('valint = %d', [buf])); // ビューを閉じる CloseMMF(h, p); end; end; { ボタンクリックで TData の valint を変更してみる } procedure TForm1.Button2Click(Sender: TObject); var h : THandle; p : Pointer; begin // ビューを開く OpenMMF(h, p, MMF_NAME); if h <> 0 then begin // MMFに保持されている valint を 1 に変更 PMMFData(p)^.valint := 1; // ビューを閉じる CloseMMF(h, p); end; end; |
ちなみに、Windows内部では CreateFileMapping() でつくられた、若しくは参照されたMMFの数を数えていて、CreateFileMapping() をするたびにカウントアップし、CloseHandle() するたびにカウントダウンしています。このカウント数が 0 になると自動的にMMFは消滅します。よくできてますな。
既に宣言されている変数を別の型として操作したい場合に便利な方法です。Delphi では absolute 宣言で絶対アドレスが指定できるので、これを利用します。
次の例は、Double型の変数をByte型配列として扱い、バイトオーダを逆順にする例です
procedure SwapDouble(var
src: Double); var ary : Array [0..7] of Byte absolute src; // srcと同じメモリ領域を占有 tmp : Array [0..7] of Byte; i : Integer; begin // まずテンポラリに全てコピー ZeroMemory(@tmp, 8); CopyMemory(@tmp, @ary, 8); // 1byteずつ入れ替え for i:= 0 to 7 do begin ary[i]:= tmp[7-i]; // ary を変更することで src も変更される end; end; |
これはいろいろ応用ができそうです。
[ Delphi へ戻る ]