舞动的代码

不是因为看到希望才坚持,是坚持了才有可能看到希望

   :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

概述

在win32中,通过使用映像文件在进程间实现共享文件或共享内存数据块,如果利用相同的映像名字或文件句柄,则不同的进程可以通过一个指针来读写一个文件或同一个内存数据块,并把它当做该进程内地址空间的一部分

在Windows9x/NT/200 向内存中装载文件时,使用了特殊的全局内存区。在该区域内,应用程序的虚拟内存地址和文件中的响应位置对应,由于所有进程恭喜了一个用于存储映像文件的全局内存区域,因而当两个进程装载相同模块(应用程序exe或dll文件)时,他们实际上是在内存中共享其执行代码

内存映像文件可以映射一个文件、一个文件中的指定区域或者指定的内存块,其中的数据就可以用内存读写指令直接访问,而不比频繁的调用ReadFile或WriteFIle这样的I/O系统函数,从而提高了文件存取速度和效率

示例代码

两个exe之间共享数据

发送数据端


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

const
  WM_DATA = WM_USER + 1024;

type
  PShareMem = ^TPShareMem;

  TPShareMem = record
    //共享数据
    Data: array[0..255] of Char;
  end;

  TForm1 = class(TForm)
    btn1: TButton;
    procedure btn1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  PShare: PShareMem;

implementation

{$R *.dfm}

var
  HMapping: THandle;
  HMapMutex: THandle;

const
  MAP_FILE_SIZE = 1000;
  REQUEST_TIME_OUT = 1000;

  {打开建立共享内存}
procedure OpenMap;
begin
  {创建一个映像文件}
  HMapping := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(TPShareMem), PChar('MapName'));

  if HMapping = 0 then
  begin
    ShowMessage('不能创建内存映像文件');
    Exit
  end;
    {将映像文件映射到进程的地址空间}
  PShare := PShareMem(MapViewOfFile(HMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0));
  if PShare = nil then
  begin
    CloseHandle(HMapping);
    ShowMessage('映像文件在内存中不存在');
    Application.Terminate;
    Exit
  end;
end;

{关闭共享内存}
procedure CloseMap;
begin
  if PShare <> nil then
  begin
  {从进程的地址空间中撤销映像文件}
    UnmapViewOfFile(PShare);
  end;

  if HMapping <> 0 then
  begin
    {关闭映像文件}
    CloseHandle(HMapping);
  end;

end;

{建立互斥对象}
function LockMap: Boolean;
begin
  Result := True;
  HMapMutex := CreateMutex(nil, False, PChar('My MUTEX NAME COES HERE'));
  if HMapMutex = 0 then
  begin
    ShowMessage('不能创建互斥对象');
    Result := False;
  end
  else
  begin
    if WaitForSingleObject(HMapMutex, REQUEST_TIME_OUT) = WAIT_FAILED then
    begin
      ShowMessage('不能对互斥对象枷锁');
      Result := False;
    end;
  end;

end;

procedure UnlockMAP();
begin
  {释放、关闭互斥对象}
  ReleaseMutex(HMapMutex);
  CloseHandle(HMapMutex);
end;

procedure TForm1.btn1Click(Sender: TObject);
var
  str: PChar;
begin
  str := PChar('简单的共享内存示例');
  //把数据拷贝到共享内存
  CopyMemory(@(pshare^.Data), str, Length(str)*SizeOf(Char));
  //发送消息表明有数据
  PostMessage(FindWindowW(nil, 'Form2'), WM_DATA, 1, 2);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  OpenMap();
  LockMap();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  UnlockMAP();
  CloseMap();
end;

end.

  • 【Delphi下深入Windows编程】一书中CopyMemory使用的长度是Length(str),但是在Delphi增加了Unicode字符支持以后这样会导致长度计算不够,只发送半截字符

  • 参考万一的博客中用ByteLength函数解决,但是也有人说,ByteLength函数只能对Unicode字符串求字节长度,如果要对Ansi字符串进行计算,那么结果会是正确值的两倍

  • 最终解决方案:Length(str)*SizeOf(Char)

接收数据端


unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

const
  //自定义消息
  WM_DATA = WM_USER + 1024;

type
  PShareMem = ^TPShareMem;

  TPShareMem = record
    //共享数据    注意要与发送数据段的定义相同
    Data: array[0..255] of Char;
  end;

type
  TForm2 = class(TForm)
    mmo1: TMemo;
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure getShareInfo(var Msg: TMessage); message WM_DATA;
  end;

var
  Form2: TForm2;
  PShare: PShareMem;
  MapHandle: THandle;

implementation

{$R *.dfm}

{ TForm2 }

{处理wm_data 自定义消息}
procedure TForm2.btn1Click(Sender: TObject);
begin
  CloseHandle(MapHandle);
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  MapHandle := OpenFileMapping(FILE_MAP_WRITE, False, PChar('MapName'));
  if MapHandle = 0 then
  begin
    ShowMessage('不能定位内存映像文件块');

  end;
  {将映像文件映射到进程的地址空间}
  PShare := PShareMem(MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0));
  if PShare = nil then
  begin
    CloseHandle(MapHandle);
    ShowMessage('不能显示映像文件');
    Application.Terminate;
    Exit;
  end;
  FillChar(PShare^, SizeOf(TPShareMem), 0);
end;

procedure TForm2.getShareInfo(var Msg: TMessage);
begin
  {如果是发送数据端发过来的参数是1}
  if Msg.LParam = 2 then
  begin

    //显示共享内存中的数据
    mmo1.Text := PShare^.Data;
  end;
  PShare := PShareMem(MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0));
  if PShare = nil then
  begin
    CloseHandle(MapHandle);
    ShowMessage('不能显示映像文件');
    Application.Terminate;
    Exit;
  end;
  FillChar(PShare^, SizeOf(TPShareMem), 0);
end;

end.

posted on 2018-06-07 14:54  舞动的代码  阅读(1066)  评论(0编辑  收藏  举报