在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 endend程序的代码主要分为两部分,comm.pas单元中定义几个对操作内存映射的函数,以及共享内存的结构信息。代码如下:
...{ 作者: wudi_1982 联系方式: wudi_1982@hotmail.com 开发工具以及平台:DELPHI7+WINXP 转载请注明出处}unit comm;interfaceuses Windows,SysUtils;const FILEMAPPINGNAME = 'MyFileMapping'; // 指定内存映射的名字 MUTEXNAME= 'MutexName'; //互斥对象的名字typeTShareMem = 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);//对共享内存进行写操作implementationfunction 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;interfaceuses 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;implementationuses 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;
转载请注明出处