進程間的數據共享
阿新 • • 發佈:2018-06-07
ansi pch 內存 utils std nmap; delphi proc 其中
1. 概述
在win32中,通過使用映像文件在進程間實現共享文件或共享內存數據塊,如果利用相同的映像名字或文件句柄,則不同的進程可以通過一個指針來讀寫一個文件或同一個內存數據塊,並把它當做該進程內地址空間的一部分
在Windows9x/NT/200 向內存中裝載文件時,使用了特殊的全局內存區。在該區域內,應用程序的虛擬內存地址和文件中的響應位置對應,由於所有進程恭喜了一個用於存儲映像文件的全局內存區域,因而當兩個進程裝載相同模塊(應用程序exe或dll文件)時,他們實際上是在內存中共享其執行代碼
內存映像文件可以映射一個文件、一個文件中的指定區域或者指定的內存塊,其中的數據就可以用內存讀寫指令直接訪問,而不比頻繁的調用ReadFile或WriteFIle這樣的I/O系統函數,從而提高了文件存取速度和效率
2. 示例代碼
2.1. 兩個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.
進程間的數據共享