• 内存映射实现进程通讯


    unit FileMap;


    interface


    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
      StdCtrls, Dialogs;


    type
      //定义TFileMap类
      TFileMap = class(TComponent)
      private
        FMapHandle: THandle; //内存映射文件句柄
        FMutexHandle: THandle; //互斥句柄
        FMapName: string; //内存映射对象
        FSynchMessage: string; //同步信息
        FMapStrings: TStringList; //存储映射文件信息
        FSize: DWord; //映射文件大小
        FMessageID: DWord; //注册的消息号
        FMapPointer: PChar; //映射文件的数据区指针
        FLocked: Boolean; //锁定
        FIsMapOpen: Boolean; //文件是否打开
        FExistsAlready: Boolean; //表示是否已经建立文件映射了
        FReading: Boolean; //正在读取内存映射文件数据
        FAutoSynch: Boolean; //是否自动同步
        FOnChange: TNotifyEvent; //当内存数据区内容改变时
        FFormHandle: Hwnd; //存储本窗口的窗口句柄
        FPNewWndHandler: Pointer; //
        FPOldWndHandler: Pointer; //
        procedure SetMapName(Value: string);
        procedure SetMapStrings(Value: TStringList);
        procedure SetSize(Value: DWord);
        procedure SetAutoSynch(Value: Boolean);
        procedure EnterCriticalSection;
        procedure LeaveCriticalSection;
        procedure MapStringsChange(Sender: TObject);
        procedure NewWndProc(var FMessage: TMessage);
      public
        constructor Create(AOwner: TComponent); override;
        destructor Destroy; override;
        procedure OpenMap;
        procedure CloseMap;
        procedure ReadMap;
        procedure WriteMap;
        property ExistsAlready: Boolean read FExistsAlready;
        property IsMapOpen: Boolean read FIsMapOpen;
      published
        property MaxSize: DWord read FSize write SetSize;
        property AutoSynchronize: Boolean read FAutoSynch write SetAutoSynch;
        property MapName: string read FMapName write SetMapName;
        property MapStrings: TStringList read FMapStrings write SetMapStrings;
        property OnChange: TNotifyEvent read FOnChange write FOnChange;
      end;




    implementation


    //构造函数
    constructor TFileMap.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      FAutoSynch := True;
      FSize := 4096;
      FReading := False;
      FMapStrings := TStringList.Create;
      FMapStrings.OnChange := MapStringsChange;
      FMapName := 'Unique & Common name';
      FSynchMessage := FMapName + 'Synch-Now';
      if AOwner is TForm then
      begin
        FFormHandle := (AOwner as TForm).Handle;
        //得到窗口处理过程的地址
        FPOldWndHandler := Ptr(GetWindowLong(FFormHandle, GWL_WNDPROC));
        FPNewWndHandler := MakeObjectInstance(NewWndProc);
        if FPNewWndHandler = nil then
          raise Exception.Create('超出资源');
        //设置窗口处理过程新的地址
        SetWindowLong(FFormHandle, GWL_WNDPROC, Longint(FPNewWndHandler));
      end
      else raise Exception.Create('组件的所有者应该是TForm');
    end;




    //析构函数
    destructor TFileMap.Destroy;
    begin
      CloseMap;
      //还原Windows处理过程地址
      SetWindowLong(FFormHandle, GWL_WNDPROC, Longint(FPOldWndHandler));
      if FPNewWndHandler <> nil then
        FreeObjectInstance(FPNewWndHandler);
      //释放对象
      FMapStrings.Free;
      FMapStrings := nil;
      inherited destroy;
    end;


    //打开文件映射,并映射到进程空间
    procedure TFileMap.OpenMap;
    var
      TempMessage: array[0..255] of Char;
    begin
      if (FMapHandle = 0) and (FMapPointer = nil) then
      begin
        FExistsAlready := False;
          // 创建文件映射对象
        FMapHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, FSize, PChar(FMapName));
        if (FMapHandle = INVALID_HANDLE_VALUE) or (FMapHandle = 0) then
          raise Exception.Create('创建文件映射对象失败!')
        else
        begin
       //判断是否已经建立文件映射了
          if (FMapHandle <> 0) and (GetLastError = ERROR_ALREADY_EXISTS) then
            FExistsAlready := True; //如果已建立的话,就设它为True
        //映射文件的视图到进程的地址空间
          FMapPointer := MapViewOfFile(FMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
          if FMapPointer = nil then
            raise Exception.Create('映射文件的视图到进程的地址空间失败')
          else
          begin
            StrPCopy(TempMessage, FSynchMessage);
          //在Windows中注册消息常量
            FMessageID := RegisterWindowMessage(TempMessage);
            if FMessageID = 0 then
              raise Exception.Create('注册消息失败')
          end
        end;
          //创建互斥对象,在写文件映射空间时,用到它,以保持数据同步
        FMutexHandle := Windows.CreateMutex(nil, False, PChar(FMapName + '.Mtx'));
        if FMutexHandle = 0 then
          raise Exception.Create('创建互斥对象失败');
        FIsMapOpen := True;
        if FExistsAlready then //判断内存文件映射是否已打开
          ReadMap
        else
          WriteMap;
      end;
    end;


    //解除文件视图和内存映射空间的关系,并关闭文件映射
    procedure TFileMap.CloseMap;
    begin
      if FIsMapOpen then
      begin
        //释放互斥对象
        if FMutexHandle <> 0 then
        begin
          CloseHandle(FMutexHandle);
          FMutexHandle := 0;
        end;
        //关闭内存对象
        if FMapPointer <> nil then
        begin
       //解除文件视图和内存映射空间的关系
          UnMapViewOfFile(FMapPointer);
          FMapPointer := nil;
        end;
        if FMapHandle <> 0 then
        begin
        //并关闭文件映射
          CloseHandle(FMapHandle);
          FMapHandle := 0;
        end;
        FIsMapOpen := False;
      end;
    end;


    //读取内存文件映射内容
    procedure TFileMap.ReadMap;
    begin
      FReading := True;
      if (FMapPointer <> nil) then FMapStrings.SetText(FMapPointer);
      FReading := False;
    end;


    //向内存映射文件里写
    procedure TFileMap.WriteMap;
    var
      StringsPointer: PChar;
      HandleCounter: integer;
      SendToHandle: HWnd;
    begin
      if FMapPointer <> nil then
      begin
        StringsPointer := FMapStrings.GetText;
        //进入互斥状态,防止其他线程进入同步区域代码
        EnterCriticalSection;
        if StrLen(StringsPointer) + 1 <= FSize
          then System.Move(StringsPointer^, FMapPointer^, StrLen(StringsPointer) + 1)
        else
          raise Exception.Create('写字符串失败,字符串太大!');
        //离开互斥状态
        LeaveCriticalSection;
        //广播消息,表示内存映射文件内容已修改
        SendMessage(HWND_BROADCAST, FMessageID, FFormHandle, 0);
        //释放StringsPointer
        StrDispose(StringsPointer);
      end;
    end;


    //当MapStrins值改变时
    procedure TFileMap.MapStringsChange(Sender: TObject);
    begin
      if FReading and Assigned(FOnChange) then
        FOnChange(Self)
      else if (not FReading) and FIsMapOpen and FAutoSynch then
        WriteMap;
    end;


    //设置MapName属性值
    procedure TFileMap.SetMapName(Value: string);
    begin
      if (FMapName <> Value) and (FMapHandle = 0) and (Length(Value) < 246) then
      begin
        FMapName := Value;
        FSynchMessage := FMapName + 'Synch-Now';
      end;
    end;


    //设置MapStrings属性值
    procedure TFileMap.SetMapStrings(Value: TStringList);
    begin
      if Value.Text <> FMapStrings.Text then
      begin
        if Length(Value.Text) <= FSize then
          FMapStrings.Assign(Value)
        else
          raise Exception.Create('写入值太大');
      end;
    end;


    //设置内存文件大小
    procedure TFileMap.SetSize(Value: DWord);
    var
      StringsPointer: PChar;
    begin
      if (FSize <> Value) and (FMapHandle = 0) then
      begin
        StringsPointer := FMapStrings.GetText;
        if (Value < StrLen(StringsPointer) + 1) then
          FSize := StrLen(StringsPointer) + 1
        else FSize := Value;
        if FSize < 32 then FSize := 32;
        StrDispose(StringsPointer);
      end;
    end;


    //设置是否同步
    procedure TFileMap.SetAutoSynch(Value: Boolean);
    begin
      if FAutoSynch <> Value then
      begin
        FAutoSynch := Value;
        if FAutoSynch and FIsMapOpen then WriteMap;
      end;
    end;


    //进入互斥,使得被同步的代码不能被别的线程访问
    procedure TFileMap.EnterCriticalSection;
    begin
      if (FMutexHandle <> 0) and not FLocked then
      begin
        FLocked := (WaitForSingleObject(FMutexHandle, INFINITE) = WAIT_OBJECT_0);
      end;
    end;


    //解除互斥关系,可以进入保护的同步代码区
    procedure TFileMap.LeaveCriticalSection;
    begin
      if (FMutexHandle <> 0) and FLocked then
      begin
        ReleaseMutex(FMutexHandle);
        FLocked := False;
      end;
    end;


    //消息捕获过程
    procedure TFileMap.NewWndProc(var FMessage: TMessage);
    begin
      with FMessage do
      begin
        if FIsMapOpen then //内存文件打开
       {如果消息是FMessageID,且WParam不是FFormHandle,就调用ReadMap,
        去读取内存映射文件的内容,表示内存映射文件的内容已变}
          if (Msg = FMessageID) and (WParam <> FFormHandle) then
            ReadMap;
        Result := CallWindowProc(FPOldWndHandler, FFormHandle, Msg, wParam, lParam);
      end;
    end;


    end.

    unit MainFrm;


    interface


    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, ExtCtrls, FileMap;


    type
      TfrmMain = class(TForm)
        btnWriteMap: TButton;
        btnReadMap: TButton;
        btnClear: TButton;
        chkExistsAlready: TCheckBox;
        chkIsMapOpen: TCheckBox;
        btnOpenMap: TButton;
        btnCloseMap: TButton;
        mmoCont: TMemo;
        chkAutoSynchronize: TCheckBox;
        Label5: TLabel;
        lblHelp: TLabel;
        procedure btnWriteMapClick(Sender: TObject);
        procedure btnReadMapClick(Sender: TObject);
        procedure btnClearClick(Sender: TObject);
        procedure btnOpenMapClick(Sender: TObject);
        procedure btnCloseMapClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure chkAutoSynchronizeClick(Sender: TObject);
        procedure mmoContKeyDown(Sender: TObject; var Key: Word;
          Shift: TShiftState);
      private
        //定义TFileMap的对象
        FileMap: TFileMap;
        //定义FileMapChange用于赋给FileMap的OnChange事件
        procedure FileMapChange(Sender: TObject);
        procedure Check;
       { Private declarations }
      public
      { Public declarations }
      end;


    var
      frmMain: TfrmMain;
    implementation


    {$R *.DFM}


    //检查FileMap的ExistsAlready和IsMapOpen属性
    procedure TfrmMain.Check;
    begin
      chkExistsAlready.Checked := FileMap.ExistsAlready;
      chkIsMapOpen.Checked := FileMap.IsMapOpen;
    end;


    //在窗体创建时,初始化FileMap对象
    procedure TfrmMain.FormCreate(Sender: TObject);
    begin
      //创建对象FileMap
      FileMap := TFileMap.Create(self);
      FileMap.OnChange := FileMapchange;
      chkAutoSynchronize.Checked := FileMap.AutoSynchronize;
      //如果内存对象还未创建,初始化FileMap里的内容
      if not FileMap.ExistsAlready then
      begin
        MmoCont.Lines.LoadFromFile('Project1.dpr');
        FileMap.MapStrings.Assign(MmoCont.Lines);
      end;
      lblHelp.Caption := '使用说明:运行两个或多个此应用程序,按下“打开内存映射”按钮,'
        + #13 + '选中“是否同步”复选框,在备注框里改动,在另外的应用程序中将会'
        + #13 + '该动后的信息,同时也可以读写数据按钮来获取共享信息'
    end;


    //写入内存文件映射的数据
    procedure TfrmMain.btnWriteMapClick(Sender: TObject);
    begin
      FileMap.WriteMap;
    end;


    //读取内存文件映射的数据
    procedure TfrmMain.btnReadMapClick(Sender: TObject);
    begin
      FileMap.ReadMap;
    end;


    //清除内存文件数据
    procedure TfrmMain.btnClearClick(Sender: TObject);
    begin
      Mmocont.Clear;
      FileMap.MapStrings.Clear;
      check;
    end;


    //打开内存文件映射
    procedure TfrmMain.btnOpenMapClick(Sender: TObject);
    begin
      FileMap.MapName := 'Delphi 6 ';
      FileMap.OpenMap;
      check;
    end;


    //关闭内存映射
    procedure TfrmMain.btnCloseMapClick(Sender: TObject);
    begin
      FileMap.CloseMap;
      Check;
    end;


    //当内存映射文件的数据改变时,显示最新数据
    procedure TfrmMain.FileMapChange(Sender: TObject);
    begin
      Mmocont.Lines.Assign(FileMap.MapStrings);
      Check;
    end;


    //设置是否同步显示
    procedure TfrmMain.chkAutoSynchronizeClick(Sender: TObject);
    begin
      FileMap.AutoSynchronize := chkAutoSynchronize.Checked;
    end;


    //在备注框里写时,同时更新进内存映射文件
    procedure TfrmMain.mmoContKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    begin
      FileMap.MapStrings.Assign(MmoCont.Lines);
    end;


    end.

    http://blog.csdn.net/zang141588761/article/details/52062603

    VC版:
    http://blog.csdn.NET/zicheng_lin/article/details/8151448

  • 相关阅读:
    vue 中的键盘事件
    红米k40刷类原生系统
    (历史) 1960s,大家争先在共享内存上实现原子性 (互斥) 但几乎所有的实现都是错的,直到 Dekker's Algorithm,还只能保证两个线程的互斥
    Go Memory Model 内存模型 同步 goroutine
    理解并发程序执行 (Peterson算法、模型检验与软件自动化工具
    源码 连接池 设计
    Thread Exception Captured Application Crash Report
    Check if the context is expired.
    A Quick Guide to Go's Assembler
    敏感问题调查 干扰变量 抛硬币
  • 原文地址:https://www.cnblogs.com/findumars/p/6711244.html
Copyright © 2020-2023  润新知