我想在两个应用程序之间共享数组数据。我的想法是,第一个程序创建数组,第二个程序可以从已分配的内存区域读取该数组。该数组不是动态数组。
我发现可以使用 OpenFileMapping
和 MapViewOfFile
共享指针。但我没有成功实现数组共享,并且我认为我暂时不想使用IPC方法。
是否有可能计划像这样的方案(共享数组)?我的目的是尽量减少内存使用并快速读取数据。
我想在两个应用程序之间共享数组数据。我的想法是,第一个程序创建数组,第二个程序可以从已分配的内存区域读取该数组。该数组不是动态数组。
我发现可以使用 OpenFileMapping
和 MapViewOfFile
共享指针。但我没有成功实现数组共享,并且我认为我暂时不想使用IPC方法。
是否有可能计划像这样的方案(共享数组)?我的目的是尽量减少内存使用并快速读取数据。
思考了一下如何展示两个应用程序之间共享内存的一个简短但完整的示例。唯一的选择是控制台应用程序,GUI 应用需要至少 3 个文件 (DPR + PAS + DFM)。因此我构建了一个小示例,其中使用内存映射文件共享了一个整数数组(由页面文件支持,因此我不需要在磁盘上拥有一个物理文件才能让它工作)。控制台应用程序响应 3 个命令:
当然,命令处理代码占整个应用程序的约 80%。要测试这个示例,请编译以下控制台应用程序,找到可执行文件并运行它两次。在第一个窗口中输入:
SET 1 100
SET 2 50
前往第二个控制台,输入以下内容:
DUMP 1
DUMP 2
DUMP 3
SET 1 150
打开第一个控制台,输入以下内容:
DUMP 1
你看到了,你刚刚见证了两个应用程序之间共享内存。
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.
array [0..1024] of Integer
。如果你编译时使用了{$R+}
,那么就没有SharedArray[1025]
,并且会出现范围检查错误。你可以根据需要定义TSharedArray
。 - Cosmin Prund主程序:
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;
读者:
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;
CreateFileMapping
返回的句柄仅分配给 TForm1.Button1Click
中的局部变量,因此无法关闭它。这是不必要的:ZeroMemory(SharedData, BUF_SIZE);
- 由页面文件支持的文件映射(值得庆幸的是)会被初始化为零。如果该内存未被初始化为零,则可能构成安全威胁! - Cosmin Prund