映射文件的使用

在WIN32种,通过使用映像文件在进程间实现共享文件或内存共享,如果利用相同的映像名字或文件句柄,则不同的进程可以通过一个指针来读写同一个文件或者同一内存数据块,并把他们当成该进程内存空间的一部分。
    内存映像文件可以映射一个文件、一个文件中的指定区域或者指定的内存块,其中的数据就可以用内存读取指令来直接访问,而不用频繁的使用操作文件的I/O系统函数,从而提高文件的存取速度和效率。
    映像文件的另一个重要作用就是用来支持永久命名的共享内存。要在两个应用程序之间共享内存,可以在一个应用程序中创建一个文件并映射,然后另外一个程序通过打开和映射此文件,并把它当作自己进程的内存来使用。事实上,此内存是所有进程共享的。 
    下面将先描述一下几个操作内存的API函数
1、创建内存映射的API函数
This function creates a named or unnamed file-mapping object for the specified file.
HANDLE CreateFileMapping(
//通过调用fileopen or FileCreate后返回的文件句柄,如果是内存,则//$FFFFFFFF
  HANDLE hFile, 
   //安全性结构,一般null
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes, 
   //文件试图的保护类型,PAGE_READONLY,PAGE_READWRITE,
  DWORD flProtect, 
  //文件大小的高32位,一般设置为0,除非文件大于4G
  DWORD dwMaximumSizeHigh, 
  //文件大小低32位
  DWORD dwMaximumSizeLow, 
  //映射的名字
  LPCTSTR lpName 
);
2、打开一个映射文件
HANDLE OpenFileMapping(
  //访问数据模式:FILE_MAP_ALL_ACCESS,FILE_MAP_COPY,FILE_MAP_READ,  //FILE_MAP_WRITE
  DWORD dwDesiredAccess,
  //子进程是否可以继承
  BOOL bInheritHandle,
  //映射文件名
  LPCTSTR lpName
);
3、将映射文件映射到本进程的API函数
LPVOID MapViewOfFile( 
  //通过CreateFileMapping或OpenFileMapping返回的文件句柄
  HANDLE hFileMappingObject, 
  //访问的数据模式:FILE_MAP_WRITE,FILE_MAP_READ,FILE_MAP_ALL_ACCESS
  DWORD dwDesiredAccess, 
  //指定数据在映射文件中起始位置的高32位
  DWORD dwFileOffsetHigh, 
  //低32位
  DWORD dwFileOffsetLow, 
  //需要映射的大小,0表示全部
  DWORD dwNumberOfBytesToMap 
);
4、关闭映射的api函数
BOOL UnmapViewOfFile( 
  //由MapViewofFile产生的映射文件的地址
  LPCVOID lpBaseAddress 
);
5、下面例子中还会用到的几个api函数
创建互斥对象
HANDLE WINAPI CreateMutex(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  BOOL bInitialOwner,
  LPCTSTR lpName
);
DWORD WaitForSingleObject( 
  HANDLE hHandle, 
  DWORD dwMilliseconds 
); 
     上文中曾经提到我们使用内存映射的方式来在多个程序或DLL中共享数据。下面就通过一个程序来介绍。
    虽然我要描述的是再两个应用程序之间共享数据,不过为了省事,我将所有的内容都写在一个程序中,你只需要把此程序打开两次就可以了。一个程序用来建立内存映射文件,另外一个程序用来打开内存映射文件。并通过对公共内存的读写操作来演示信息共享。
    程序的窗体单元代码如下:
object Form1: TForm1
  Left = 236
  Top = 147
  Width = 327
  Height = 412
  Caption = ’MyMapForm_1’
  Color = clBtnFace
  Font.Charset = ANSI_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = ’宋体’
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object GroupBox1: TGroupBox
    Left = 0
    Top = 0
    Width = 319
    Height = 121
    Align = alTop
    Caption = ’共享内存的信息[发送]’
    TabOrder = 0
    object Label1: TLabel
      Left = 16
      Top = 24
      Width = 26
      Height = 13
      Caption = ’数据’
    end
    object Label2: TLabel
      Left = 16
      Top = 48
      Width = 39
      Height = 13
      Caption = ’修改者’
    end
    object Label3: TLabel
      Left = 16
      Top = 80
      Width = 52
      Height = 13
      Caption = ’修改时间’
    end
    object edData: TEdit
      Left = 75
      Top = 21
      Width = 230
      Height = 21
      TabOrder = 0
    end
    object edModifyUser: TEdit
      Left = 74
      Top = 49
      Width = 231
      Height = 21
      TabOrder = 1
    end
    object edModifyTime: TEdit
      Left = 74
      Top = 73
      Width = 231
      Height = 21
      Enabled = False
      TabOrder = 2
    end
  end
  object Panel1: TPanel
    Left = 0
    Top = 242
    Width = 319
    Height = 121
    Align = alClient
    BevelOuter = bvNone
    TabOrder = 1
    object btnCreate: TButton
      Left = 32
      Top = 8
      Width = 113
      Height = 25
      Caption = ’新建内存映射’
      TabOrder = 0
      OnClick = btnCreateClick
    end
    object btnOpen: TButton
      Left = 160
      Top = 8
      Width = 113
      Height = 25
      Caption = ’打开已存在映射’
      TabOrder = 1
      OnClick = btnOpenClick
    end
    object btnRead: TButton
      Left = 160
      Top = 45
      Width = 113
      Height = 25
      Caption = ’读取映射信息’
      TabOrder = 2
      OnClick = btnReadClick
    end
    object btnSet: TButton
      Left = 32
      Top = 45
      Width = 113
      Height = 25
      Caption = ’设置内存信息’
      TabOrder = 3
      OnClick = btnSetClick
    end
    object btnClose: TButton
      Left = 32
      Top = 85
      Width = 113
      Height = 25
      Caption = ’关闭映射’
      TabOrder = 4
      OnClick = btnCloseClick
    end
    object btnClear: TButton
      Left = 160
      Top = 85
      Width = 113
      Height = 25
      Caption = ’清空编辑狂’
      TabOrder = 5
      OnClick = btnClearClick
    end
  end
  object StatusBar1: TStatusBar
    Left = 0
    Top = 363
    Width = 319
    Height = 19
    Panels = <
      item
        Width = 200
      end>
  end
  object GroupBox2: TGroupBox
    Left = 0
    Top = 121
    Width = 319
    Height = 121
    Align = alTop
    Caption = ’共享内存的信息[接收]’
    Enabled = False
    TabOrder = 3
    object Label4: TLabel
      Left = 16
      Top = 24
      Width = 26
      Height = 13
      Caption = ’数据’
    end
    object Label5: TLabel
      Left = 16
      Top = 48
      Width = 39
      Height = 13
      Caption = ’修改者’
    end
    object Label6: TLabel
      Left = 16
      Top = 80
      Width = 52
      Height = 13
      Caption = ’修改时间’
    end
    object edRData: TEdit
      Left = 75
      Top = 21
      Width = 230
      Height = 21
      TabOrder = 0
    end
    object edRUser: TEdit
      Left = 74
      Top = 49
      Width = 231
      Height = 21
      TabOrder = 1
    end
    object edRTime: TEdit
      Left = 74
      Top = 73
      Width = 231
      Height = 21
      Enabled = False
      TabOrder = 2
    end
  end
end
   程序的代码主要分为两部分,comm.pas单元中定义几个对操作内存映射的函数,以及共享内存的结构信息。代码如下:
...{
      作者:           wudi_1982
      联系方式:       wudi_1982@hotmail.com
      开发工具以及平台:DELPHI7+WINXP
      转载请注明出处
}
unit comm;
interface
uses
  Windows,SysUtils;
const
  FILEMAPPINGNAME = ’MyFileMapping’; // 指定内存映射的名字
  MUTEXNAME= ’MutexName’; //互斥对象的名字
type
TShareMem = record //共享内存的结构信息
  Data       : array[0..255] of char;  //描述共享数据信息
  ModifyUser : array[0..255] of char;  //对数据的修改者
  ModifyTime : array[0..7] of char;    //数据最近一次的修改时间
end;
PShareMem = ^TShareMem;
var
  FileMapHandle : THandle;  //建立映射的句柄
  MutexHandle : THandle;  // 互斥对象的句柄
  ShareMem : PShareMem;  //一个指向共享内存的指针
function OpenMap:THandle; //打开一个映射文件并映射到本进程中
function CreateMap:THandle; //新建一个映射文件并映射到本进程中
function LockMap:boolean;   //加锁
procedure UnLockMap;        //解锁
procedure CloseMap;         //关闭映射
function ReadCommData:TShareMem;  //从共享信息中读取数据
procedure WriteCommData(data,user,time : string);//对共享内存进行写操作
implementation
function OpenMap:THandle;
begin
  //打开映射文件
  FileMapHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, //所有权限
              false,   //子进程不可继承
              FILEMAPPINGNAME
  );
  if FileMapHandle <> 0 then  //如果映射文件打开成功
  begin
     //将映射文件映射到本进程
     ShareMem := pSharemem(MapViewOfFile(FileMapHandle,FILE_MAP_ALL_ACCESS,0,0,0));
     if ShareMem = nil then
     begin
       CloseHandle(FileMapHandle);
       Result := 0;
     end else begin
       //初始化共享区域
       FillChar(ShareMem^,sizeof(TSharemem),0);
       Result := FileMapHandle;
     end;
  end else Result := 0;
end;
function CreateMap:THandle;
begin
  FileMapHandle := CreateFileMapping($FFFFFFFF,//内存映射
  nil,
  PAGE_READWRITE,//读写操作
  0,//高32位 ,一般为0,除非要映射的文件大于4G
  sizeof(TShareMem),
  FILEMAPPINGNAME
  );
  if FileMapHandle <> 0 then
  begin
    ShareMem := pSharemem(MapViewOfFile(FileMapHandle,FILE_MAP_ALL_ACCESS,0,0,0));
    if ShareMem = nil then
    begin
      CloseHandle(FileMapHandle);
      Result := 0;
    end else Result := FileMapHandle;
  end else Result := 0;
end;
function LockMap:boolean;
begin
   //创建一个互斥对象并加锁
   MutexHandle := CreateMutex(nil,false,MUTEXNAME);
   if MutexHandle <> 0 then
   begin
     if WaitForSingleObject(MutexHandle,1000)= WAIT_FAILED then Result := false
     else Result := true;
   end else Result := false;
end;
procedure UnLockMap;
begin
  //释放资源
  if MutexHandle <> 0 then
  begin
     ReleaseMutex(MutexHandle);
     CloseHandle(MutexHandle);
  end;
end;
procedure CloseMap;
begin
   // 关闭映射并释放资源
   if ShareMem <> nil then UnmapViewOfFile(ShareMem);
   if FileMapHandle <> 0 then CloseHandle(FileMapHandle);
end;
function ReadCommData:TShareMem;
var
  tm : TShareMem;
begin
  with tm do
  begin
    Data := ShareMem^.Data;
    ModifyUser := ShareMem^.ModifyUser;
    ModifyTime := ShareMem^.ModifyTime;
  end;
  Result := tm;
end;
procedure WriteCommData(data,user,time : string);
begin
   StrCopy(ShareMem^.Data,pchar(data));
   StrCopy(ShareMem^.ModifyUser,pchar(user));
   StrCopy(ShareMem^.ModifyTime,pchar(time));
end;
end.
代码的另一个部分就是根据需要调用这些函数的FirstTest.pas,即上面窗体单元对应的代码
...{
      作者:           wudi_1982
      联系方式:       wudi_1982@hotmail.com
      开发工具以及平台:DELPHI7+WINXP
      转载请注明出处
}
unit FirstTest;
interface
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;
const
  WM_MYMESSAGE=WM_USER+1024; //一个自定义消息,用来通知接受程序数据到达
type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    edData: TEdit;
    edModifyUser: TEdit;
    edModifyTime: TEdit;
    Panel1: TPanel;
    btnCreate: TButton;
    btnOpen: TButton;
    btnRead: TButton;
    btnSet: TButton;
    btnClose: TButton;
    btnClear: TButton;
    StatusBar1: TStatusBar;
    GroupBox2: TGroupBox;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    edRData: TEdit;
    edRUser: TEdit;
    edRTime: TEdit;
    procedure btnCreateClick(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnSetClick(Sender: TObject);
    procedure btnCloseClick(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure btnClearClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    atm : TAtom; //一个原子
    nextwindow : string; //被发送消息程序的标题信息
    procedure MyMessage(var msg : TMessage);message WM_MYMESSAGE; //自定义消息的处理
  public
  end;
var
  Form1: TForm1;
implementation
uses comm;
...{$R *.dfm}
procedure TForm1.btnCreateClick(Sender: TObject);
begin
  if CreateMap = 0 then
    ShowMessage(’内存映射建立失败’)
  else begin
    btnCreate.Enabled := false;
    btnOpen.Enabled := false;
    StatusBar1.Panels[0].Text := ’内存映射文件新建立完毕’
  end;
end;
procedure TForm1.btnOpenClick(Sender: TObject);
begin
   if OpenMap = 0 then
     ShowMessage(’内存映射打开失败’)
   else begin
     btnCreate.Enabled := false;
     btnOpen.Enabled := false;
     StatusBar1.Panels[0].Text := ’内存映射文件打开完毕’
   end;
end;
procedure TForm1.btnSetClick(Sender: TObject);
var
  hd : THandle;
begin
   if (edData.Text = ’’) or (edModifyUser.Text = ’’) then
      ShowMessage(’请填写完整信息’)
   else begin
     edModifyTime.Text := FormatDateTime(’mm:hh:mm’,Now);
     WriteCommData(edData.Text,edModifyUser.Text,edModifyTime.Text);
    //查找此程序的另外一个实例,如果找到,发送数据到达的消息
     hd := FindWindow(nil,pchar(nextwindow));
     if hd <> 0 then
       SendMessage(hd,WM_MYMESSAGE,1,0);
   end;
end;
procedure TForm1.btnCloseClick(Sender: TObject);
begin
  UnLockMap;
  CloseMap;
  btnCreate.Enabled := true;
  btnOpen.Enabled := true;
end;
procedure TForm1.btnReadClick(Sender: TObject);
var
  tm : TShareMem;
begin
   tm := ReadCommData;
   edRData.Text := tm.Data;
   edRUser.Text := tm.ModifyUser;
   edrTime.Text := tm.ModifyTime;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  UnLockMap;
  CloseMap;
  //下面这一句非常重要,如果不及时删除原子表中添加的原子,
  //怕是只有重启计算机才能干掉程序启动时添加到原子表中的信息了
  GlobalDeleteAtom(atm);
end;
procedure TForm1.btnClearClick(Sender: TObject);
begin
   edData.Text := ’’;
   edModifyUser.Text := ’’;
   edModifyTime.Text := ’’;
end;
procedure TForm1.MyMessage(var msg: TMessage);
begin
  if msg.WParam = 1 then
  begin
    Application.BringToFront;
    StatusBar1.Panels[0].Text := ’新数据到代’;
    btnReadClick(nil);
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
   //下面的代码将在程序启动时执行,主要是通过原子表检查此程序是否运行,
   //本程序运行运行两个实例,一个用来建立映射文件,
   //一个用来打开映射文件,你完全可以用两个不同的程序来处理,这里为了方便
   //以及演示原子表的使用而采用一个程序执行两次的方法来做
   if GlobalFindAtom(pchar(’wudi_1982’)) <> 0 then//查找原子表如果第一个窗体已经存在
   begin
      if GlobalFindAtom(pchar(’jingyang’)) <> 0 then//如果第二个窗体也存在
      begin
        Application.Terminate;
      end else begin
        //添加原子到原子表,以记录此程序的第二个实例已经运行,并做相应操作
        atm := GlobalAddAtom(pchar(’jingyang’));
        Application.Title := ’MyMapForm_2’;
        Form1.Caption := ’MyMapForm_2’;
        nextwindow := ’MyMapForm_1’;
      end;
   end else begin
     //添加原子到原子表,以记录此程序的第一个实例已经运行,并做相应操作
     atm := GlobalAddAtom(pchar(’wudi_1982’));
     Application.Title := ’MyMapForm_1’;
     Form1.Caption := ’MyMapForm_1’;
     nextwindow := ’MyMapForm_2’;
   end;
end;
end.
程序运行效果图:

例程的使用方法:
        编译之后,运行此程序的两个实例,在其中一个实例中,点击按钮【新建内存映射】,另一个实例使用【打开已存在的映射】,然后在窗体的发送部分,填写相应信息,然后点击【设计内存信息】,就可以看到效果了。
       注:WINXP+D7;

原文地址:https://www.cnblogs.com/railgunman/p/1900699.html