内存映射文件的原理是,先保留一段虚拟内存空间,再将磁盘文件提交给这段内存空间,并且返回一个指向这段内存空间的指针,就
可以文件的内容了。
使用内存映射文件的好处:1.可以实现不同进程间共享文件或共享内存区域中的数据。2.可访问映射文件的数据,并简化对文件访问的
操作。
内存映射文件的使用步骤:
1.创建或打开一个磁盘文件,调用FileCreate()或FileOpen()函数,它们的声明如下:
function FileCreate(const FileName:String):Integer; //创建由FileName指定文件名的文件,并返回文件句柄。
function FileOpen(const FileName:String;Mode:Word):Integer; //以Mode指定的文件访问模式打开由FileName指定文件名的文件,并返回文件句柄。
FileOpen()的Mode参数值如下:
fmOpenRead 只读模式
fmOpenWrite 只写模式
fmOpenReadWrite 可读写模式
0 即不能读也不能写,如果只想得到文件的属性时,可采用这种模式。
如果需要在不同的应用程序间共享文件的数据,则需要加上以下的模式:
fmShareCompat 与DOS 1.x和DOS 2.x文件控制块兼容的文件共享机制。可以与其他fmShareXXX模式一起使用。
fmShareExclusive 禁止共享
fmShareDenyWrite 不允许他人使用fmOpenWrite模式打开文件
fmShareDenyRead 不允许他人使用fmOpenRead模式打开文件
fmShareDenyNone 允许他人使用任何模式打开文件
2.创建或打开内存映射文件对象,调用CreateFileMapping()或OpenFileMapping()函数,它们的声明如下:
function CreateFileMapping(hFile:THandle; //要映射到内存中的文件的文件句柄,就是调用FileCreate()或FileOpen()函数返回的文件句柄。也可以是值$ffffffff(即 -1),这时表示要创建的内存映射对象只是一段虚拟内存区域,并不与磁盘文件关联,这样也可以在不同进程间实现内存共享。
lpFileMappingAttributes:PSecurityAttributes; //内存映射文件对象的安全属性,此参数通常为nil。
flProtect, //指定文件视图的保护类型,其值必须与打开文件时使用的文件访问模式一致。其值如下:
{
PAGE_READONLY 只读,文件的访问模式必须是fmOpenRead
PAGE_READWRITE 可读写,文件的访问模式必须是fmOpenReadWrite
PAGE_WRITECOPY 可读写,但进行写操作时,只有修改过的页面才被复制。这样,多个进程之间共享的映射文件就不会双倍占用系统内存和其它资源。文件的访问模式必须是fmOpenWrite或fmOpenReadWrite
可以使用位或的方法将扇区属性设置给lpProtect参数,扇区属性如下:
SEC_COMMIT 在内存或页交换文件中为同一扇区中的所有页面分配物理内存地址(默认)
SEC_IMAGE 文件映射信息和属性取自文件映像(Windows9X忽略此属性)
SEC_NOCACHE 未被映射的页面被置入缓存,因此系统允许直接把数据写入磁盘文件(主要用于设备驱动程序,Windows9X忽略此属性)
SEC_RESERVE 保留扇区中的页面,不分配物理存储
}
dwMaximumSizeHigh,dwMaximumSizeLow:DWORD; //分别指定内存映射文件对象的最大尺寸的高32位和低32位。除非要访问的文件大于4GB,否则dwMaximumSizeHigh为 0;dwMaximumSizeLow为0时,表示内存映射文件对象的最大尺寸为被映射的文件的大小。
lpName:PChar //内存映射文件对象的名称,可以是除'/'以外的任何字符组成的字符串。为0时表示创建一个无名对象。
):THandle; //成功则返回内存映射文件对象的句柄,若指定名称的对象已经存在,则GetLastError返回常量ERROR_ALREADY_EXISTS。失败则返回0。
OpenFileMapping()函数参考帮助系统。
3.将磁盘文件的视图映射到进程的地址空间中,调用MapViewOfFile()函数,其声明如下:
function MapViewOfFile(hFileMappingObject:THandle; //内存映射文件对象的句柄,就是调用CreateFileMapping()或OpenFileMapping()返回的句柄。
dwDesiredAccess:DWORD; //指定文件数据的访问模式,其值如下:
{
FILE_MAP_WRITE 可读写,必须用PAGE_READ_WRITE属性调用CreateFileMapping()或OpenFileMapping()
FILE_MAP_READ 只读,必须用PAGE_READ_WRITE或PAGE_READ属性调用CreateFileMapping()或OpenFileMapping()
FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同。
FILE_MAP_COPY 允 许Copy-on-Write模式,即在文件写入的同时进行内容复制。必须用PAGE_READ_ONLY、PAGE_READ_WRITE或 PAGE_WRITE_COPY属性调用CreateFileMapping()或OpenFileMapping()
}
dwFileOffsetHigh,dwFileOffsetLow, //指定文件映射起始位置的偏移量的高32位和低32位。
dwNumberOfBytesToMap:DWORD //指定需要映射的字节数,为0时表示文件的全部。
):Pointer; //成功则返回视图的起始地址,利用该指针就可对映射文件进行操作了。失败则返回nil。
4.取消文件视图的映射,调用UnmapViewOfFile(),其声明如下:
function UnmapViewOfFile(lpBaseAddress:Pointer):BOOL;
该函数解除文件视图与进程地址空间的映射关系,参数为指向映射区域的起始地址。
5.关闭文件映射和文件,调用CloseHandle()和CloseFile()分别关闭文件映射和文件。
以下是应用内存映象文件的例子:
----------第一个程序(即第一个进程)-------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const WM_DATA=WM_USER+1024;//自定义消息常量,用于发送消息
type
{以下的记录类型(也可以是其它数据类型)定义和指向该记录类型(也可以是其它数据类型)的指针类型的定义是必须的}
{定义的记录类型(也可以是其它数据类型)是共享内存区域中的数据的类型,指针类型是为了访问共享内存区域的数据而定义的}
{必须注意的是,下面定义的这两个类型在其它要共享该内存区域的进程中的定义也必须是相同的}
TShareMem=record
Data:array[0..255]of Char;
end;
PShareMem=^TShareMem;
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
PShare:PShareMem;
implementation
{$R *.dfm}
var
HMapping:THandle;
HMapMutex:THandle;
const
MapFileSize=1000;
Request_TimeOut=1000;
procedure OpenMap;
begin
HMapping:=CreateFileMapping($ffffffff,nil,Page_ReadWrite,0,sizeof(TShareMem),PAnsiChar('UserMap'));
if(HMapping=0)then
begin
ShowMessage('不能创建内存映象文件!');
Application.Terminate;
exit;
end;
PShare:=PShareMem(MapViewOfFile(HMapping,File_Map_All_Access,0,0,0));
if(PShare=nil)then
begin
ShowMessage('不能映射内存映象文件!');
Application.Terminate;
exit;
end;
end;
procedure CloseMap;
begin
if(PShare<>nil)then
UnMapViewOfFile(PShare);
if(HMapping<>0)then
CloseHandle(HMapping);
end;
function LockMap:Boolean;
begin
result:=true;
HMapMutex:=CreateMutex(nil,false,PAnsiChar('UserMapObject'));
if(HMapMutex=0)then
begin
ShowMessage('不能创建互斥对象!');
Result:=false;
end
else begin
if(WaitForSingleObject(HMapMutex,Request_TimeOut)=Wait_Failed)then
begin
ShowMessage('不能给互斥对象加锁!');
Result:=false;
end;
end;
end;
procedure UnLockMap;
begin
ReleaseMutex(HMapMutex);
CloseHandle(HMapMutex);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
CopyMemory(@(PShare^.Data),Memo1.Lines.GetText,Length(Memo1.Lines.GetText));
PostMessage(FindWindow(nil,'接收数据'),WM_DATA,1,1);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
OpenMap;
LockMap;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnLockMap;
CloseMap;
end;
end.
---------------第二个程序(即第二个进程)------------
unit Unit2; //该单元的窗体的Caption为"接收数据"
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
const WM_DATA=WM_USER+1024;
type
TShareMem=record
Data:array[0..255]of Char;
end;
PShareMem=^TShareMem;
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure GetShareInfo(var msg:TMessage);message WM_DATA;
public
{ Public declarations }
end;
var
Form1: TForm1;
PShare:PShareMem;
MapHandle:THandle;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1.GetShareInfo(var msg: TMessage);
begin
if(msg.LParam=1)then
begin
Memo1.Text:=PShare^.Data;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MapHandle:=OpenFileMapping(File_Map_Write,false,PAnsiChar('UserMap'));
if(MapHandle=0)then
begin
ShowMessage('不能打开内存映象文件!');
Application.Terminate;
Halt;
end;
PShare:=MapViewOfFile(MapHandle,File_Map_All_Access,0,0,0);
if(PShare=nil)then
begin
CloseHandle(MapHandle);
ShowMessage('不能映射内存映象文件!');
Application.Terminate;
exit;
end;
FillChar(PShare^,sizeof(TShareMem),0);
end;
end.
注意:这两个程序的运行顺序是:先运行第一个程序,再运行第二个程序,再单击第一个程序上的发送数据按钮。