分享

Delphi内存映射函数

 aaie_ 2012-10-18
Delphi内存映射函数

1:CreateFileMapping创建一个有名的共享内存:
  VB声明
   Declare Function CreateFileMapping Lib "kernel32" Alias "CreateFileMappingA" (ByVal hFile As Long, lpFileMappigAttributes As SECURITY_ATTRIBUTES, ByVal flProtect As Long, ByVal dwMaximumSizeHigh As Long, ByVal dwMaximumSizeLow As Long, ByVal lpName As String) As Long
  返回值
   Long,新建文件映射对象的句柄;零意味着出错。会设置GetLastError。即使函数成功,但倘若返回的句柄属于一个现成的文件映射对象,那么 GetLastError也会设置成ERROR_ALREADY_EXISTS。在这种情况下,文件映射的长度就是现有对象的长度,而不是这个函数指定的 尺寸.其他说明:因为windows的全局Handle是唯一的,所以在两个进程中创建的同名内存映射对象返回的Handle值会不同....在使用了函 数MapViewOfFile()之后,必须要有对应的UnmapViewOfFile()调用,否则在进程终止之前,保留的区域将无法释放。
  参数表
  参数 类型及说明
  hFile Long,指定欲在其中创建映射的一个文件句柄。&HFFFFFFFF&表示在内存中创建一个文件映射(创建该类型的内存映射时dwMaximumSizeLow不能为0, 在Delphi中该值以Dword(-1)表示)
  lpFileMappigAttributes SECURITY_ATTRIBUTES,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象(Delphi中nil或者0默认安全对象)
  flProtect Long,下述常数之一:
  PAGE_READONLY 以只读方式打开映射
  PAGE_READWRITE 以可读、可写方式打开映射
  PAGE_WRITECOPY 为写操作留下备份
  可组合使用下述一个或多个常数
  SEC_COMMIT 为文件映射一个小节中的所有页分配内存
  SEC_IMAGE 文件是个可执行文件
  SEC_RESERVE 为没有分配实际内存的一个小节保留虚拟内存空间
  dwMaximumSizeHigh Long,文件映射的最大长度(高32位)
  dwMaximumSizeLow Long,文件映射的最小长度(低32位)。如这个参数和dwMaximumSizeHigh都是零,就用磁盘文件的实际长度
  lpName String,指定文件映射对象的名字。如存在这个名字的一个映射,函数就会打开它。用vbNullString创建一个无名的文件映射
  调用CreateFileMapping的时候GetLastError的对应错误
  ERROR_FILE_INVALID 如果企图创建一个零长度的文件映射
  ERROR_INVALID_HANDLE 内存空间的命名和现有的内存映射, 互斥量, 信号量, 临界区有同名
  ERROR_ALREADY_EXISTS 表示内存空间命名已经存在

2:OpenFileMapping 打开一个现成的文件映射对象
  VB声明
   Declare Function OpenFileMapping Lib "kernel32" Alias "OpenFileMappingA" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal lpName As String) As Long
  返回值
  Long,指定文件映射对象的句柄。零表示出错。会设置GetLastError
  参数表
  参数 类型及说明
  dwDesiredAccess Long,带有前缀FILE_MAP_???的一个常数。参考MapViewOfFile函数的dwDesiredAccess参数的说明
FILE_MAP_ALL_ACCESS
FILE_MAP_COPY
FILE_MAP_READ
FILE_MAP_WRITE
  bInheritHandle Long,如这个函数返回的句柄能由当前进程启动的新进程继承,则这个参数为TRUE
  lpName String,指定要打开的文件映射对象名称

3:MapViewOfFile, MapViewOfFileEx 将一个文件映射对象映射到当前应用程序的地址空间。MapViewOfFileEx允许我们指定一个基本地址来进行映射
  VB声明
   Declare Function MapViewOfFile& Lib "kernel32" (ByVal hFileMappingObject As Long, ByVal dwDesiredAccess As Long, ByVal dwFileOffsetHigh As Long, ByVal dwFileOffsetLow As Long, ByVal dwNumberOfBytesToMap As Long)
  Declare Function MapViewOfFileEx& Lib "kernel32" (ByVal hFileMappingObject As Long, ByVal dwDesiredAccess As Long, ByVal dwFileOffsetHigh As Long, ByVal dwFileOffsetLow As Long, ByVal dwNumberOfBytesToMap As Long, lpBaseAddress As Any)
  返回值
  Long,文件映射在内存中的起始地址。零表示出错。会设置GetLastError
  参数表
  参数 类型及说明
  hFileMappingObject Long,文件映射对象的句柄
  dwDesiredAccess Long,下述常数之一:
  FILE_MAP_WRITE 映射可读可写。文件映射对象必须通过PAGE_READWRITE访问创建
  FILE_MAP_READ 映射只读。文件映射对象必须通过PAGE_READ 或 PAGE_READWRITE访问创建
  FILE_MAP_ALL_ACCESS 与FILE_MAP_WRITE相同
  FILE_MAP_COPY 映射时保留写操作的副本。文件映射对象必须用PAGE_WRITECOPY访问在win95下创建
  dwFileOffsetHigh Long,文件中映射起点的高32位地址
  dwFileOffsetLow Long,文件中映射起点的低32位地址
  dwNumberOfBytesToMap Long,文件中要映射的字节数。用零映射整个文件映射对象
  lpBaseAddress Long,指定映射文件映射对象的地址。如这个地址处没有足够的内存空间,那么对MapViewOfFileEx的调用会失效。零表示允许windows寻找一个地址
  注解
   dwFileOffsetLow和dwFileOffsetHigh必须反映一个偏移距离,它由系统的内存分配精度决定。例如,假设系统的内存精度是 64KB(即最小分配单位是64KB),则这些值必须是64KB的整数倍。大多数应用程序都简单的用零从文件的起始处开始映射。 lpBaseAddress也必须是内存分配精度的整数倍

4:UnmapViewOfFile 在当前应用程序的内存地址空间解除对一个文件映射对象的映射
  VB声明
  Declare Function UnmapViewOfFile& Lib "kernel32" (ByVal lpBaseAddress As Long)
  返回值
  Long,非零表示成功,零表示失败。会设置GetLastError
  参数表
  参数 类型及说明
  lpBaseAddress Long,指定要解除映射的一个文件映射的基准地址。这个地址是早先用MapViewOfFile函数获得的
  注解
  在vb的api文本查看器里复制的声明如下,请注意参数类型不同
  Declare Function UnmapViewOfFile Lib "kernel32" Alias "UnmapViewOfFile" (lpBaseAddress As Any) As Long

内存映射一般使用步骤(如有问题请留言告知,谢谢):
1、获取文件句柄(如果要映射文件)
2、创建内存映射对象CreateFileMaping(映射文件则使用上面的文件句柄,映射内存的一块区域则使用Dword(-1))
3、打开内存映射对象OpenFileMaping
4、获取内存映射的数据地址MapViewOfFile
5、关闭内存映射对象UnmapViewOfFile
6、关闭文件句柄

一个共享字符串的例子:

var
……
hFileMapping:HWND;//指向内存区域的句柄
SetString:pchar;//这里应该使用标准的windows字符串而非delphi特有的string类型
这样我们可以在按纽的单击事件中编写相应的代码:
procedure TForm1.Button1Click(Sender: TObject);
begin
hFileMapping:=CreateFileMapping($FFFFFFFF,nil,Page_ReadWrite,0,11,'MapString');
//$FFFFFFFF表示使用虚拟文件,注意两个工程中应该使用同一个唯一的名称MapString
if hFileMapping=0 then
raise Exception.Create('Error creating map file!');
SetString:=MapViewOfFile(hFileMapping,File_Map_Write,0,0,11);
strcopy(SetString,pchar(Edit1.Text));
//注意这里应该使用拷贝函数而不是直接赋值 SetString:=pchar(Edit1.Text)
end;
当然,我们还可以在这个主工程中编写一个释放内存映象的过程:
procedure TForm1.Button2Click(Sender: TObject);
begin
UnMapViewOfFile(SetString);
if hFileMapping<>0 then
closehandle(hFileMapping);
end;
接下来是第2个工程的按纽单击事件,它和第一个工程几乎没有什么不同,只是我们修改了内存映象的读取权限为只读而以:
var
……
GetString:pchar;
hMapFileMaping:HWND;
procedure TForm1.Button1Click(Sender: TObject);
begin
hMapFileMaping:=CreateFileMapping($FFFFFFFF,nil,Page_ReadOnly,0,11,'MapString');
//再次注意一定要使用唯一的名称(MapString)和第一个工程保持一致
if hMapFileMaping=0 then
raise Exception.Create('Error');
GetString:=MapViewOfFile(hMapFileMaping,File_Map_Read,0,0,11);
Edit1.Text:=String(GetString);
end;

一个传结构的例子:

结构定义
type
PMapViewDataItem = ^TMapViewDataItem;
TMapViewDataItem = record
    ID: Integer;
    Age: Integer;
    Name: array [0..9] of Char;
end;

工程1:
...
private
    MapViewHandle: THandle;
    pItem: PMapViewDataItem;
public
...

procedure TForm1.btnCreateClick(Sender: TObject);
var
A: PSecurityAttributes;
strName: String;
begin
MapViewHandle := CreateFileMapping(Dword(-1), nil, PAGE_READWRITE, 0, SizeOf(TMapViewDataItem), 'MAP_VIEW_TEST');
edtMapHandle.Text := IntToStr(MapViewHandle);
pItem := MapViewOfFile(MapViewHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TMapViewDataItem));
pItem^.ID := 10;
PItem^.Age := 242;
strName := '朋友们';
StrCopy(PITem^.Name, PChar(StrName));
end;

procedure TForm1.btnCloseClick(Sender: TObject);
begin
UnmapViewOfFile(pItem);
CloseHandle(MapViewHandle);
end;

工程2:
...
public
    pItem: PMapViewDataItem;
    MapViewHandle: THandle;
end;
...


procedure TForm1.btnOpenClick(Sender: TObject);
var
ErrorID: DWORD;
begin
MapViewHandle := CreateFileMapping(Dword(-1), nil, PAGE_READWRITE, 0, SizeOf(TMapViewDataItem), 'MAP_VIEW_TEST');
ErrorID := GetLastError ;
// 处理ErrorID
edtMapHandle.Text := IntToStr(MapViewHandle);
pItem := MapViewOfFile(MapViewHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TMapViewDataItem));
edtID.Text := IntToStr(PItem^.ID);
edtAge.Text := IntToStr(PItem^.Age);
edtName.Text := PItem^.Name ;
end;

        内存映射文件本身还是有一些局限性的,譬如一旦您生成了一个内存映射文件,那么您在那个会话期间是不能够改变它的大小的。所以内存映射文件对于只读文件和 不会影响其大小的文件操作是非常有用的。当然这并不意味着对于会引起改变其大小的文件操作就一定不能用内存影射文件的方法,您可以事先估计操作后的文件的 可能大小,然后生成这么大小一块的内存映射文件,然后文件的长度就可以增长到这么一个大小。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多