Совместное использование массива данных между двумя приложениями в Delphi - PullRequest
9 голосов
/ 13 апреля 2011

Я хочу поделиться данными массива между двумя приложениями. На мой взгляд, первая программа создает массив, а вторая может считывать массив из уже выделенной области памяти. Массив не является динамическим массивом.

Я нашел способ поделиться указателем, используя OpenFileMapping и MapViewOfFile. Мне не повезло реализовать совместное использование массива, и я думаю, что пока не хочу использовать метод IPC.

Можно ли спланировать такую ​​схему (общий массив)? Моя цель - минимизировать использование памяти и быстрое чтение данных.

Ответы [ 2 ]

16 голосов
/ 13 апреля 2011

Почесал голову, размышляя о том, каким может быть короткий, но полный пример совместного использования памяти между двумя приложениями.Единственным вариантом является консольное приложение, приложения с графическим интерфейсом требуют минимум 3 файла (DPR + PAS + DFM).Поэтому я подготовил небольшой пример, в котором один массив целых чисел используется совместно с отображенным в память файлом (на основе файла подкачки, поэтому мне не нужно иметь физический файл на диске, чтобы это работало).Консольное приложение отвечает на 3 команды:

  • EXIT
  • SET NUM VALUE Изменяет значение в индексе NUM в массиве VALUE
  • DUMP NUM отображает значение в массиве с индексом NUM
  • DUMP ALL отображает весь массив

Конечно, код обработки команд занимает около 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.
5 голосов
/ 13 апреля 2011

A Отображение именованных файлов будет самым простым решением, вот небольшой пример кода. В этом примере есть основная программа, которая записывает некоторые данные и считыватели, которые только читают из нее.

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;

Читатель:

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;
...