Solved

TList to Memory Mapping

Posted on 2004-08-18
9
436 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 125 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 125 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

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…

732 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