zoukankan      html  css  js  c++  java
  • 映射文件的使用

    在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;

  • 相关阅读:
    如何在腾讯云上安装Cloud Foundry
    Chrome浏览器扩展程序的本地备份
    如何在Kubernetes里创建一个Nginx service
    如何在Kubernetes里创建一个Nginx应用
    在Mac里给Terminal终端自定义颜色
    linux sed命令详解
    跟我一起写Makefile--- 变量(嵌套变量+追加变量+overrid+多行变量+环境变量+目标变量+模式变量)
    makefile详解 嵌套执行make,定义命令包
    makefile学习笔记(多目录嵌套调用、变量使用)
    Makefile所有内嵌函数
  • 原文地址:https://www.cnblogs.com/railgunman/p/1900699.html
Copyright © 2011-2022 走看看