• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 453
  • Last Modified:

TList to Memory Mapping

Dear Experts,

I'd like to create an application where instances will share TList with each other. The TList will contain the following record:

type
    TMyRecord = record
        AString: string;
        ANumber: Integer;
end;

I can't allocate the fixed memory size, because I don't know how much records will be created. The read/write operations are synchronized with mutexes.

TIA,
dumb
0
iam_dumb
Asked:
iam_dumb
2 Solutions
 
TyrsisCommented:
Hello,

Just to clarify.  You want to be able to share a TList between multiple instances of the same application (ie:  Launching the same app twice shares the same TList)?  If this is the case, you can't do that, at least not without interprocess communication.  But I'm not certain if this is what you mean.

Tyrsis
0
 
Slick812Commented:
hello  iam_dumb, , ,
???
if you do memory mapping, them there are NO OPTIONS, you will need to get a FIXED size memory mapped file, and I would think you can do two things, , ONE -  a large size Mem Map file, to cover all of your memory requirements . . OR. .  TWO - you will have to do the memory manager methods, , , , allocate a Megabyte to start and if there is a mem requirement for more than a Megabyte, then copy the mem from the mem map , release the old mem map, and then reallocate a new mem map file ( 2 megabytes) and copy your saved mem into the new mem map. The default memory memory manager of Delphi does this sort of thing, with LocalAllocate, not mem map. . . .  you could store the size of the current mem map in the first 4 bytes of that pointer

also

your record type will not directly store into a memory segment, since the    "AString: string;"   member is just a 4 byte pointer to a string data
0
 
Lee_NoverCommented:
as others pointed out you can't have a dynmic size MMF and/or use dynamic size fields in a record
the dyn. fields sizes could be solved using a size field to store the size of the dyn. size field
anyway I use the following global list implementation (just change the record definition to whatever you like (note the current impl. doesn't handle dyn. sized fields in the record)
it can be a good starting point ;-)

unit GlobalList;

interface

uses
  windows,
  sysutils,
  syncobjs;

const
  MAX_GLITEMS = 64;

type
  TGLItem = record
    Event: Integer;
    Window: Cardinal;
    Message: Cardinal;
  end;

  PGLItems = ^TGLItems;
  TGLItems = array[0..0] of TGLItem;

  TGlobalList = class(TObject)
  private
    FCount: PInteger;
    FFirst: Boolean;
    FLock: TEvent;
    FMap: Cardinal;
    FName: string;
    FItems: PGLItems;
    sa: TSecurityAttributes;
    sd: TSecurityDescriptor;
  protected
    function GetActive: Boolean;
    procedure SetActive(Value: Boolean);
    function GetCount: Integer;
    function GetFirst: Boolean;
    procedure SetName(Value: string);
    function GetItem(Index: Integer): TGLItem;
    procedure SetItem(Index: Integer; Value: TGLItem);
  public
    constructor Create(const AName: string);
    destructor Destroy; override;

    function Add(AItem: TGLItem): Integer;
    procedure Delete(AIndex: Integer);

    procedure Open;
    procedure Close;

    property Active: Boolean read GetActive write SetActive;
    property Count: Integer read GetCount;
    property First: Boolean read GetFirst;
    property Items[Index: Integer]: TGLItem read GetItem write SetItem; default;
    property Name: string read FName write SetName;
  end;

implementation

{ TGlobalList }

constructor TGlobalList.Create(const AName: string);
begin
     inherited Create;
     FCount:=nil;
     FName:=AName;
     FItems:=nil;
end;

destructor TGlobalList.Destroy;
begin
     Close;
     inherited;
end;

procedure TGlobalList.Close;
begin
     if not Active then exit;
     UnMapViewOfFile(FItems);
     CloseHandle(FMap);
     FMap:=0;
     FItems:=nil;
     FLock.SetEvent;
     FreeAndNil(FLock);
end;

function TGlobalList.GetActive: Boolean;
begin
     Result:=(FMap > 0) and Assigned(FItems);
end;

function TGlobalList.GetItem(Index: Integer): TGLItem;
begin
     if not Active then
        raise Exception.Create('Items not accessible when not Active');

     if (Index > Count) or (Index < 0) then
        raise Exception.Create('List Index out of bounds')
     else
     begin
        FLock.WaitFor(INFINITE);
        FLock.ResetEvent;
        try
           Result:=FItems^[Index];
        finally
           FLock.SetEvent;
        end;
     end;
end;

procedure TGlobalList.Open;
begin
     if Active then exit;

     if not InitializeSecurityDescriptor(@sd, SECURITY_DESCRIPTOR_REVISION) then
        raise Exception.Create('Can''t intialize security descriptor');

     SetSecurityDescriptorDacl(@sd, true, nil, false);

     sa.nLength:=SizeOf(sa);
     sa.bInheritHandle:=true;
     sa.lpSecurityDescriptor:=@sd;

     FMap:=CreateFileMapping($FFFFFFFF, @sa, PAGE_READWRITE, 0, SizeOf(Integer) + (SizeOf(TGLItem) * MAX_GLITEMS), PChar('mmf' + Name));
     if FMap = 0 then
        raise Exception.Create('Couldn''t create FileMapping with name: ' + Name)
     else
        FFirst:=GetLastError <> ERROR_ALREADY_EXISTS;

     FCount:=MapViewOfFile(FMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
     FItems:=Pointer(Cardinal(FCount) + SizeOf(Integer));
     FLock:=TEvent.Create(@sa, true, true, 'evt' + Name);
end;

procedure TGlobalList.SetActive(Value: Boolean);
begin
     if Value = Active then
        exit
     else
        if Value then
           Open
        else
           Close;
end;

procedure TGlobalList.SetItem(Index: Integer; Value: TGLItem);
begin
     if not Active then
        raise Exception.Create('Items not accessible when not Active');

     if (Index > Count) or (Index < 0) then
        raise Exception.Create('List Index out of bounds')
     else
     begin
        FLock.WaitFor(INFINITE);
        FLock.ResetEvent;
        try
           FItems^[Index]:=Value;
        finally
           FLock.SetEvent;
        end;
     end;
end;

procedure TGlobalList.SetName(Value: string);
begin
     if Active then
        raise Exception.Create('Can''t change Name when Active')
     else
        FName:=Value;
end;

function TGlobalList.GetCount: Integer;
begin
     if not Active then
        Result:=0
     else
     begin
        FLock.WaitFor(INFINITE);
        FLock.ResetEvent;
        try
           Result:=FCount^;
        finally
           FLock.SetEvent;
        end;
     end;
end;

function TGlobalList.Add(AItem: TGLItem): Integer;
begin
     if not Active then
        Result:=-1
     else
     begin
        FLock.WaitFor(INFINITE);
        FLock.ResetEvent;
        try
           FItems^[FCount^]:=AItem;
           Inc(FCount^);
           Result:=FCount^;
        finally
           FLock.SetEvent;
        end;
     end;
end;

procedure TGlobalList.Delete(AIndex: Integer);
begin
     if not Active then
        exit
     else
     begin
        FLock.WaitFor(INFINITE);
        FLock.ResetEvent;
        try
           if (AIndex < 0) or (AIndex > FCount^) then
           begin
             exit;
           end;
           Dec(FCount^);
           FItems^[AIndex]:=FItems^[FCount^];
        finally
           FLock.SetEvent;
        end;
     end;
end;

function TGlobalList.GetFirst: Boolean;
begin
     if Active then
        Result:=FFirst
     else
        Result:=true;
end;

end.

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
lmikleCommented:
Make your record as a class and use a TObjectList.

// declaration
unit SomeUnit;

interface

uses Classes, Contnrs;

type
  TMyRecord = class
      AString: string;
      ANumber: Integer;

      constructor Create; // Create a new item
      constructor Load(Stream : TStream); // Create item and load data from any source - from Stream for example
  end;

var
  GlobalList : TObjectList;

implementation

constructor TMyRecord.Create;
begin
  AString := '';
  ANumber := 0;
end;

initialization
  GlobalList := TObjectList.Create;
  GlobalList.OwnObjects := True; // Set a list as a owner of stored objects - read documentation

finalization
  FreeAndNil(GlobalList);

end.

// using

uses SomeUnit;

var
  Item : TMyRecord;
begin

// Create new item
  Item := TMyRecord.Create;
  Item.AString := 'aaa';
  Item.ANumber := 10;
  GlobalList.Add(Item);

// Load item
  Stream := TFileStream.Create(FileName,fmOpenRead);
  GlobalList.Add(TMyRecord.Load(Stream));
  Stream.Free;

// You don't need to call destructor because the TObjectList will call it automatically
// then you'll remove item (OwnObjects = True)!!!

// Remove item by index
  GlobalList.Delete(I);

// Remove item by object
  GlobalList.Remove(Item);

// Remove all items
  GlobalList.Clear;

end;
0
 
Wim ten BrinkCommented:
It might just be easier to use COM to share that list between multiple applications. All you need is a singleton, a COM object that exists only once systemwide. The first application calling the COM object would create it. All others would just get a pointer to the existing object.

COM would be a better solution here.
0
 
iam_dumbAuthor Commented:
Workshop_Alex, who will destroy such COM object when all applications will close?
0
 
Lee_NoverCommented:
the system :-)
.. and it's a good idea really .. doesn't look like you''l need any special security configs as that's where COM becomes a general PIA
0
 
iam_dumbAuthor Commented:
Lee_Nover, I'd like to use singleton at LSP level (Layered Service Provider). Is this posisble?
0
 
Lee_NoverCommented:
seems so .. your LSP dll would simply create this COM obj (which would be in a separate library)
haven't done anything like that but should cause no problems .. the instalation part could be more trouble
I found this article to be quite nice: http://www.microsoft.com/msj/0599/LayeredService/LayeredService.aspx
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now