首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Delphi 進程間資料共享:記憶體映射檔實作完整指南

     編輯:更多程式設計知識

DLL 載入時會映射至各進程獨立位址空間,全域變數**不共享**。若需多進程即時共享資料,Windows 提供記憶體映射檔機制:在系統分頁檔保留命名共享記憶體區域,多進程透過同一名稱映射至各自位址空間,實現高效、無需實體檔案的跨進程資料交換。本文提供完整 DLL 實作與呼叫範例。

核心 API 流程

  1. CreateFileMapping(INVALID_HANDLE_VALUE, ...):建立/開啟分頁檔支援的映射物件,指定名稱實現跨進程共享。
  2. MapViewOfFile(...):將映射物件視圖映射至呼叫進程位址空間,取得指標存取資料。
  3. 進程間透過指標直接讀寫共享結構體,變更即時可見。
  4. UnmapViewOfFile/CloseHandle:釋放資源。

共享資料 DLL 完整原始碼


library ShareDataDLL;

uses
  SysUtils, Windows, Classes;

const
  MapName = 'Global\MyApp_SharedData'; // Global\ 前綴確保跨 Session 可見

type
  PSharedData = ^TSharedData;
  TSharedData = record
    Status: array[0..49] of Char; // 固定長度字串,避免指標跨進程失效
    Counter: Integer;
    Timestamp: TDateTime;
  end;

var
  MapHandle: THandle = 0;
  SharedMem: PSharedData = nil;

{ 供外部進程呼叫:取得共享記憶體指標 }
procedure GetSharedData(var AData: PSharedData); stdcall;
begin
  AData := SharedMem;
end;

{ 初始化映射 }
procedure InitMapping;
var
  Size: Integer;
begin
  Size := SizeOf(TSharedData);
  // 建立映射物件:分頁檔、讀寫、大小、名稱
  MapHandle := CreateFileMapping(
    THandle(-1),      // INVALID_HANDLE_VALUE,使用分頁檔
    nil,              // 預設安全性
    PAGE_READWRITE,   // 保護屬性
    0, Size,          // 高/低 32 位大小
    PChar(MapName)    // 共享名稱
  );
  if MapHandle = 0 then RaiseLastOSError;

  // 映射視圖至進程位址空間
  SharedMem := MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, Size);
  if SharedMem = nil then begin
    CloseHandle(MapHandle);
    RaiseLastOSError;
  end;

  // 初始化預設值
  StrPCopy(SharedMem.Status, 'INITIALIZED');
  SharedMem.Counter := 0;
  SharedMem.Timestamp := Now;
end;

{ 清理映射 }
procedure CleanupMapping;
begin
  if SharedMem <> nil then UnmapViewOfFile(SharedMem);
  if MapHandle <> 0 then CloseHandle(MapHandle);
  SharedMem := nil;
  MapHandle := 0;
end;

{ DLL 進程附加/分離入口 }
procedure DllEntryPoint(dwReason: DWORD);
begin
  case dwReason of
    DLL_PROCESS_ATTACH: InitMapping;
    DLL_PROCESS_DETACH: CleanupMapping;
  end;
end;

exports
  GetSharedData index 1;

begin
  DllProc := @DllEntryPoint;
  DllEntryPoint(DLL_PROCESS_ATTACH); // 立即初始化
end.

呼叫端應用程式範例


unit MainUnit;

interface
uses Windows, SysUtils, Classes, Forms, Controls, StdCtrls;

type
  TSharedData = record
    Status: array[0..49] of Char;
    Counter: Integer;
    Timestamp: TDateTime;
  end;
  PSharedData = ^TSharedData;

  TForm1 = class(TForm)
    btnRead: TButton;
    btnWrite: TButton;
    edtStatus: TEdit;
    edtCounter: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure btnReadClick(Sender: TObject);
    procedure btnWriteClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    SharedData: PSharedData;
    procedure GetSharedData(var AData: PSharedData); stdcall; external 'ShareDataDLL.dll' index 1;
  end;

implementation
{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  GetSharedData(SharedData); // 取得共享記憶體指標
end;

procedure TForm1.btnReadClick(Sender: TObject);
begin
  if SharedData <> nil then begin
    edtStatus.Text := StrPas(SharedData.Status);
    edtCounter.Text := IntToStr(SharedData.Counter);
  end;
end;

procedure TForm1.btnWriteClick(Sender: TObject);
begin
  if SharedData <> nil then begin
    StrPCopy(SharedData.Status, edtStatus.Text);
    SharedData.Counter := SharedData.Counter + 1;
    SharedData.Timestamp := Now;
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // 無需釋放共享記憶體,DLL 會在進程分離時清理
end;
end.

關鍵技術細節

項目說明
名稱前綴 Global\確保跨 Terminal Services Session 共享;本機單 Session 可省略。
固定長度陣列共享結構體中**禁止使用動態字串、動態陣列、介面、物件參考**,因其內部指標指向進程私有堆疊,跨進程無效。改用 array[0..N] of Char、Integer、Double 等實值型別。
同步化多進程並發讀寫需外部同步:
• Mutex (跨進程互斥鎖)
• Event (通知機制)
• Critical Section 僅限進程內,不可跨進程。
安全性CreateFileMapping 可傳遞 SECURITY_ATTRIBUTES 限制存取權限 (ACL)。
大小限制單一映射物件大小受限於虛擬記憶體空間 (32-bit 約 2GB),大數據建議分段映射或使用檔案映射。

進階:加入 Mutex 保護寫入


// DLL 端新增
var
  MutexHandle: THandle;

procedure InitMapping;
begin
  // ... 原有代碼 ...
  MutexHandle := CreateMutex(nil, False, PChar(MapName + '_Mutex'));
end;

procedure LockShared; stdcall;
begin
  WaitForSingleObject(MutexHandle, INFINITE);
end;

procedure UnlockShared; stdcall;
begin
  ReleaseMutex(MutexHandle);
end;

exports
  GetSharedData index 1,
  LockShared index 2,
  UnlockShared index 3;

呼叫端寫入前呼叫 LockShared,寫入後 UnlockShared,確保資料一致性。

結語

記憶體映射檔是 Windows 進程間共享資料的最高效方式:零拷貝、系統管理分頁、命名跨進程可見。封裝於 DLL 中,配合固定結構體、Mutex 同步、進程附加/分離自動管理,即構成穩健、可重用的跨進程共享元件。掌握此技術,可解決多進程架構下的即時資料同步、狀態共享、協同運算等核心需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved