Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Dynamic array and Memory Mapped File

79 views
Skip to first unread message

Jason Smart

unread,
Oct 6, 2003, 2:56:49 AM10/6/03
to
Hi

I am trying to use a dynamic array in my Memory Mapped DLL File. I have
the memory mapped file stuff working ok but trying to retrieve stored
information out of it from a different process causes some issues.

What I have (in theory) is a structure as...
PBaseTemplateInfo = ^TBaseTemplateInfo;
TBaseTemplateInfo = record
TemplateName: ShortString;
PublicIDList: array[0..CMaxPublicIDs-1] of ShortString;
end;

PSharedTemplateInfo = ^TSharedTemplateInfo;
TSharedTemplateInfo = array of TBaseTemplateInfo;

ideally the PublicIDList array would be dynamic too, but for the sake of
it I have set it's length (CMaxPublicIDs = 20 FYI).

I get AVs when I'm access the array so I must be doing something wrong
with the way I am allocating the array?? I can access it fine from the
application which I run "first". But retrieving the information written
to it in App1 from App2 causes the AV. If I statically set my array
length then it works fine.

I read on here that I might need to use ShareMem to access it?? I tried
that but it didn't work either.

I populate the structure as:
ReturnSharedTemplateInfo(GlobalSharedTemplateInfo); // dll function call
SetLength(GlobalSharedTemplateInfo^, GTemplateList.Count);

the loop through some stuff placing it into the array:
GlobalSharedTemplateInfo^[l].TemplateName := 'some value'; // eg

When I access it from the app which populated it in the first place, it
works ok, so I know the data is 'in' there...

When I try to access it (from my 2nd app) I use:
ReturnSharedTemplateInfo(SharedTemplateInfo);
where SharedTemplateInfo = TSharedTemplateInfo (I tried
PSharedTemplateInfo too)...

The "addressing" of the array I am guessing is where I am falling down.

my ReturnSharedTemplateInfo is defined in my DLL as:
procedure ReturnSharedTemplateInfo(var ASharedTemplateInfo:
PSharedTemplateInfo); stdcall;
begin
ASharedTemplateInfo := GSharedTemplateInfo;
end;

I hope I have provided enough info to give me an insight...
As I mentioned, it works FINE if I statically set the length of my
array, so the problem is not in my initialization of the MMF (I think??)

TIA

Jason
--
remove .--- to respond via email

Peter Below (TeamB)

unread,
Oct 6, 2003, 2:36:28 PM10/6/03
to
In article <3f81...@newsgroups.borland.com>, Jason Smart wrote:
> I am trying to use a dynamic array in my Memory Mapped DLL File. I have
> the memory mapped file stuff working ok but trying to retrieve stored
> information out of it from a different process causes some issues.
>
> What I have (in theory) is a structure as...
> PBaseTemplateInfo = ^TBaseTemplateInfo;
> TBaseTemplateInfo = record
> TemplateName: ShortString;
> PublicIDList: array[0..CMaxPublicIDs-1] of ShortString;
> end;
>
> PSharedTemplateInfo = ^TSharedTemplateInfo;
> TSharedTemplateInfo = array of TBaseTemplateInfo;
>
> ideally the PublicIDList array would be dynamic too, but for the sake of
> it I have set it's length (CMaxPublicIDs = 20 FYI).
>
> I get AVs when I'm access the array so I must be doing something wrong
> with the way I am allocating the array?? I can access it fine from the
> application which I run "first". But retrieving the information written
> to it in App1 from App2 causes the AV. If I statically set my array
> length then it works fine.
>
> I read on here that I might need to use ShareMem to access it?? I tried
> that but it didn't work either.
>
> I populate the structure as:
> ReturnSharedTemplateInfo(GlobalSharedTemplateInfo); // dll function call
> SetLength(GlobalSharedTemplateInfo^, GTemplateList.Count);

If this code is in the EXE and the variable GlobalSharedTemplateInfo points
to is in the DLL then you will need Sharemem on both sides.

> When I try to access it (from my 2nd app) I use:
> ReturnSharedTemplateInfo(SharedTemplateInfo);
> where SharedTemplateInfo = TSharedTemplateInfo (I tried
> PSharedTemplateInfo too)...

You never explain how the memory-mapped file comes into the picture. You
cannot simply treat the address of the shared memory as a
PSharedTemplateInfo, for example, since the behind-the-scenes handling of
dynamic arrays would run into serious problems here. You need to copy the
data from the shared memory to a properly sized dynamic array first.

>
> The "addressing" of the array I am guessing is where I am falling down.
>
> my ReturnSharedTemplateInfo is defined in my DLL as:
> procedure ReturnSharedTemplateInfo(var ASharedTemplateInfo:
> PSharedTemplateInfo); stdcall;
> begin
> ASharedTemplateInfo := GSharedTemplateInfo;
> end;

If GSharedTemplateInfo is the address of the shared memory then this is the
problem part. Where do you figure out how many TBaseTemplateInfo records are
in the shared memory, for example?

I would do it this way: the shared memory starts with a 4 byte integer that
holds the count of TBaseTemplateInfo records stored. You can then define a
record type that maps the start of the shared memory:

Type
TTemplateInfoarray = record
Count: Integer;
Elements: Integer; // dummy, we only need the address of this field
end;
PTemplateInfoArray = ^TTemplateInfoArray;

You allocate a memory mapped file of size GMemFileSize bytes (a constant) at
program start. I assume that GSharedTemplateInfo holds the address of the
shared memory block, it is now of type PTemplateInfoArray. With this in place
you can write two routines to copy template info from and to the shared
memory, which the DLL would either export or use from the exported routines:

procedure CopyTemplatesToSharedMem( const Templates: TSharedTemplateInfo );
var
Arraysize: Integer;
begin
Arraysize := Length(Templates)*Sizeof( Templates[0] );
if (Arraysize + Sizeof( Integer )) <= GMemFileSize then begin
SharedMutex.Aquire;
try
GSharedTemplateInfo^.Count := Length(Templates);
Move( Templates[0], GSharedTemplateInfo^.Elements, Arraysize );
finally
SharedMutex.Release;
end;
...notify other app that data is ready
end;
end;

procedure CopyTemplatesFromSharedMem( Var Templates: TSharedTemplateInfo );
begin
SharedMutex.Aquire;
try
SetLength( Templates, GSharedTemplateInfo^.Count );
Move( GSharedTemplateInfo^.Elements,
Templates[0],
GSharedTemplateInfo^.Count * Sizeof( Templates[0] ));
finally
SharedMutex.Release;
end;
end;

SharedMutex is the mutex that serializes access to the shared memory between
the applications. Note that this approach still requires ShareMem in EXE and
DLL, since the memory allocated for the array will be deallocated in the EXE.

--
Peter Below (TeamB)
Use the newsgroup archives :
http://www.mers.com/searchsite.html
http://www.tamaracka.com/search.htm
http://groups.google.com
http://www.prolix.be


Jason Smart

unread,
Oct 6, 2003, 11:23:40 PM10/6/03
to
> SharedMutex is the mutex that serializes access to the shared memory between
> the applications. Note that this approach still requires ShareMem in EXE and
> DLL, since the memory allocated for the array will be deallocated in the EXE.

Hi

Thank you for those two procs. I got them working nicely with a
couple of
little adjustments to suit.

However, I still have one problem when I go to exit my program. I get
an
"application error at xxxx reading from xxxx. The memory cannot be
"read". ".
So after much playing around with everything, I removed the ShareMem
completely
from my apps and DLLs and the problem went away. Will I be causing
some memory
issues here and that exception being raised is a way of telling me
this? Even
if I remove the reference to my DLL completely, the exception still
gets raised.

Here's the basic process of what I do:

"Run Application"...

DLL
====
The DLL has an entry point procedure which sets up another MMF record.

EXE
====
In my form create event, I do what is necessary to get the total
number of Templates.
This value gets stored in a field of a record initially created in the
first MMF.
I then call a procedure in my DLL which sets up the MMF base on this
size.

DLL Call
========
procedure InitializeSharedTemplateInfo;
var
Size: Integer;
begin
{ shared template info }
Size := GClientInfo^.TemplateCount *
SizeOf(TBaseTemplateInfo)+SizeOf(Integer);
GMemFileSize := Size;
STIMapHandle := CreateFileMapping(DWord(-1), nil, PAGE_READWRITE, 0,
Size, PChar(CMMFNameSTI));

if STIMapHandle = 0 then
RaiseLastWin32Error;

GSharedTemplateInfo := MapViewOfFile(STIMapHandle,
FILE_MAP_ALL_ACCESS, 0, 0,
Size);

if GSharedTemplateInfo = nil then
begin
CloseHandle(STIMapHandle);
RaiseLastWin32Error;
end;
end;

EXE
====
After I have Initialized my MMF here, I then populate it as (using
your kindly provided procs)...

procedure "Populate"
var
TI : TSharedTemplateInfo;
BTI : TBaseTemplateInfo;
begin
SetLength(TI, GTemplateList.Count);
for l := 0 to GTemplateList.Count-1 do
begin
T := ToTemplateData(GTemplateList.Items[l]);
BTI.TemplateName := T.Name;
TI[l] := BTI;
end;

CopyTemplatesToSharedMem(TI);
end;

Then whenever I want to retrieve this information I simply call the
CopyTemplatesFromSharedMem(TI) where TI = TSharedTemplateInfo.

All works great. Can access the info from my other app by calling the
CopyTemplatesFromSharedMem procedure. I just keeping getting this
Application Error being called... Could it be directly related to
these calls???

The other Question I have is regarding the constructor/destructor of
the mutex.
I initialize it in my DLL Entry point procedure then Free it again
when I clear
up in the DLL Exit point procedure. Is that OK??

eg
===
procedure CloseSharedData;
begin
SharedMutex.Free;
UnMapViewOfFile(GClientInfo);
CloseHandle(MapHandle);
end;

procedure DLLEntryPoint(Reason: DWord);
begin
case Reason of
DLL_PROCESS_ATTACH : OpenSharedData;
DLL_PROCESS_DETACH : CloseSharedData;
end;
end;

{ initialization part of the DLL }
begin
DLLProc := @DLLEntryPoint;
DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

Thanks again for your help. Much appreciated...

Jason

0 new messages