Sharing data array between two applications in Delphi
I want to share array data between two applications. In my mind, first program create the array and the second program can read the array from already allocated memory area. The array is not a dynamic array.
I found a way to share pointer using OpenFileMapping
and MapViewOfFile
. I have no luck to implement array sharing and I think i don't want to use IPC method yet.
Is it possible to plan a scheme like this (sharing array)? My purpose is to minimize memory 开发者_运维技巧usage and reading data quickly.
Scratched my head thinking of what a short-but-complete example of sharing memory between two applications might be. The only option is a console application, GUI applications require a minimum of 3 files (DPR + PAS + DFM). So I cooked up a small example where one integers array is shared using a memory mapped file (backed by the page file so I don't need to have a phisical file on disk for this to work). The console application responds to 3 commands:
- EXIT
- SET NUM VALUE Changes the value at index NUM in the array to VALUE
- DUMP NUM displays the value in the array at index NUM
- DUMP ALL displays the whole array
Of course, the command processing code takes up about 80% of the whole application. To test this compile the following console application, find the executable and start it twice. Go to the first window and enter:
SET 1 100
SET 2 50
Go to the second console and enter this:
DUMP 1
DUMP 2
DUMP 3
SET 1 150
Go to the first console and enter this:
DUMP 1
There you have it, you've just witnessed sharing memory between two applications.
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows, Classes;
type
TSharedArray = array[0..10] of Integer;
PSharedArray = ^TSharedArray;
var
hFileMapping: THandle; // Mapping handle obtained using CreateFileMapping
SharedArray: PSharedArray; // Pointer to the shared array
cmd, s: string;
num, value, i: Integer;
L_CMD: TStringList;
function ReadNextCommand: string;
begin
WriteLn('Please enter command (one of EXIT, SET NUM VALUE, DUMP NUM, DUMP ALL)');
WriteLn;
ReadLn(Result);
end;
begin
try
hFileMapping := CreateFileMapping(0, nil, PAGE_READWRITE, 0, SizeOf(TSharedArray), '{C616DDE6-23E2-425C-B871-9E0DA54D96DF}');
if hFileMapping = 0 then
RaiseLastOSError
else
try
SharedArray := MapViewOfFile(hFileMapping, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TSharedArray));
if SharedArray = nil then
RaiseLastOSError
else
try
WriteLn('Connected to the shared view of the file.');
cmd := ReadNextCommand;
while UpperCase(cmd) <> 'EXIT' do
begin
L_CMD := TStringList.Create;
try
L_CMD.DelimitedText := cmd;
for i:=0 to L_CMD.Count-1 do
L_CMD[i] := UpperCase(L_CMD[i]);
if (L_CMD.Count = 2) and (L_CMD[0] = 'DUMP') and TryStrToInt(L_CMD[1], num) then
WriteLn('SharedArray[', num, ']=', SharedArray^[num])
else if (L_CMD.Count = 2) and (L_CMD[0] = 'DUMP') and (L_CMD[1] = 'ALL') then
begin
for i:= Low(SharedArray^) to High(SharedArray^) do
WriteLn('SharedArray[', i, ']=', SharedArray^[i]);
end
else if (L_CMD.Count = 3) and (L_CMD[0] = 'SET') and TryStrToInt(L_CMD[1], num) and TryStrToInt(L_CMD[2], value) then
begin
SharedArray^[num] := Value;
WriteLn('SharedArray[', num, ']=', SharedArray^[num]);
end
else
WriteLn('Error processing command: ' + cmd);
finally L_CMD.Free;
end;
// Requst next command
cmd := ReadNextCommand;
end;
finally UnmapViewOfFile(SharedArray);
end;
finally CloseHandle(hFileMapping);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
A Named File Mapping would be the easiest solution, here is some short example code. In this sample there is a main program that writes some data and reader(s) that only read from it.
Main:
type
TSharedData = record
Handle: THandle;
end;
PSharedData = ^TSharedData;
const
BUF_SIZE = 256;
var
SharedData: PSharedData;
hFileMapping: THandle; // Don't forget to close when you're done
function CreateNamedFileMapping(const Name: String): THandle;
begin
Result := CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0,
BUF_SIZE, PChar(Name));
Win32Check(Result > 0);
SharedData := MapViewOfFile(Result, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
Win32Check(Assigned(SharedData));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
hFileMapping := CreateNamedFileMapping('MySharedMemory');
Win32Check(hFileMapping > 0);
SharedData^.Handle := CreateHiddenWindow;
end;
reader:
var
hMapFile: THandle; // Don't forget to close
function GetSharedData: PSharedData;
begin
hMapFile := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MySharedMemory');
Win32Check(hMapFile > 0);
Result := MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, BUF_SIZE);
Win32Check(Assigned(Result));
end;
精彩评论