Sharing data array between two applications in Delphi
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 100SET 2 50
Go to the second console and enter this:
DUMP 1DUMP 2DUMP 3SET 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 donefunction 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 closefunction 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;