⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filemap.pas

📁 《delphi深度编程及其项目开发》
💻 PAS
字号:
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.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -