Newsgroup: it.comp.lang.delphi
Titolo: File Mappati in Memoria...

Salve, ho un grosso problema con i MMF.
Dunque, vorrei poter utilizzare un MMF file condiviso tra un eseguibile ed
alcune DLL.
Dall'help di Delphi, ho visto che utilizzando CreateFileMapping() e passando
$FFFFFFFF
ovvero (DWORD(-1)) come parametro per l'handle del file da mappare si mappa
in realtà
uno spazio di indirizzamento del file di paginazione del sistema.
Bene mi attendevo di poter utilizzare il "file" (o meglio la pagina di
memoria) condiviso tra
più applicazioni... in realtà pare che ogni applicazione utilizzi una sua
pagina non condivisa con
le altre applicazioni, sebbene il nome del "file " sia sempre lo stesso.
Riporto di seguito il codice che ho scritto:
<FILE MMF_UTIL>
unit MMF_Util;
interface
uses
  SysUtils, Windows, SyncObjs;
type
  TByteArray = array of Byte;
  TMemoryMappedFile = class(TObject)
  private
    FFileHandle: DWORD;
    FMappedFileHandle: THandle;
    FMappedFileName: PChar;
    FName: string;
    FMapView: Pointer;
    FData: TByteArray;
    FSize: Longint;
    procedure SetData(const AData: TByteArray);
    function GetData: TByteArray;
  public
    constructor Create(const AName: string);
    destructor Destroy; override;
    property Data: TByteArray read GetData write SetData;
    property Name: string read FName;
    property Size: Longint read FSize;
  end;
implementation
{ TMemoryMappedFile }
constructor TMemoryMappedFile.Create(const AName: string);
begin
  inherited Create;
  FName := AName;
  FMappedFileName := PChar(AName+#0);
  FFileHandle := DWORD(-1); // Area di memoria di Windows!!!
  FMappedFileHandle := CreateFileMapping(FFileHandle, nil, PAGE_READWRITE,
                                         0, 1048576, FMappedFileName);
  //FMappedFileHandle := OpenFileMapping(0, False, FMappedFileName);
  FMapView := MapViewOfFile(FMappedFileHandle, FILE_MAP_WRITE, 0, 0, 0);
  FSize := FileSeek(FMappedFileHandle, 0, 2);
  FileSeek(FMappedFileHandle, 0, 0);
end;
destructor TMemoryMappedFile.Destroy;
begin
  FlushViewOfFile(FMapView, 0);
  UnmapViewOfFile(FMapView);
  CloseHandle(FMappedFileHandle);
  CloseHandle(FFileHandle);
  inherited Destroy;
end;
function TMemoryMappedFile.GetData: TByteArray;
begin
  SetLength(FData, FSize);
  FileRead(FMappedFileHandle, FData, FSize);
  FileSeek(FMappedFileHandle, 0, 0);
  Result := FData;
end;
procedure TMemoryMappedFile.SetData(const AData: TByteArray);
begin
  FSize := Length(AData);
  FData := Copy(AData, 0, FSize);
  FileWrite(FMappedFileHandle, FData, FSize);
  FileSeek(FMappedFileHandle, 0, 0);
end;
end.
---
<FILE MAIN.PAS>
unit Main;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  MMF_Util, StdCtrls;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    MMF: TMemoryMappedFile;
  public
    { Public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
  MMF := TMemoryMappedFile.Create('prova.dat');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  MMF.Destroy;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  Tmp: TByteArray;
  Size: Longint;
  i: Longint;
begin
  Size := Length(Edit1.Text);
  SetLength(Tmp, Size);
  for i:=0 to Pred(Size) do Tmp[i] := Ord(Edit1.Text[Succ(i)]);
  MMF.Data := Tmp;
  SetLength(Tmp, 0);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  Tmp: TByteArray;
  TmpStr: string;
  Size: Longint;
  i: Longint;
begin
  Tmp := MMF.Data;
  Size := MMF.Size;
  SetLength(TmpStr, Size);
  for i:=0 to Pred(Size) do TmpStr[Succ(i)] := Chr(Tmp[i]);
  Edit2.Text := TmpStr;
  SetLength(Tmp, 0);
end;
end.
--- Scusate la lunghezza...
Bene, se ho commesso qualche grossolano errore, o c'è qualcosa che ignoro vi
prego di
aiutarmi.
Grazie mille,
  Roberto Milani

unit ShareMem;
interface
uses
  SysUtils, Windows;
const
  MFILE_TEMP = $FFFFFFFF;
type
  ESharedMemoryError = class(Exception);
  TSharedMemory = class
  private
    FMappingHandle: THandle;
    FMemory: Pointer;
    FSize: Longint;
  public
    constructor Create(ObjectName : PChar; DesiredSize: Longint);
    destructor Destroy; override;
    property Memory: Pointer read FMemory;
    property Size: Longint read FSize;
  end;
implementation
// ----------------------------------
// Create (TSharedMemory)
// ----------------------------------
constructor TSharedMemory.Create(ObjectName: PChar; DesiredSize: Longint);
begin
  FSize:= DesiredSize;
  FMappingHandle:= CreateFileMapping(MFILE_TEMP, nil, Page_ReadWrite, 0,
DesiredSize, ObjectName);
  if (FMappingHandle=0) then
    raise ESharedMemoryError.Create('TSharedMemory CreateFileMapping
error');
  FMemory:= MapViewOfFile(FMappingHandle, FILE_MAP_WRITE, 0, 0, 0);
  if (FMemory=nil) then
    raise ESharedMemoryError.Create('TFileMapping Map View error');
end;
// ----------------------------------
// Destroy (TSharedMemory)
// ----------------------------------
destructor TSharedMemory.Destroy;
begin
  if (FMemory<>nil) then
  begin
    if not UnmapViewOfFile(FMemory) then
      raise ESharedMemoryError.Create( 'TFileMapping UnmapViewOfFile
error');
    FMemory:= nil;
  end;
  if (FMappingHandle<>0) then
    if not CloseHandle(FMappingHandle) then
      raise ESharedMemoryError.Create('TFileMapping Close Handle error' );
  inherited Destroy;
end;
end.
Ciao,
-=SIMO=-

Grazie mille,
provvedo subito...
A buon rendere,
  Roberto

Ciao,
ho fatto qualche modifica al codice, l'ho testato e mi sembra di aver
ottenuto un bel risultato:
unit SharedMemory;
interface
uses
  SysUtils, Windows;
const
  FILE_HANDLE = $FFFFFFFF;
type
  TByteArray = array of Byte;
  ESharedMemoryError = class(Exception);
  TSharedMemory = class(TObject)
  private
    FMappingHandle: THandle;
    FMemory: Pointer;
    FSize: Longint;
  public
    constructor Create(AName: PChar; ADesiredSize: Longint);
    destructor Destroy; override;
    procedure Write(const AData: TByteArray); overload;
    procedure Write(const AData: string); overload;
    procedure Read(var AData: TByteArray); overload;
    procedure Read(var AData: string); overload;
  end;
implementation
{ TSharedMemory }
constructor TSharedMemory.Create(AName: PChar; ADesiredSize: Integer);
begin
  inherited Create;
  FSize := ADesiredSize;
  FMappingHandle := CreateFileMapping(FILE_HANDLE, nil, PAGE_READWRITE, 0,
                                      FSize, AName);
  if (FMappingHandle=0) then
    raise ESharedMemoryError.Create('TSharedMemory CreateFileMapping
error');
  FMemory := MapViewOfFile(FMappingHandle, FILE_MAP_WRITE, 0, 0, 0);
  if (FMemory=nil) then
    raise ESharedMemoryError.Create('TFileMapping Map View Error');
end;
destructor TSharedMemory.Destroy;
begin
  if (FMemory<>nil) then
  begin
    if not UnmapViewOfFile(FMemory) then
      raise ESharedMemoryError.Create('TFileMapping UnmapViewOfFile error');
    Fmemory := nil;
  end;
  if (FMappingHandle<>0) then
    if not CloseHandle(FMappingHandle) then
      raise ESharedMemoryError.Create('TFileMapping CloseHandle error');
  inherited Destroy;
end;
procedure TSharedMemory.Read(var AData: TByteArray);
var
  TmpLong: ^Longint;
  TmpByte: ^Byte;
  Size: Longint;
  i: Longint;
begin
  TmpLong := FMemory;
  Size := TmpLong^;
  Inc(TmpLong);
  TmpByte := Pointer(TmpLong);
  SetLength(AData, Size);
  for i:=0 to Pred(Size) do
  begin
    AData[i] := TmpByte^;
    Inc(TmpByte);
  end;
end;
procedure TSharedMemory.Write(const AData: TByteArray);
var
  TmpLong: ^Longint;
  TmpByte: ^Byte;
  Size: Longint;
  i: Longint;
begin
  TmpLong := FMemory;
  Size := Length(AData);
  TmpLong^ := Size;
  Inc(TmpLong);
  TmpByte := Pointer(TmpLong);
  for i:=0 to Pred(Size) do
  begin
    TmpByte^ := AData[i];
    Inc(TmpByte);
  end;
end;
procedure TSharedMemory.Read(var AData: string);
var
  Tmp: TByteArray;
  Size: Longint;
  i: Longint;
begin
  Read(Tmp);
  Size := Length(Tmp);
  SetLength(AData, Size);
  for i:=0 to Pred(Size) do AData[Succ(i)] := Chr(Tmp[i]);
  SetLength(Tmp, 0);
end;
procedure TSharedMemory.Write(const AData: string);
var
  Tmp: TByteArray;
  Size: Longint;
  i: Longint;
begin
  Size := Length(AData);
  SetLength(Tmp, Size);
  for i:=0 to Pred(Size) do Tmp[i] := Ord(AData[Succ(i)]);
  Write(Tmp);
  SetLength(Tmp, 0);
end;
end.
---
In pratica uso i primi 4 Byte per registrare la dimensione nell'area di
memoria condivisa, in tal modo ogni applicazione legge lo spazio
effettivamente riempito con qualcosa...
Un ultima domanda: se volessi non sprecare spazio di memoria e
cambiare a run-time la dimensione del "file mappato", è possibile
farlo? (Ho questa esigenza in quanto il contenuto di ciò che
vado a scrivare può andare da poche decine di byte a qualche
centinaio di KByte e visto che me ne servono una ventina di
aree condivise vorrei non dovere assegnare tutte staticamente
alla dimensione massima di un 1 Mbyte!!!).
Grazie ancora,
  Roby