在WIN32种,通过使用映像文件在进程间实现共享文件或内存共享,如果利用相同的映像名字或文件句柄,则不同的进程可以通过一个指针来读写同一个文件或者同一内存数据块,并把他们当成该进程内存空间的一部分。
内存映像文件可以映射一个文件、一个文件中的指定区域或者指定的内存块,其中的数据就可以用内存读取指令来直接访问,而不用频繁的使用操作文件的I/O系统函数,从而提高文件的存取速度和效率。
映像文件的另一个重要作用就是用来支持永久命名的共享内存。要在两个应用程序之间共享内存,可以在一个应用程序中创建一个文件并映射,然后另外一个程序通过打开和映射此文件,并把它当作自己进程的内存来使用。事实上,此内存是所有进程共享的。
下面将先描述一下几个操作内存的API函数
1、创建内存映射的API函数
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、打开一个映射文件
// 访问数据模式:FILE_MAP_ALL_ACCESS,FILE_MAP_COPY,FILE_MAP_READ, // FILE_MAP_WRITE
DWORD dwDesiredAccess,
// 子进程是否可以继承
BOOL bInheritHandle,
// 映射文件名
LPCTSTR lpName
);
3、将映射文件映射到本进程的API函数
// 通过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函数
//由MapViewofFile产生的映射文件的地址
LPCVOID lpBaseAddress
);
5、下面例子中还会用到的几个api函数
HANDLE WINAPI CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
上文中曾经提到我们使用内存映射的方式来在多个程序或DLL中共享数据。下面就通过一个程序来介绍。
虽然我要描述的是再两个应用程序之间共享数据,不过为了省事,我将所有的内容都写在一个程序中,你只需要把此程序打开两次就可以了。一个程序用来建立内存映射文件,另外一个程序用来打开内存映射文件。并通过对公共内存的读写操作来演示信息共享。
程序的窗体单元代码如下:
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;
转载请注明出处