Solved

TList to Memory Mapping

Posted on 2004-08-18
9
420 Views
Last Modified: 2010-04-05
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
Comment
Question by:iam_dumb
9 Comments
 
LVL 3

Expert Comment

by:Tyrsis
Comment Utility
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
 
LVL 33

Expert Comment

by:Slick812
Comment Utility
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
 
LVL 12

Assisted Solution

by:Lee_Nover
Lee_Nover earned 125 total points
Comment Utility
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
 
LVL 3

Expert Comment

by:lmikle
Comment Utility
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 125 total points
Comment Utility
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
 

Author Comment

by:iam_dumb
Comment Utility
Workshop_Alex, who will destroy such COM object when all applications will close?
0
 
LVL 12

Expert Comment

by:Lee_Nover
Comment Utility
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
 

Author Comment

by:iam_dumb
Comment Utility
Lee_Nover, I'd like to use singleton at LSP level (Layered Service Provider). Is this posisble?
0
 
LVL 12

Expert Comment

by:Lee_Nover
Comment Utility
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

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now