sharedMemory.c
/*
** $RCSfile: sharedMemory.c $
** $Revision: 1.24 $
** $Author: doomer $
** $Date: 1996/05/10 15:06:22 $
** Copyright (c) 1996 John Dumais
*/
#include "sharedMemory.h"
static struct node *veeSharedMemoryList;
static long timeToWait=INFINITE;
static char *lastNameReferenced;
static struct VeeSharedDataInfo *lastSegmentReferenced;
/********************************************************************
These functions are used to create shared memory segments of the
corresponding types. When we create a new segment, we add that
segment to a list, so that we may clean up after we no longer need
the shared memory.
Each of these functions accepts a symbolic name by which we may
reference the shared memory segment. If we try to create a segment
having a name that already exists in our list of segments, we will
ensure that the type and size of the data match that which is
already defined. If the data type and/or size does not match, we
return the error code 'vErrorAlreadyExists'.
********************************************************************/
long createLongShare(char *segmentName, long nLongs){
long returnVal;
returnVal=openSharedMemory(segmentName, TypeLong, nLongs, FALSE);
return(returnVal);
}
long createShortShare(char *segmentName, long nShorts){
long returnVal;
returnVal=openSharedMemory(segmentName, TypeShort, nShorts, FALSE);
return(returnVal);
}
long createFloatShare(char *segmentName, long nFloats) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeFloat, nFloats, FALSE);
return(returnVal);
}
long createDoubleShare(char *segmentName, long nDoubles) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeDouble, nDoubles, FALSE);
return(returnVal);
}
/********************************************************************
These functions open already-existing functions of the respective
types. Opening one of these segments has the effect of adding the
newly-opened segment to the list of segments.
Each of these functions accepts a symbolic name by which we may
reference the shared memory segment. If we try to create a segment
having a name that already exists in our list of segments, we will
ensure that the type and size of the data match that which is
already defined. If the data type and/or size does not match, we
return the error code 'vErrorAlreadyExists'.
********************************************************************/
long openLongShare(char *segmentName, long nLongs) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeLong, nLongs, TRUE);
return(returnVal);
}
long openShortShare(char *segmentName, long nShorts) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeShort, nShorts, TRUE);
return(returnVal);
}
long openFloatShare(char *segmentName, long nFloats) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeFloat, nFloats, TRUE);
return(returnVal);
}
long openDoubleShare(char *segmentName, long nDoubles) {
long returnVal;
returnVal=openSharedMemory(segmentName, TypeDouble, nDoubles, TRUE);
return(returnVal);
}
/********************************************************************
This function removes the shared memory segment 'segmentName' from
the list of segments. It also frees any memory that had been allocated
on this segment's behalf and removes the virtual file mapping from the
system.
********************************************************************/
long destroySharedMemory(char *segmentName) {
struct VeeSharedDataInfo *segmentEntry;
struct node *listPtr;
long returnVal=0;
BOOL result;
listPtr=iterateUntil(veeSharedMemoryList,
(void *)segmentName,
compareSegName);
if (listPtr) {
segmentEntry=(struct VeeSharedDataInfo *)removeFromList(
&veeSharedMemoryList, listPtr);
result=removeMappedSegment(segmentEntry);
returnVal=(long)((result == TRUE ) ? 0 : -1);
}
return(returnVal);
}
/********************************************************************
This function removes all the shared memory segment from the list of
segments. It also frees any memory that had been allocated on this
segment's behalf and removes the virtual file mapping from the system.
********************************************************************/
long destroyAllSharedMemory(void) {
BOOL result;
while (veeSharedMemoryList) {
result=removeMappedSegment(pop(&veeSharedMemoryList));
}
return((long)((result == TRUE) ? 0 : -1));
}
/********************************************************************
These functions allow you to write data into the shared memory
segment identified by the name 'segmentName'. If 'segmentName'
does not exist in the list of shared segments, this function returns
a negative value. If you try to write more data than the
segment has room for, we copy the first values that will fit into the
allocated space. An error trying
to acquire a lock causes a negative return value. Success results
in returning 0.
********************************************************************/
long writeLongData(char *segmentName, long *data, long nLongs) {
size_t dataSize;
dataSize=nLongs * sizeof(long);
return(writeSegment(segmentName, data, dataSize));
}
long writeShortData(char *segmentName, short *data, long nShorts) {
size_t dataSize;
dataSize=nShorts * sizeof(short);
return(writeSegment(segmentName, data, dataSize));
}
long writeFloatData(char *segmentName, float *data, long nFloats) {
size_t dataSize;
dataSize=nFloats * sizeof(float);
return(writeSegment(segmentName, data, dataSize));
}
long writeDoubleData(char *segmentName, double *data, long nDoubles) {
size_t dataSize;
dataSize=nDoubles * sizeof(double);
return(writeSegment(segmentName, data, dataSize));
}
/********************************************************************
These functions allow you to read data from the shared memory
segment identified by the name 'segmentName'. If 'segmentName'
does not exist in the list of shared segments, this function returns
a negative value. If you try to read more data than the
you have reserved in the data block pointed to by the argument 'data',
we copy the first values that will fit into the
allocated space. An error trying to acquire a lock causes a negative
return value. Success results in returning 0.
********************************************************************/
long readLongData(char *segmentName, long *data, long nLongs) {
size_t dataSize;
dataSize=nLongs * sizeof(long);
return(readSegment(segmentName, data, dataSize));
}
long readShortData(char *segmentName, short *data, long nShorts) {
size_t dataSize;
dataSize=nShorts * sizeof(short);
return(readSegment(segmentName, data, dataSize));
}
long readFloatData(char *segmentName, float *data, long nFloats) {
size_t dataSize;
dataSize=nFloats * sizeof(float);
return(readSegment(segmentName, data, dataSize));
}
long readDoubleData(char *segmentName, double *data, long nDoubles) {
size_t dataSize;
dataSize=nDoubles * sizeof(double);
return(readSegment(segmentName, data, dataSize));
}
static long nSegments;
/********************************************************************
This is a convenience function to tell you how many segment entries
are currently defined. Typically, you would use this in conjunction
with segment enumeration, so that a process needing to build a list
of shared segments would know how many segment descriptors to read.
********************************************************************/
long numberOfSegments(void) {
struct node *listPtr;
nSegments=0;
listPtr=iterateOver(veeSharedMemoryList, numSegments);
return(nSegments);
}
void numSegments(void *nodeData) {
nSegments++;
return;
}
static long nIterations;
static long nReturnEntries;
static char **returnSegNames;
static long *returnElements;
static short *returnTypes;
/********************************************************************
This function fills in the data you allocate with information
describing each segment currently in the list. The calling function
is responsible for allocating the data space which will holf the
segment descriptors. segmentNames is an nEntries-long array of
strings that will be filled in with nEntries or less segment names.
If there are more names than the calling function has allocated
space for, this function fills in only nEntries. If there are fewer
segments than there are allocated names, we fill in only as many
fields as there are segment entries.
The return value is the number of fields we filled in.
nElements is an nEntries-long array of longs. It is the calling
functions responsibility to allocated this data. This gets filled
in with the number of elements reserved for each segment entry.
dataType is an nEntry-long array of shorts that gets filled in with
the data type held in each segment entry.
********************************************************************/
long enumerateSharedMemory(char **segmentNames,
long *nElements,
short *dataType,
long nEntries) {
struct node *listPtr;
nIterations=0;
nReturnEntries=nEntries;
returnSegNames=segmentNames;
returnElements=nElements;
returnTypes=dataType;
listPtr=iterateOver(veeSharedMemoryList, enumerateSegments);
return(nIterations);
}
void enumerateSegments(void *nodeData) {
struct VeeSharedDataInfo *segmentEntry;
if (nIterations < nReturnEntries) {
segmentEntry=(struct VeeSharedDataInfo *)nodeData;
strncpy(returnSegNames[nIterations],
segmentEntry->segmentName,
strlen(returnSegNames[nIterations]) -1);
returnElements[nIterations]=segmentEntry->nDataElements;
returnTypes[nIterations]=segmentEntry->dataType;
nIterations++;
}
return;
}
/********************************************************************
This function attempts to acquire a write lock. If the attempt fails,
usually because of a time-out, it returns a negative value. Otherwise,
the return value is 0.
********************************************************************/
long getWriteLock(struct VeeSharedDataInfo *segmentEntry) {
DWORD waitResult;
waitResult=SWMRGWaitToWrite(&segmentEntry->sharedMemoryGuard,
timeToWait);
return(lockResult(waitResult));
}
/********************************************************************
This function attempts to acquire a write lock. If the attempt fails,
usually because of a time-out, it returns a negative value. Otherwise,
the return value is 0.
********************************************************************/
long getReadLock(struct VeeSharedDataInfo *segmentEntry) {
DWORD waitResult;
waitResult=SWMRGWaitToRead(&segmentEntry->sharedMemoryGuard,
timeToWait);
return(lockResult(waitResult));
}
/********************************************************************
This function returns an indication of the success or failure of an
attempt to acquire a read or write lock.
********************************************************************/
long lockResult(DWORD waitResult) {
long returnVal=0;
switch (waitResult) {
case WAIT_ABANDONED_0:
case WAIT_ABANDONED_0 + 1:
case WAIT_TIMEOUT:
returnVal=vErrorLockFailed;
break;
case WAIT_OBJECT_0:
case WAIT_OBJECT_0 + 1:
default:
break;
}
return(returnVal);
}
/********************************************************************
This function sets the amount of time to wait, in milliseconds, before
read and write operations bail out. The default is to wait forever.
********************************************************************/
void setTimeOut(long timeValue) {
timeToWait=timeValue;
}
/********************************************************************
This function is called by the create[Long|Short|String]Share()
functions. It performs a number of sanity checks to ensure the
validity of the shared memory segment it creates. We check to
see if a segment with the identified by 'segmentName' already exists.
If so, we check to see that the data type and size correct. If
not, we try to create the new segment.
This file contains a structure called 'veeSharedMemoryList' which is
the pointer to the start of the list of shared memory segments. If
this pointer is NULL, we initialize it to point at the start of a
list of shared memory segments.
********************************************************************/
short openSharedMemory(char *segmentName,
enum DataType type,
long nElements,
BOOL openExisting){
short searchResult=0;
short mapReturn;
struct VeeSharedDataInfo *segmentEntry;
struct node *newNode;
char *newName;
if(veeSharedMemoryList){
searchResult=segmentIsInList(segmentName, type, nElements);
if(searchResult < 0){
return(searchResult);
}
}
if (!searchResult) {
newName=strdup(segmentName);
if (!newName) {
return(vErrorAllocFailed);
}
segmentEntry=calloc(sizeof(struct VeeSharedDataInfo), 1);
if (!segmentEntry) {
free(newName);
return(vErrorAllocFailed);
}
newNode=createNewNode((void *)segmentEntry);
if (!newNode) {
free(newName);
free(segmentEntry);
return(vErrorAllocFailed);
}
segmentEntry->segmentName=newName;
segmentEntry->nDataElements=nElements;
segmentEntry->dataType=type;
if (!veeSharedMemoryList) {
veeSharedMemoryList=newNode;
}
else {
push(&veeSharedMemoryList, newNode);
}
if (openExisting) {
mapReturn=openMappedSegment(segmentEntry);
}
else {
mapReturn=createMappedSegment(segmentEntry);
}
if (mapReturn < 0) {
pop(&veeSharedMemoryList);
free(newName);
free(segmentEntry);
return(mapReturn);
}
}
return(0);
}
/********************************************************************
This is the function that opens a previously-created file mapping
object and maps that newly-opened object into the calling process'
data space.
********************************************************************/
short openMappedSegment(struct VeeSharedDataInfo *segmentEntry) {
HANDLE hMappedFile;
DWORD dataSize;
LPVOID baseAddr;
BOOL initResult;
dataSize=allocatedSize(segmentEntry);
hMappedFile=OpenFileMapping(
FILE_MAP_WRITE, /* read write access */
FALSE, /* children inherit handle */
segmentEntry->segmentName);
if (!hMappedFile) {
return(vErrorMapFailed);
}
baseAddr=MapViewOfFile(hMappedFile,
FILE_MAP_READ | FILE_MAP_WRITE,
0, /* high order mapping offset */
0, /* low order mapping offset */
0); /* map the entire segment */
if (!baseAddr) {
CloseHandle(hMappedFile);
return(vErrorMapFailed);
}
initResult=openReadWriteGuard(segmentEntry);
if (!initResult) {
UnmapViewOfFile(baseAddr);
CloseHandle(hMappedFile);
return(vErrorGuardInitFailed);
}
segmentEntry->segmentHandle=hMappedFile;
segmentEntry->baseAddr=baseAddr;
return(0);
}
/********************************************************************
This function creates a mapped file in virtual memory. Because we
do not associate the virtual memory with a physical disk file, two
or more processes may use this memory space to share data without
the need to physically hit a disk.
********************************************************************/
short createMappedSegment(struct VeeSharedDataInfo *segmentEntry) {
HANDLE hMappedFile;
DWORD dataSize=0;
LPVOID baseAddr;
BOOL initResult;
dataSize=allocatedSize(segmentEntry);
hMappedFile=CreateFileMapping(
(HANDLE)0xffffffff, /* create a virtual memory segment */
(LPSECURITY_ATTRIBUTES)0, /* default security */
PAGE_READWRITE,
0, /* high order mapping size */
dataSize, /* low order mapping size */
segmentEntry->segmentName);
if (!hMappedFile) {
return(vErrorMapFailed);
}
baseAddr=MapViewOfFile(hMappedFile,
FILE_MAP_READ | FILE_MAP_WRITE,
0, /* high order mapping offset */
0, /* low order mapping offset */
0); /* map the entire segment */
if (!baseAddr) {
CloseHandle(hMappedFile);
return(vErrorMapFailed);
}
initResult=initializeReadWriteGuard(segmentEntry);
if (!initResult) {
UnmapViewOfFile(baseAddr);
CloseHandle(hMappedFile);
return(vErrorGuardInitFailed);
}
segmentEntry->segmentHandle=hMappedFile;
segmentEntry->baseAddr=baseAddr;
return(0);
}
/*******************************************************************
This file calculates the size of the data space reserved when you
created the shared memory identified by 'segmentName'
*******************************************************************/
long allocatedSize(struct VeeSharedDataInfo *segmentEntry) {
long dataSize;
switch (segmentEntry->dataType) {
case TypeLong:
dataSize=sizeof(long) * segmentEntry->nDataElements;
break;
case TypeShort:
dataSize=sizeof(short) * segmentEntry->nDataElements;
break;
case TypeFloat:
dataSize=sizeof(float) * segmentEntry->nDataElements;
break;
case TypeDouble:
dataSize=sizeof(double) * segmentEntry->nDataElements;
break;
default:
return(vErrorUnknown);
break;
}
return(dataSize);
}
/*******************************************************************
This function is called when we create a shared memory segment. It
initializes a guard structure which allows multiple processes/threads
to read the data, while preventing a process from writing to the data
area while another is reading. This prevents a race condition and data
corruption which can arise from multiple process access to shared
data.
This function calls another function: SWMRGInitialize() which is
taken from the book:
Advanced Windows by Jeffrey Richter, published by Microsoft Press.
*******************************************************************/
BOOL initializeReadWriteGuard(struct VeeSharedDataInfo *segmentEntry) {
SWMRG *readWriteGuard;
readWriteGuard=&segmentEntry->sharedMemoryGuard;
return(SWMRGInitialize(readWriteGuard, segmentEntry->segmentName));
}
/*******************************************************************
This function is called when we open a shared memory segment. It
initializes a guard structure which allows multiple processes/threads
to read the data, while preventing a process from writing to the data
area while another is reading. This prevents a race condition and data
corruption which can arise from multiple process access to shared
data.
This function is adapted for our own use from a function
taken from the book:
Advanced Windows by Jeffrey Richter, published by Microsoft Press.
*******************************************************************/
BOOL openReadWriteGuard(struct VeeSharedDataInfo *segmentEntry) {
TCHAR szFullObjName[100];
LPCTSTR lpszObjName;
BOOL fOk;
SWMRG *readWriteGuard=&segmentEntry->sharedMemoryGuard;
readWriteGuard->hMutexNoWriter=NULL;
readWriteGuard->hEventNoReaders=NULL;
readWriteGuard->hSemNumReaders=NULL;
lpszObjName = ConstructObjName(
__TEXT("SWMRGMutexNoWriter"), segmentEntry->segmentName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk) {
readWriteGuard->hMutexNoWriter=OpenMutex(
MUTEX_ALL_ACCESS,
FALSE, /* children inherit handle */
lpszObjName);
}
lpszObjName = ConstructObjName(
__TEXT("SWMRGEventNoReaders"), segmentEntry->segmentName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk) {
readWriteGuard->hEventNoReaders=OpenEvent(
EVENT_ALL_ACCESS,
FALSE, /* children inherit handle */
lpszObjName);
}
lpszObjName = ConstructObjName(
__TEXT("SWMRGSemNumReaders"), segmentEntry->segmentName,
szFullObjName, ARRAY_SIZE(szFullObjName), &fOk);
if (fOk) {
readWriteGuard->hSemNumReaders=OpenSemaphore(
SEMAPHORE_ALL_ACCESS,
FALSE, /* children inherit handle */
lpszObjName);
}
if ((readWriteGuard->hMutexNoWriter == NULL) ||
(readWriteGuard->hEventNoReaders == NULL) ||
(readWriteGuard->hSemNumReaders == NULL)) {
SWMRGDelete(readWriteGuard);
fOk=FALSE;
}
else {
fOk=TRUE;
}
return(fOk);
}
/*******************************************************************
This function is called when we no destroy the shared memory segment.
It removes the guard that provides data protection.
This function calls another function: SWMRGDelete() which is taken
from the book:
Advanced Windows by Jeffrey Richter, published by Microsoft Press.
*******************************************************************/
void destroyReadWriteGuard(struct VeeSharedDataInfo *segmentEntry) {
SWMRGDelete(&segmentEntry->sharedMemoryGuard);
return;
}
/*******************************************************************
This function removes a segment of mapped virtual memory and frees
the members of the structure which we no longer need.
*******************************************************************/
BOOL removeMappedSegment(struct VeeSharedDataInfo *segmentEntry) {
BOOL result;
if (lastNameReferenced &&
(!strcmp(lastNameReferenced, segmentEntry->segmentName))) {
lastNameReferenced=(char *)0;
lastSegmentReferenced=(struct VeeSharedDataInfo *)0;
}
result=UnmapViewOfFile(segmentEntry->baseAddr);
CloseHandle(segmentEntry->segmentHandle);
destroyReadWriteGuard(segmentEntry);
free(segmentEntry->segmentName);
free(segmentEntry);
return(result);
}
/*******************************************************************
This function is called from openSharedMemory() and searches for
an occurence of a given shared memory segment in the list of
segments. We make use of the list's iterateUntil() function to allow
us to easily search the list.
A negative return value indicates an error, usually because we found
an entry with the given name, but the type and/or size did not match
the requested type and/or size. A zero return value indicates that
we did not find an entry with the given segment name. A positive
return value indicates that we found an entry of the same name, size,
and type of that which was requested.
********************************************************************/
short segmentIsInList(char *segmentName,
enum DataType type,
long nElements){
struct node *listPtr;
struct VeeSharedDataInfo *segmentEntry;
short returnVal=0;
listPtr=iterateUntil(veeSharedMemoryList,
(void *) segmentName,
compareSegName);
if(listPtr){
segmentEntry=(struct VeeSharedDataInfo *)listPtr->data;
if((segmentEntry->dataType == type) &&
(segmentEntry->nDataElements == nElements)){
returnVal=1;
}
else{
returnVal=vErrorAlreadyExists;
}
}
return(returnVal);
}
/********************************************************************
This function is called from segmentIsInList() and serves as the
callback routine which allows us to determine if a segment of a given
name already exists in the list. A return value of 0 indicates that
we wish to terminate the search, usually because we found a match.
********************************************************************/
BOOL compareSegName(void *nodeData, void *cmpData){
struct VeeSharedDataInfo *segmentInfo=(struct VeeSharedDataInfo *)nodeData;
char *segmentName;
segmentName=segmentInfo->segmentName;
if(!strcmp(segmentName, (char *)cmpData)){
/* we found a match */
return(0);
}
/* keep searching */
return(1);
}
/********************************************************************
This function copies data into the file mapping encapsulated in
the shared segment identified by segmentName. We first check the
data's size against the room reserved in the shared segment to
prevent an access violation.
Before attempting to copy the data, we acquire a write lock to
prevent data corruption.
The return value is zero on success or a manifest constant (less
than zero) on failure.
********************************************************************/
long writeSegment(char *segmentName, void *data, size_t dataSize) {
struct node *listPtr;
long canWrite;
size_t allocatedDataSize;
/*
** We don't want to search the list of shared memory segments
** every time we read or write. If we haven't yet written to
** or read from any memory, or the last action referenced a
** different segment, find it in the list. This way we can
** remember it for future use.
*/
if ((!lastNameReferenced) ||
(strcmp(segmentName, lastNameReferenced))) {
listPtr=iterateUntil(veeSharedMemoryList,
(void *) segmentName,
compareSegName);
if (!listPtr) {
return(vErrorWriteFailed);
}
lastSegmentReferenced=(struct VeeSharedDataInfo *)listPtr->data;
lastNameReferenced=segmentName;
}
/*
** If we are trying to write more data than we have allocated
** room for in the shared memory segment, we can crash.
*/
allocatedDataSize=(size_t)allocatedSize(lastSegmentReferenced);
if (allocatedDataSize < dataSize) {
dataSize=allocatedDataSize;
}
/*
** Acquire a write lock & write the data. Then release the
** write lock.
*/
canWrite=getWriteLock(lastSegmentReferenced);
if (canWrite < 0) {
return(canWrite);
}
memcpy(lastSegmentReferenced->baseAddr, data, dataSize);
SWMRGDoneWriting(&lastSegmentReferenced->sharedMemoryGuard);
return(0);
}
/********************************************************************
This function copies dats from the mapped file encapsulated in the
shared segment identified by segmentName into the space 'date'
points to. This function copies dataSize or less bytes, depending on
whether dataSize or the shared segment's allocated size is smaller.
Before copying the data, we acquire a read lock to prevent data
corruption.
The return value is zero on success or a manifest constant less than
zero on failure.
********************************************************************/
long readSegment(char *segmentName, void *data, size_t dataSize) {
struct node *listPtr;
long canRead;
size_t allocatedDataSize;
if ((!lastNameReferenced) ||
(strcmp(lastNameReferenced, segmentName))) {
listPtr=iterateUntil(veeSharedMemoryList,
(void *)segmentName,
compareSegName);
if (!listPtr) {
return(vErrorReadFailed);
}
lastSegmentReferenced=(struct VeeSharedDataInfo *)listPtr->data;
lastNameReferenced=segmentName;
}
allocatedDataSize=(size_t)allocatedSize(lastSegmentReferenced);
if (allocatedDataSize < dataSize) {
dataSize=allocatedDataSize;
}
canRead=getReadLock(lastSegmentReferenced);
if (canRead < 0) {
return(canRead);
}
memcpy(data, lastSegmentReferenced->baseAddr, dataSize);
SWMRGDoneReading(&lastSegmentReferenced->sharedMemoryGuard);
return(0);
}
/********************************************************************
This function returns a string by which you can identify the revision
of this dll.
********************************************************************/
static char rcsHeader[]="$Id: sharedMemory.c 1.24 1996/05/10 15:06:22 doomer Exp $";
char *revInfo(void) {
return(rcsHeader);
}
/*
** $Log: sharedMemory.c $
** Revision 1.24 1996/05/10 15:06:22 doomer
** comments
** Revision 1.23 1996/02/06 13:37:06 doomer
** fixed enumerateSegments(). It was calling strlen() on the value of
** returnSegNames -1 instead of calling strlen(returnSegNames) -1.
** Revision 1.22 1996/02/02 11:14:42 doomer
** cleaned things up a little.
** Revision 1.21 1996/02/02 09:51:34 doomer
** cleaned up some segmentation faults.
** Revision 1.20 1996/02/01 11:53:02 doomer
** first time I git round trip with locking working
** Revision 1.19 1996/01/31 16:31:22 doomer
** removed unnecessary call to destroyReadWriteGuard() in createMappedSegment().
** it is already done if the guard initialization fails.
** Revision 1.18 1996/01/31 13:15:02 doomer
** added stuff to read & enumerate shared segments.
** Revision 1.17 1996/01/30 17:58:56 doomer
** added copyright information.
** Revision 1.16 1996/01/30 17:54:04 doomer
** added some comments.
** Revision 1.15 1996/01/30 17:49:40 doomer
** added default case to switch() statement in lockResult().
** Revision 1.14 1996/01/30 17:42:42 doomer
** added stuff to write to a shared memory segment.
** Revision 1.13 1996/01/30 15:55:58 doomer
** added the code to initialize and destroy the guarding structure when you
** create and destroy shared memory segments.
** Revision 1.12 1996/01/29 18:04:54 doomer
** added revInfo() function
** Revision 1.11 1996/01/29 17:24:20 doomer
** added implementation for destroyAllSharedMemory().
** changed definition of removeMappedSegment() to take
** struct VeeSharedDataInfo * as an argument. Did this so that
** destroyAllSharedMemory() could use the same function to remove
** a mapped segment.
** Revision 1.10 1996/01/29 16:53:52 doomer
** added function removeMappedSegment() for use with destroyAllSharedMemory().
** chaged destroySharedMemory() to call this function.
** Revision 1.9 1996/01/29 16:25:30 doomer
** createShortShare() was allocaitng space for longs.
** Revision 1.8 1996/01/29 15:55:02 doomer
** added implementation of destroySharedMemory().
** in createSharedMemory(), changed the call to calloc() to refer to the
** data type. It had referred to the pointer to the data, & was only
** allocating room for the pointer.
** Revision 1.7 1996/01/29 14:57:30 doomer
** gutted stuff to make this library depend on list.dll for its list management
** compiled all the stuff to add a shared memory segement.
** Revision 1.6 1995/12/29 18:07:20 doomer
** removed references to lastEntry from removeFromList() & destroySharedMemory().
** in removeSharedMemory(), adjusted the list head pointer in the case where
** I wanted to remove the first entry (which could, by definition be the
** last entry) in the list.
** Revision 1.5 1995/12/29 17:42:48 doomer
** added definition for removeFromList()
** moved sharedmemoryList to be a static in the dll.
** added sharedMemoryHandle to addToList.
** added definition for destroySharedMemory().
** added pointer to previous list entry into struct veeSharedDataInfo
** and made addToList keep it up to date.
** changed return value from isInList() to be of type
** struct veeSharedDataInfo.
** Revision 1.4 1995/12/29 16:35:18 doomer
** a bunch of typos to get first successful compile.
** Revision 1.3 1995/12/29 16:16:10 doomer
** added definition for addToList()
** changed the order of the calls to CloseHandle() & UnmapViewOfFile().
** I don't know if closing the handle to the shared memory segment before
** unmapping the memory that's pointed to the thing the handle represents
** is an error or not.
** Revision 1.2 1995/12/29 15:39:50 doomer
** added definition for isInList()
** Revision 1.1 1995/12/29 15:29:12 doomer
** Initial revision
*/