?
Solved

TList to Memory Mapping

Posted on 2004-08-18
9
Medium Priority
?
448 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
9 Comments
 
LVL 3

Expert Comment

by:Tyrsis
ID: 11835922
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 34

Expert Comment

by:Slick812
ID: 11837973
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 500 total points
ID: 11838573
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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 3

Expert Comment

by:lmikle
ID: 11839503
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
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 500 total points
ID: 11840027
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
ID: 11842598
Workshop_Alex, who will destroy such COM object when all applications will close?
0
 
LVL 12

Expert Comment

by:Lee_Nover
ID: 11843614
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
ID: 11844964
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
ID: 11849549
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

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In this video we outline the Physical Segments view of NetCrunch network monitor. By following this brief how-to video, you will be able to learn how NetCrunch visualizes your network, how granular is the information collected, as well as where to f…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses

752 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