I have a complex class which contain many different variables, methods
etc.
I haved tried to share this class object by creating a DLL file with a
.def file, but without success, because each time a new application
using this DLL is started the shared object is somehow reset
(constructor is run).
I was wondering if it is possible to share complex class object
instances between processes and not just simple types like integers
and strings..
Is this possible with CreateFileMapping ? I have only come across
shareing of simple types.
Thanks for your time,
Miv
Virtual Methods are a no-no. Function address be not be the same across
processes.
Pointers in the class won't work.
You must have an inteface in the dll to obtain the object and only call the
constrctor once.
Basically this is a total pain.
Why not Create a class that raps a structure and put the struct in the
shared memory? This way the class can automatically manage the Mapping
itself.
I did this with a C++ cross-process critical-section object. The technique
works very well as long as you do not need to use polymorphic classes and
virtual functions. If you do emulate them with per-process pointer tables
and store the index of the function in the struct not its pointer.
So every app gets its own copy of any static data & classes.
A way around this limitation is to write an out of processes server (exe)
that hosts a single instance of an object - sometimes known as a singleton.
The MSDEV COM wizard thing is good for quickly knocking up interfaces.
As Stuart Dunn already said - trying to place the object in shared memory is
possible but there are a lot of limitations. Mainly because pointers in one
process are only valid in that process. Put a pointer in shared memory and
if another application reads that pointer value and tries to use it, it will
almost certainly cause an exception.
Even the shared memory pointer itself might be different between processes.
This is a nuisance because it means the 'this' pointer cannot be shared.
Your shared memory pointer in app1 might be 1234h and in app2 its 2345h even
though it points to the same block of shared memory.
I wrote a framework for c++ classes so that I could just do "p =
new("SomeName") SharedMemoryClass()" and the returned pointer would actually
be shared memory named "SomeName" with the instance of SharedMemoryClass
sitting in there. This is a nifty way of doing it (and was fun to write)
but you can achieve the exact same functionality by, as Stuard recommended,
having normal classes that under the hood reads/writes to a named block of
shared memory. You can only put real data in there - not pointers to real
data.
If you used Named Pipes then sending a pointer from one machine to another
wouldn't make much sense either - you would always send real data.
Regards,
Ed.
"Michael Kristensen" <miv...@stofanet.dk> wrote in message
news:b1ead7a3.03032...@posting.google.com...
Has your class had a number 1 hit? Its a rap monster :-) (sorry couldn't
resist).
>shared memory? This way the class can automatically manage the Mapping
>itself.
I concur, I've done much the same on a variety of projects. For the
shared memory stuff, store the data of interest (whatever it may be) and
have each process then either wrap the data with classes or transform
the data to a more useful/powerful format.
For anything that needs "pointers", I use offsets into the shared data
region (assuming the pointers are into the data region). Using offsets
allows the region to be based at different addresses in each process,
and also allows the region to be moved should you need to resize it on
the fly (and get a different address back and have to copy your data
from the old mapping to the new mapping).
If you do go the "offsets" route, provide a function./method to
calculate the true address from the offset and vice versa. This way if
you change your implementation, you only need to change one thing rather
than hunt around your code looking for those places that calculate
addresses. If you really don't want a function, put it in a MACRO.
Stephen
--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk
RSI Information: http://www.objmedia.demon.co.uk/rsi.html
CreateFileMapping gives you a block of memory that multiple processes
can attach to and read & write. I would create a struct to hold my
shared data in the shared memory block and then create a class that
manages access to the struct. That class would have a single member
variable -- a pointer to the struct. That is the class constructor
would call CreateFileMapping and MapViewOfFile to get a pointer to the
shared memory. Of course, the class will also need a shared mutex
(call CreateMutex and pass in a name) to serialize access to the
struct.
Here's some shared memory code
BOOL fInit;
int shmemSize = sizeof(SharedData) + 2; //2 for safety
hMapObject = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // no security attributes
PAGE_READWRITE, // read/write access
0, // size: high 32-bits
shmemSize, // size: low 32-bits
"WapSrvMem"); // name of map object
if (hMapObject == NULL)
return FALSE;
// The first process to attach initializes memory.
fInit = (GetLastError() != ERROR_ALREADY_EXISTS);
// Get a pointer to the file-mapped shared memory.
lpvMem = MapViewOfFile(
hMapObject, // object to map view of
FILE_MAP_WRITE, // read/write access
0, // high offset: map from
0, // low offset: beginning
0); // default: map entire file
if (lpvMem == NULL)
return FALSE;
// Initialize memory if this is the first process.
if (fInit)
memset(lpvMem, '\0', shmemSize);
return TRUE;
Also you must be aware of COM's threading models and their impact on your
code performance and security (if you use any security APIs). COM's
requirement for a message pump can be a problem at times as well. (Its a
pain in the arse if you are writting AsyncIO driven middleware - as I have
found...)
Anyone have a complete example of how this is done? Sharing a
singleton class object between processes?
For the moment I will try creating a wrapper class handling all the
file mapping and mutex protection, but I any have an example of this,
please feel free to drop me an email with your project.
Thanks a lot.
Michael Kristensen,
Sofeware Developer
m...@visanti.com
miv...@stofanet.dk
>For anything that needs "pointers", I use offsets into the shared
data
>region (assuming the pointers are into the data region). Using
offsets
>allows the region to be based at different addresses in each process,
>and also allows the region to be moved should you need to resize it
on
>the fly (and get a different address back and have to copy your data
>from the old mapping to the new mapping).
Got an example on this? I seem to get an access violation when
accessing som of the class objects member functions.
Thanks,
Michael
Sorry, its commercial code. I can't share it.
Its straight forward though. Something like (heavily abbreviated)
typedef struct _myData
{
DWORD childOffset;// offset from start of shared memory
DWORD myData1;
DWORD myData2;
} MY_DATA;
MY_DATA is what is stored in the shared memory. Lots of them. In this
example, childOffset is the offset from the start of shared memory (in
bytes) to the location where the child MY_DATA of this MY_DATA is
located.
For example you may specify a value of -1 to mean the offset is not
valid. Note also, that offsets in the range 0..(sizeof(MY_DATA) -1) are
offsets to the internals of MY_DATA, so by definition, they are not
valid offsets.
Since "childOffset" is a byte offset, arithmetic is straighforward:
MY_DATA *getPointerFromOffset(DWORD baseAddress,
DWORD offset)
{
return (MY_DATA *)baseAddress + offset;
}
where baseAddress is the address in memory that the memory mapped file
is mapped to. Note I've used DWORD here instead of void *, for
simplicity - if you plan to have your code portable to say, WIN64, you
don't want to write the code like this, you want to use pointers, or
pointer sized values (DWORD_PTR).
The code I'm writing is tied to Win32 and i386 in so many ways that it
will never be portable (it has assembler in it), so I can use DWORD if
it is more convenient.
I then write classes that use a MY_DATA pointer to gain access to the
shared memory. Just keep the data layout and basic accessing methods
simple, and build from that.