Bmp画像をファイルマッピングしたい

235 views
Skip to first unread message

mbbbg01

unread,
Sep 7, 2020, 8:17:19 AM9/7/20
to radstu...@googlegroups.com
tonboです。
長文失礼します。

2つのアプリを立ち上げ、Write側でBmp画像をファイルマッピングし、
Read側でそれを参照したいのですがエラーになります。
アドバイスお願い致します。

【環境】Windows7,10 64Bit Pro、Delphi10.3.3

【異常】Write側を立ち上げた後、Read側を立ち上げると
Read側の
System.Move(P^, xBmp, n);で、
エラー「プロジェクトRead.exeは例外クラス $C0000005(メッセージ
'access violation at 0x004070b7:write of address 0x00619000')を
送出しました。」
が出ます。

【ソース Write側】

FormにTImageを配置(256*256)
適当なBmpファイル Lenna.bmpを用意(256*256)

unit Unit1;

interface

uses
Windows, Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
public
end;

var
Form1: TForm1;
Handle1 : THandle;
P : Pointer;
xBmp : TBitMap;
n : Cardinal;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
xBmp := TBitmap.Create;
xBmp.Width := 256;
xBmp.Height := 256;
xBmp.PixelFormat := pf24bit;

//n := SizeOf(xBmp); //n=4となる
n := 256*256; //ダミー

xBmp.LoadFromFile('Lenna.bmp'); //256*256
Image1.Picture.Assign(xBmp); //確認用、無くても良い

Handle1:=CreateFileMapping( $FFFFFFFF, nil, PAGE_READWRITE, 0, n,
'MemoryMap');
if Handle1 = 0 then
raise Exception.Create('マッピングできない');

P := MapViewOfFile(Handle1,FILE_MAP_WRITE,0,0,0);
System.Move(xBmp, P^, n);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
UnmapViewOfFile(P);
CloseHandle(Handle1);
end;

end.

【ソース Read側】

unit Unit1;

interface

uses
Windows, Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
public
end;

var
Form1: TForm1;
Handle1 : THandle;
P : Pointer;
xBmp : TBitMap;
n : Cardinal;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
xBmp := TBitmap.Create;
xBmp.Width := 256;
xBmp.Height := 256;
xBmp.PixelFormat := pf24bit;

//n := SizeOf(xBmp); //n=4となる
n := 256*256; //ダミー

Handle1 := OpenFileMapping(FILE_MAP_READ,False,'MemoryMap');
if Handle1 =0 then
raise Exception.Create('Error');

P := MapViewOfFile(Handle1,FILE_MAP_READ,0,0,0);
System.Move(P^, xBmp, n); //ここでエラー発生
Image1.Picture.Assign(xBmp); //確認用
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
UnmapViewOfFile(P);
CloseHandle(Handle1);
end;

end.

【補足】
・質問:xBmpのバイト数を知る方法はありますか?
 (SizeOf(xBmp)は4になるます)。
 正しいバイト数にすれば、動くかもしれません。
 現在 256*256 にしていますが、これはダメです・・・
 
・256*256=65536バイトのByte配列を用意し、ScanLineを使って、
 xBmpの中身を配列に移して、配列をメモリマップすれば
 できるかもしれませんが、Write側、参照するRead側も処理に
 時間がかかるため、したくありません。
 先頭アドレスとバイト数が共有できれば十分と思います。

Bmp画像を、簡単にどうしたらファイルマッピンできるか、
ご教示お願い致します。

Tarou Takagi, Imageom

unread,
Sep 7, 2020, 6:24:17 PM9/7/20
to radstu...@googlegroups.com
おはようございます、イマジオムの高木です。 いつもお世話に
なっております。

tonboさん:
> 2つのアプリを立ち上げ、Write側でBmp画像をファイル
> マッピングし、Read側でそれを参照したいのですがエラーに
> なります。

 ソースコードをざっと眺めただけですが、ビットマップを
ストリーム化したり、逆にストリームをビットマップに戻したり
する部分が見当たらないので、根本的な方法に間違いがあると
思います。 やり方としては──

■書き込み側:

  1.メモリマップトファイルを作成/参照する。

  2.メモリストリームを作成する。

      MemoryStream:=TMemoryStream.Create;

  3.ビットマップの内容をメモリストリームに書き出す。

      Bitmap.SaveToStream(MemoryStream);

  4.メモリストリームの内容をメモリマップトファイルに
    コピーする。

      Move(MemoryStream.Memory^,P^,...);

  5.使い終わったリソースを破棄する。

■読み出し側

  1.メモリマップトファイルを参照する。

  2.メモリストリームを作成する。

      MemoryStream:=TMemoryStream.Create;

  3.メモリマップトファイルの内容をメモリストリームに
    コピーする。

      Move(MemoryStream.Memory^,P^,...);

  4.メモリストリームの内容をビットマップから読み出す。

      Bitmap.LoadFromStream(MemoryStream);

  5.不要なリソースを破棄する。

──という流れになるはずです。


 TBitMap などのオブジェクト型変数をそのまま他のプロセスに
渡すことはできません。 オブジェクト型変数は実質的には
ポインタなのですが、プロセスが違うとアドレス空間も別なので、
アクセス違反例外が発生してしまいます。 変数そのものではなく
その中身を渡すようにしてください。
――――――――――――――――――――――――――――――――――――
株式会社イマジオム 代表取締役 高木太郎
〒316-0024 茨城県 日立市 水木町 1-11-10
電話:0294-28-0147
ファクシミリ:0294-28-0148
携帯電話:090-8177-5709
電子メール:tarou_...@imageom.co.jp
ウェブサイト:http://www.imageom.co.jp/

mbbbg01

unread,
Sep 8, 2020, 7:54:56 AM9/8/20
to radstu...@googlegroups.com
高木様

ご無沙汰しています。
いつもお世話になっています。

MemoryStreamの使い方を詳しく教えて頂きありがとうございます。
MemoryStreamは知りませんでした。
 Bitmap.SaveToStream(MemoryStream);
 Bitmap.LoadFromStream(MemoryStream);
で、Bitmapを完全に戻せました。
強力ですね。これから活用させて頂きます。
ありがとうございました。

さて、MemoryStreamを以下のように入れてみましたが、
Read側の
System.Move(P^, MemoryStream.Memory^, n);
で、前回同様のエラーが出ました。
色々やってみましたが、解決できませんでした。

高木さんのご指摘
「TBitMap などのオブジェクト型変数を・・・その中身を
渡すようにしてください。」を今回もしている気がします。

やはり、ScanLineを使って、BitMapの中身を65536バイトの配列に
移して共有するようなことをするしかないのでしょうか?
大きな画像では時間がかかり使えません。

------------------------------------------------------
【Write側】
unit Unit1;

interface

uses
Windows, Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;

type
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
public
end;

var
Form1: TForm1;
Handle1 : THandle;
P : Pointer;
xBmp : TBitMap;
n : Cardinal;
MemoryStream : TMemoryStream;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
MemoryStream := TMemoryStream.Create;
xBmp := TBitmap.Create;
xBmp.LoadFromFile('Lenna.bmp');
Image1.Picture.Assign(xBmp); //確認用

xBmp.SaveToStream(MemoryStream);
n := MemoryStream.Size; //n=196662

Handle1:=CreateFileMapping( $FFFFFFFF, nil, PAGE_READWRITE, 0, n, 'MemoryMap');
if Handle1 = 0 then
raise Exception.Create('マッピングできない');

P := MapViewOfFile(Handle1,FILE_MAP_WRITE,0,0,0);
System.Move(MemoryStream.Memory^, P^, n);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
MemoryStream.Free;
xBmp.Free;

UnmapViewOfFile(P);
CloseHandle(Handle1);
end;

end.

------------------------------------------------
【Read側】
unit Unit1;

interface

uses
Windows, Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
TForm1 = class(TForm)
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
public
end;

var
Form1: TForm1;
Handle1 : THandle;
P : Pointer;
xBmp : TBitMap;
n : Cardinal;
MemoryStream : TMemoryStream;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
MemoryStream := TMemoryStream.Create;
xBmp := TBitmap.Create;

Handle1 := OpenFileMapping(FILE_MAP_READ,False,'MemoryMap');
if Handle1 =0 then
raise Exception.Create('Error');

P := MapViewOfFile(Handle1,FILE_MAP_READ,0,0,0);
n := 196662; //Writeと同じ値。手抜き
System.Move(P^, MemoryStream.Memory^, n); //ここでエラー

xBmp.LoadFromStream(MemoryStream);
Image1.Picture.Assign(xBmp); //確認用
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
MemoryStream.Free;
xBmp.Free;

UnmapViewOfFile(P);
CloseHandle(Handle1);
end;

end.

Tarou Takagi, Imageom

unread,
Sep 8, 2020, 9:50:01 AM9/8/20
to radstu...@googlegroups.com
こんばんは、イマジオムの高木です。 いつもお世話になって
おります。

tonboさん:
> さて、MemoryStreamを以下のように入れてみましたが、
> Read側の
> System.Move(P^, MemoryStream.Memory^, n);
> で、前回同様のエラーが出ました。
> 色々やってみましたが、解決できませんでした。
>
> 高木さんのご指摘
> 「TBitMap などのオブジェクト型変数を・・・その中身を
> 渡すようにしてください。」を今回もしている気がします。
>
> やはり、ScanLineを使って、BitMapの中身を65536バイトの配列に
> 移して共有するようなことをするしかないのでしょうか?
> 大きな画像では時間がかかり使えません。

 いいえ、TBitmap.SaveToStream(MemoryStream) でビットマップの
「中身」が MemoryStream.Memory の指すメモリに入りますから
ScanLine などを使う必要はありません。

 Move(P^, MemoryStream.Memory^, n) を呼び出す前に、Memory-
Stream のサイズを確保しておく必要があります。 そうしないと
0バイトのメモリにビットマップの中身を流し込もうとしてエラーに
なってしまいます。 次の1行を忘れずに。

 MemoryStream.Size:=n;

mbbbg01

unread,
Sep 8, 2020, 6:13:09 PM9/8/20
to radstu...@googlegroups.com
高木様

tonboです。
続けてのご指摘、ありがとうございます。

>  Move(P^, MemoryStream.Memory^, n) を呼び出す前に、Memory-
> Stream のサイズを確保しておく必要があります。 そうしないと
> 0バイトのメモリにビットマップの中身を流し込もうとしてエラーに
> なってしまいます。 次の1行を忘れずに。
>
>  MemoryStream.Size:=n;

Read側
n := 196662; の下に
MemoryStream.Size := n;
を追加し解決しました!

MemoryStreamでBMP以外もメモリマップできそうです。
とても大きいです。
10日ぐらい悩んでいました・・・
本当にありがとうございました!

Mr.XRAY

unread,
Sep 9, 2020, 1:54:50 AM9/9/20
to radstu...@googlegroups.com
こんにちは,Mr.XRAY です.
ビットマップのサイズを気にしてされているようですが,
ビットマップのサイズも共有メモリに格納してしまうのはどうでしょうか.

共有メモリに,最初にビットマップのサイズを書き込みます.
その後にビットマップのデータを書き込みます.

読み出す時は,最初にビットマップのサイズを読み出して.
メモリストリームのサイズをその値にしてビットマップをロードするという方法
です.


[書き込み側]
LBmpSize は Integer 型の変数

LBmpSize := MemoryStream.Size;
n := SizeOf(LBmpSize) + LBmpSize;

CopyMemory(Pointer(NativeUInt(P)), @LBmpSize, SizeOf(LBmpSize));
CopyMemory(Pointer(NativeUInt(P) + SizeOf(LBmpSize)), MemoryStream.
Memory, LbmpSize);


[読み出し側]

CopyMemory(@LBmpSize, Pointer(NativeUInt(P)), SizeOf(LBmpSize));
MemoryStream.Size := LBmpSize;
CopyMemory(MemoryStream.Memory, Pointer(NativeUInt(P) + SizeOf
(LBmpSize)), LBmpSize);

mbbbg01

unread,
Sep 9, 2020, 8:48:15 AM9/9/20
to radstu...@googlegroups.com
Mr.XRAY様

tonboです。
MemoryStream.Sizeを読み出し側にどう渡そうか、検討するつもりでした。
詳細に教えて頂きありがとうございました。
このままで、動作しました。
(NativeUInt(P) 、見たことがなくこれから勉強します)

Mr.XRAYさんの「Delphi Liblary」にはいつもお世話になっています。
最近では記事を参考に、DelphiにOpenCV 2.4.13をインストールして
使用し始めました。
この場をお借りしてお礼申し上げます。

Mr.XRAY

unread,
Sep 9, 2020, 10:17:02 PM9/9/20
to radstu...@googlegroups.com
こんにちは,Mr.XRAY です.
ゴメンなさい.

CopyMemory(Pointer(NativeUInt(P)), @LBmpSize, SizeOf(LBmpSize));
     ↓
CopyMemory(P, @LBmpSize, SizeOf(LBmpSize));

でいいですね.P はホインタですから,
わざわざアドレスにして,またポインタにする必要はありませんね (汗).

CopyMemory(Pointer(NativeUInt(P) + SizeOf(LBmpSize)), MemoryStream.
Memory, LbmpSize);

の方は,アドレスを加算する必要があるので,一度アドレスに変換して,
加算後にまたポインタにします.

mbbbg01

unread,
Sep 10, 2020, 7:06:52 AM9/10/20
to radstu...@googlegroups.com
tonboです。

とんでもないです。続けてありがとうございます。
送信側、受信側をこちらに変えて、同じ動作を確認しました。

解説頂き、今回のポインタの操作、理解できました。
自分では、ポインタの中身をいじってまたポインタに戻すような
発想は無いため、勉強になります。
ありがとうございました。
にアクセスしてください。

Mr.XRAY

unread,
Sep 13, 2020, 6:20:48 AM9/13/20
to radstu...@googlegroups.com
こんにちは,Mr.XRAY です.
私の場合,誤字脱字もそうですが,こういうポカが多いんです.

共有メモリについては,他のアプリの情報取得や DLL 内の共有変数の管理等
で使用するサンプルは私のサイトでも掲載しています.
今回のような,プロセス間でデータを共有するというのはありません.
興味を持ちました.で,記事にしてみました.

[ 共有メモリを使用して異なるプロセス間でデータを共有 ]
http://mrxray.on.coocan.jp/Delphi/Others/CommonMemory.htm

mbbbg01

unread,
Sep 14, 2020, 5:31:39 PM9/14/20
to radstu...@googlegroups.com
Mr.XRAY様

いつもお世話になっています。
これだけの内容を短時間でUPされ、感服しています。

Windows7,10 64Bit Pro + Delphi10.3.3 で、全て記事と同じ動作を
することを確認しました。
05_共有メモリのサイズを取得
も貴重な情報です。
理解の途中ですが、まずはお礼申し上げます。

今回を足掛かりに、
・ビットマップ以外の画像データの共有
・Delphiと他言語アプリでの画像データの共有
に進んでいけたらと思っています。
特に2番目は道は遠いですが。

ありがとうございました。

   tonbo
Reply all
Reply to author
Forward
0 new messages