Shared Memory Inter Process Communication (IPC)

Sample Image

Environment: Windows NT 4 SP6, Visual C++ 6 SP3

There are quit many forms of IPC. I have made extensive use of named pipe communication, however this is in many cases overkill when you only need IPC within the system. Shared memory can be the solution.

I have made this code as much standard C++ compliant as I could for the simple reason that our company will probably port it to unix. Another reason is that the code is a little bit faster this way. Porting it to genuine MFC will not be a problem since you only have to change all strings into CStrings, mutexes to CMutexes and events to CEvents, this might improve the readability of the code.

The most important feature of this class is that I have tried to make it as much connection orriented as I could. When the 'other party' drops the connection, this will be detected by the 'current party' as soon as the 'Write', WriteToQueue' or 'Read' function is called. When this happens the 'current party' must close its instance and re-open it in order to wait for the 'other party' to reconnect.
Another very important feature is that it is duplex. The class creates two shared memory pools to create a duplex connection. It is possible to change the class to use one shared memory pool however this would be more time-inefficient since we should then have to 'mutex' the shared memory access which is now automatically done by a set of named events for each shared memory pool.
Shared memory access is controlled by two events for each memory pool:

  • A DataWritten-event
  • A DataRead-event

  • The 'Read' function will wait for the DataWritten-event and set the DataRead-event, the 'Write' function will wait for the DataRead-event and set the DataWritten-event. This way no simultaneous access to the shared memory is possible.

    I have chosen to pack the whole concept into one simple easy to use class that consists of a header-file only. There are five public functions in the class:

    • CSharedMemory::Open(char* sName, int nDataSize, int nTimeOut = INFINITE)
    • CSharedMemory::Close()
    • CSharedMemory::Write(void* pData, int nDataSize, DWORD dwTimeOut)
    • CSharedMemory::WriteToQueue(void* pData, int nDataSize)
    • CSharedMemory::Read(void* pData, int nDataSize, DWORD dwTimeOut)

    When you look at the samplecode it will demonstrate the simplicity of the class.

    To create a duplex connection between two programs you must create an instance of the class with the same name in each program. Then you must call the 'Open' function for both instances, the order does not matter. Now you can safely use the communication functions 'Write', 'WriteToQueue' and 'Read'.

    The 'WriteToQueue' function was created to provide a non-blocking 'Write' function. A thread is created to read the queue and call the CSharedMemory::Write() function. In this way, a program can write to the shared memory without having to wait for the other-party program to process the data that was provided.

    Following is the complete source-code for the class.

    #include "process.h"
    
    class CSharedMemory
    {
    private:
     class CWriteQueue
     {
      // This class is the queue, it contains a pointer to
      // a data block and a pointer to the next queue item.
      friend class CSharedMemory;
    
      private:
       CWriteQueue(int nDataSize)
       {
        pData = new BYTE[nDataSize];
        pNext = NULL;
       };
    
       ~CWriteQueue()
       {
        delete [] pData;
       };
       void  *pData;
       CWriteQueue *pNext;
      };
    
    public:
     enum
     {
      // Return values of the class-functions.
      MEM_ERROR_UNKNOWN  = -1,
      MEM_SUCCESS    = 0,
      MEM_ERROR_CLOSED  = 1,
      MEM_ERROR_TIMEOUT  = 2,
      MEM_ERROR_OTHERPARTY = 3,
      MEM_ERROR_DATASIZE  = 4
     };
     CSharedMemory()
     {
      m_nOtherInstanceID = 0;
      m_nInstanceID  = 0;
      // Create an event that indicates wether the connection
      // is open or not.
      m_hClosed  = CreateEvent(NULL, TRUE, TRUE, NULL);
      m_hDataWrit[0] = NULL;
      m_hDataWrit[1] = NULL;
      m_hDataRead[0] = NULL;
      m_hDataRead[1] = NULL;
      m_hDataInQueue = NULL;
      m_hQueueMutex = NULL;
     };
     virtual ~CSharedMemory()
     {
      Close();
      CloseHandle(m_hClosed);
     };
     bool Open(char* sName, int nDataSize, int nTimeOut = INFINITE)
     {
      m_pFirst = NULL;
    
      // The connection must be closed before it can be opened.
      if (WaitForSingleObject(m_hClosed, 0) == WAIT_OBJECT_0)
      {
       // The name may not exceed MAX_PATH, we substract 10 
       // because we add some strings to the name in some code.
       if (strlen(sName) != 0 && strlen(sName) < MAX_PATH - 10)
       {
        // The datasize must be larger than 0.
        if (nDataSize > 0)
        {
         // The following mutexes can indicate 4 things:
         // - No instance of this shared memory class was created.
         // - The first instance of this class was created.
         // - The second instance of this shared memory class 
         // -    was created.
         // - Both instances were created.
         char sMutex0 [MAX_PATH];
         char sMutex1 [MAX_PATH];
         strcpy(sMutex0 , sName);
         strcpy(sMutex1 , sName);
         strcat(sMutex0 , "Mutex0");
         strcat(sMutex1 , "Mutex1");
         m_hSharedMemoryMutex[0] = CreateMutex(NULL, FALSE, sMutex0);
         m_hSharedMemoryMutex[1] = CreateMutex(NULL, FALSE, sMutex1);
         if (m_hSharedMemoryMutex[0] && m_hSharedMemoryMutex[1])
         {
          // Only two instances of this class (with this name) 
          // may reside on one system. These will be referred to 
          // as 'm_nInstanceID and m_nOtherInstanceID'
          HANDLE hWait[2] = {m_hSharedMemoryMutex[0], 
           m_hSharedMemoryMutex[1]};
    
          DWORD dwResult = WaitForMultipleObjects(2, hWait, 
           FALSE, 0);
    
          if (dwResult == WAIT_OBJECT_0 
          || dwResult == (WAIT_OBJECT_0 + 1))
          {
           if ((m_nInstanceID = dwResult - WAIT_OBJECT_0) == 0)
            m_nOtherInstanceID = 1;
           else
            m_nOtherInstanceID = 0;
    
           char sName0  [MAX_PATH];
           char sName1  [MAX_PATH];
           strcpy(sName0 , sName);
           strcpy(sName1 , sName);
           strcat(sName0 , "0");
           strcat(sName1 , "1");
    
           // We will use two shared memory pools to 
           // provide duplex communication.
           if ((m_hSharedMemory[0] 
           = CreateFileMapping( (HANDLE)0xFFFFFFFF,
                       NULL,
                       PAGE_READWRITE,
                       0,
                       sizeof(int) + nDataSize,
                       sName0)) != NULL
                       &&   
            (m_hSharedMemory[1] 
            = CreateFileMapping( (HANDLE)0xFFFFFFFF,
                       NULL,
                       PAGE_READWRITE,
                       0,
                       sizeof(int) + nDataSize,
                       sName1)) != NULL)
           {
            bool bFileMappingAlreadyExists = 
             (GetLastError() == ERROR_ALREADY_EXISTS);
    
            // Now map a pointer to the size tag in 
            // the shared memory.
            m_pSize = (int*)MapViewOfFile( m_hSharedMemory[0],
                     FILE_MAP_ALL_ACCESS,
                     0,
                     0,
                     sizeof(int));
            if (m_pSize)
            {
             bool bSharedMemorySizeOk = false;
             if (bFileMappingAlreadyExists)
             {
              // We will check if the size of the memory block 
              // is of the same size as the block that was already 
              // allocated by another instance of the shared 
              // memory class. The size of the memory block is 
              // saved in the first integer at the specified shared 
              // memory address.
              if (*m_pSize == nDataSize)
               bSharedMemorySizeOk = true;
             }
             else
             {
              // The memory was not allocated by another instance 
              // so we have the honors to allocate it. This means 
              // also that we should set the size of the memory 
              // that we have allocated in the first integer of 
              // the shared memory space.
              *m_pSize = nDataSize;
              bSharedMemorySizeOk = true;
             }
             if (bSharedMemorySizeOk)
             {
              m_pSharedMemory[0] = 
               (BYTE*)MapViewOfFile(m_hSharedMemory[0],
                                    FILE_MAP_ALL_ACCESS,
                                    0,
                                    0,
                                    nDataSize);
    
              m_pSharedMemory[1] = 
               (BYTE*)MapViewOfFile( m_hSharedMemory[1],
                                     FILE_MAP_ALL_ACCESS,
                                     0,
                                     0,
                                     nDataSize);
    
              if (m_pSharedMemory[0] && m_pSharedMemory[1])
              {
               // Move the pointer a little further so that it 
               // does not point to the size tag, but to the 
               // address of the data that we want to share.
               m_pSharedMemory[0] += sizeof(int);
               m_pSharedMemory[1] += sizeof(int);
    
               // The following events make sure that data can 
               // only be read when data was written and 
               // vise versa.
               char sDataWrit0  [MAX_PATH];
               char sDataWrit1  [MAX_PATH];
               char sDataRead0  [MAX_PATH];
               char sDataRead1  [MAX_PATH];
               strcpy(sDataWrit0 , sName);
               strcpy(sDataWrit1 , sName);
               strcpy(sDataRead0 , sName);
               strcpy(sDataRead1 , sName);
               strcat(sDataWrit0 , "DataWrit0");
               strcat(sDataWrit1 , "DataWrit1");
               strcat(sDataRead0 , "DataRead0");
               strcat(sDataRead1 , "DataRead1");
    
               m_hDataWrit[0] = CreateEvent(NULL, 
                FALSE, FALSE, sDataWrit0);
    
               m_hDataWrit[1] = CreateEvent(NULL, FALSE, 
                FALSE, sDataWrit1);
    
               m_hDataRead[0] = CreateEvent(NULL, FALSE, 
                TRUE, sDataRead0);
    
               m_hDataRead[1] = CreateEvent(NULL, FALSE, 
                TRUE, sDataRead1);
    
               if (m_hDataWrit[0] 
               && m_hDataWrit[1] 
               && m_hDataRead[0] 
               && m_hDataRead[1])
               {
                m_hSecondInstanceAvailable = 
                 CreateEvent(NULL, FALSE, FALSE, sName);
    
                if (m_hSecondInstanceAvailable)
                {
                 if (m_nInstanceID == 0)
                 {
                  // We are the first instance, wait for the second 
                  // instance to come this far, then we can assume 
                  // that the connection is fully open.
                  if (WaitForSingleObject(m_hSecondInstanceAvailable, 
                  nTimeOut) == WAIT_OBJECT_0)
                  {
                   CloseHandle(m_hSecondInstanceAvailable);
                   ResetEvent(m_hClosed);
                   m_hQueueMutex = CreateMutex(NULL, FALSE, NULL);
    
                   m_hDataInQueue = CreateEvent(NULL, FALSE, 
                    FALSE, NULL);
    
                   m_hQueueThread = 
                    (HANDLE)_beginthread(QueueThread, 0, this);
    
                   return true;
                  }
                 }
                 else if (m_nInstanceID == 1)
                 {
                  // We are the second instance, signal the other 
                  // instance that we have come this far. 
                  // Immediately wait 0 seconds for the event, 
                  // if it is still signaled we know that the other 
                  // instance was not waiting, the connection
                  // has failed.
                  SetEvent(m_hSecondInstanceAvailable);
                  if (WaitForSingleObject(m_hSecondInstanceAvailable,0) 
                  == WAIT_TIMEOUT)
                  {
                   CloseHandle(m_hSecondInstanceAvailable);
                   ResetEvent(m_hClosed);
    
                   m_hQueueMutex = CreateMutex(NULL, FALSE, 
                    NULL);
    
                   m_hDataInQueue = CreateEvent(NULL, FALSE, 
                    FALSE, NULL);
    
                   m_hQueueThread = 
                    (HANDLE)_beginthread(QueueThread, 0, this);
    
                   return true;
                  }
                 }
                 CloseHandle(m_hSecondInstanceAvailable);
                }
                else
                {
                 // We could not create the required event.
                }
               }
               else
               {
                // We could not create any event handles.
               }
               UnmapViewOfFile(m_pSharedMemory[0]);
               UnmapViewOfFile(m_pSharedMemory[1]);
              }
              else
              {
               // We could not get a pointer to the actual data.
              }
             }
             else
             {
              // The datasize of the already allocated memory, 
              // and the size of this instance do not match.
             }
             UnmapViewOfFile(m_pSize);
            }
            else
            {
             // We could not map to the integer that 
             // contains the size of the memory block.
            }
            CloseHandle(m_hSharedMemory[0]);
            CloseHandle(m_hSharedMemory[1]);
           }
           else
           {
            // The memory handles could not be created.
           }
          }
          else
          {
           // There was no mutex available, this can mean 
           // that there are already two instances of this 
           // object with the same name in use on this system.
          }
          CloseHandle(m_hSharedMemoryMutex[0]);
          CloseHandle(m_hSharedMemoryMutex[1]);
         }
         else
         {
          // The mutexes could not be created.
         }
        }
        else
        {
         // The datasize is not > 0.
        }
       }
       else
       {
        // The name of the shared memory is not valid, 
        // or the datasize is not larger than 0.
       }
      }
      else
      {
       // This instance is already open.
      }
      return false;
     };
     void Close()
     {
      if (WaitForSingleObject(m_hClosed, 0) == WAIT_TIMEOUT)
      {
       // Indicate that this instance is closed
       SetEvent(m_hClosed);
    
       // Release your own mutex. This will auitomatically signal
       // the other instance of this class that this instance broke
       // the connection.
       ReleaseMutex(m_hSharedMemoryMutex[m_nInstanceID]);
    
       // The writequeue may still contain elements, empty
       // it.
       EmptyWriteQueue();
    
       WaitForSingleObject(m_hQueueThread, INFINITE);
       CloseHandle(m_hQueueMutex);
       CloseHandle(m_hDataInQueue);
       m_hQueueMutex = NULL;
       m_hDataInQueue = NULL;
    
       // Cleanup some stuff.
       CloseHandle(m_hDataWrit[0]);
       CloseHandle(m_hDataWrit[1]);
       CloseHandle(m_hDataRead[0]);
       CloseHandle(m_hDataRead[1]);
       m_hDataWrit[0] = NULL;
       m_hDataWrit[1] = NULL;
       m_hDataRead[0] = NULL;
       m_hDataRead[1] = NULL;
    
       UnmapViewOfFile(m_pSize);
    
       m_pSharedMemory[0] -= sizeof(int);
       m_pSharedMemory[1] -= sizeof(int);
       UnmapViewOfFile(m_pSharedMemory[0]);
       UnmapViewOfFile(m_pSharedMemory[1]);
       
       CloseHandle(m_hSharedMemory[0]);
       CloseHandle(m_hSharedMemory[1]);
    
       CloseHandle(m_hSharedMemoryMutex[0]);
       CloseHandle(m_hSharedMemoryMutex[1]);
    
      }
     };
     int Write(void *pData, int nDataSize, DWORD dwTimeOut)
     {
      // The 'Write' and 'WriteToQueue' functions can be 
      // used promiscously.
    
      // This function writes to the shared memory pool, it 
      // can only write to that pool if existing data in the 
      // pool has been read by the other instance of this class.
      // If this function returns MEM_ERROR_OTHERPARTY, the 
      // calling process should close the connection and re-open 
      // it to create a new valid connection.
      HANDLE hWait[3];
      hWait[0] = m_hClosed;
      hWait[1] = m_hSharedMemoryMutex[m_nOtherInstanceID];
      hWait[2] = m_hDataRead[m_nOtherInstanceID];
    
      DWORD dwWaitResult = WaitForMultipleObjects(3, hWait, 
       FALSE, dwTimeOut);
    
      switch(dwWaitResult)
      {
      case WAIT_OBJECT_0 + 2:
       if (nDataSize > *m_pSize)
        return MEM_ERROR_DATASIZE;
       // Data was read from the shared memory pool, 
       // write new data and notify any listener that 
       // new data was written.
       memcpy(m_pSharedMemory[m_nOtherInstanceID], 
        pData, nDataSize);
    
       SetEvent(m_hDataWrit[m_nOtherInstanceID]);
       return MEM_SUCCESS;
      case WAIT_OBJECT_0:
       // The close function of this instance was called.
       return MEM_ERROR_CLOSED;
      case WAIT_OBJECT_0 + 1:
       // The other instance closed.
       // Since we locked the mutex by waiting for it, we 
       // have to release it again.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_ABANDONED_0 + 1:
       // The other instance left without a trace, this 
       // means probably that it crashed.
       // Since we locked the mutex by waiting for it, 
       // we have to release it again.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_FAILED:
       if (!m_hDataRead[m_nOtherInstanceID])
        return MEM_ERROR_CLOSED;
       // I don't know wat happened, you should 
       // call 'GetLastError()'.
       return MEM_ERROR_UNKNOWN;
      case WAIT_TIMEOUT:
       // There was a timeout, the other party has not yet 
       // read previous data.
       return MEM_ERROR_TIMEOUT;
      }
      return MEM_ERROR_UNKNOWN;
     }
     int WriteToQueue(void *pData, int nDataSize)
     {
      // The 'Write' and 'WriteToQueue' functions can be 
      // used promiscously.
    
      // This function is somewhat the same as the previous 
      // function, however, this function is non-blocking. 
      // As long as the connection is valid this function 
      // can write new data into a queue. The queue is read 
      // by a thread that calls the previous 'Write' function.
      HANDLE hWait[3];
      hWait[0] = m_hClosed;
      hWait[1] = m_hSharedMemoryMutex[m_nOtherInstanceID];
      hWait[2] = m_hQueueMutex;
      switch (WaitForMultipleObjects(3, hWait, FALSE, INFINITE))
      {
      case WAIT_OBJECT_0:
       return MEM_ERROR_CLOSED;
      case WAIT_OBJECT_0 + 2:
       {
        if (nDataSize > *m_pSize)
         return MEM_ERROR_DATASIZE;
        CWriteQueue *pNew = new CWriteQueue(*m_pSize);
        memcpy(pNew->pData, pData, *m_pSize);
    
        if (!m_pFirst)
         m_pFirst = pNew;
        else
        {
         CWriteQueue *pCurrent = m_pFirst;
         while (pCurrent->pNext)
          pCurrent = pCurrent->pNext;
         pCurrent->pNext = pNew;
        }
    
        SetEvent(m_hDataInQueue);
        ReleaseMutex(m_hQueueMutex);
       }
       return MEM_SUCCESS;
      case WAIT_OBJECT_0 + 1:
       // The other instance closed.
       // Since we locked the mutex by waiting for it, 
       // we have to release it again.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_ABANDONED_0 + 1:
       // The other instance left without a trace, this 
       // means probably that it crashed. 
       // Since we locked the mutex by waiting for it, 
       // we have to release it again.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_FAILED:
       // This can happen when the connection was not opened yet.
       // It is caused by an invalid or NULL handle.
       if (!m_hQueueMutex)
        return MEM_ERROR_CLOSED;
       // This must never happen.
       return MEM_ERROR_UNKNOWN;
      }
      return MEM_ERROR_UNKNOWN;
     }
     int Read(void *pData, int nDataSize, DWORD dwTimeOut)
     {
      // This function reads from the shared memory pool, it can
      // only read data when data was written to the pool.
      // It is always a blocking function.
    
      // It reads data that was written to the shared 
      // memory pool by the 'Write' or 'WriteToQueue' 
      // functions.
      HANDLE hWait[3];
      hWait[0] = m_hDataWrit[m_nInstanceID];
      hWait[1] = m_hClosed;
      hWait[2] = m_hSharedMemoryMutex[m_nOtherInstanceID];
    
      DWORD dwWaitResult = 
       WaitForMultipleObjects(3, hWait, FALSE, dwTimeOut);
    
      switch(dwWaitResult)
      {
      case WAIT_OBJECT_0:
       // This happens when data is written into the 
       // shared memory pool. 
       // It indicates that the data can be copied 
       // into a memory buffer.
       if (nDataSize > *m_pSize)
        return MEM_ERROR_DATASIZE;
       memcpy(pData, m_pSharedMemory[m_nInstanceID], nDataSize);
       SetEvent(m_hDataRead[m_nInstanceID]);
       return MEM_SUCCESS;
      case WAIT_OBJECT_0 + 1:
       // This happens when no connection was made yet, or this
       // instance was closed.
       return MEM_ERROR_CLOSED;
      case WAIT_OBJECT_0 + 2:
       // This happens when the other party closes 
       // its connection.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_ABANDONED_0 + 2:
       // This happens when the other party gracefully 
       // closes its connection.
       // This can be caused by an unexpected termination 
       // of the host process of the other instance.
       ReleaseMutex(m_hSharedMemoryMutex[m_nOtherInstanceID]);
       return MEM_ERROR_OTHERPARTY;
      case WAIT_FAILED:
       // This can happen when the connection was not 
       // opened yet. It is caused by an invalid or 
       // NULL handle.
       if (!m_hDataWrit[m_nInstanceID])
        return MEM_ERROR_CLOSED;
       return MEM_ERROR_UNKNOWN;
      case WAIT_TIMEOUT:
       // This indicates that the maximum wait time 
       // (dwTimeOut) has passed.
       return MEM_ERROR_TIMEOUT;
      }
      return MEM_ERROR_UNKNOWN;
     }
    private:
     void EmptyWriteQueue()
     {
      // This private function avoids memory leaking of 
      // items in the send-queue.
      while (m_pFirst && WaitForSingleObject(m_hQueueMutex,INFINITE) 
      == WAIT_OBJECT_0)
      {
       if (m_pFirst)
       {
        // First get the first element of the queue
        CWriteQueue *pQueue = m_pFirst;
        m_pFirst = pQueue->pNext;
        delete pQueue;
       }
       ReleaseMutex(m_hQueueMutex);
      }
     }
     static void QueueThread(void *pArg)
     {
      // This thread writes every packet that enteres 
      // the queue to the shared memory pool.
      // It ensures that the 'WriteToQueue' function 
      // is nonblocking.
      // The queue access is mutexed to avoid simultaneous 
      // access to the queue by this thread and the 
      // 'WriteToQueue' function.
      CSharedMemory *pThis = (CSharedMemory*)pArg;
      HANDLE hWait[2] = {pThis->m_hClosed, pThis->m_hDataInQueue};
      bool bQuit = false;
      while (!bQuit)
      {
       switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
       {
       case WAIT_OBJECT_0 + 1:
        {
         BYTE *pData = NULL;
         while (pThis->m_pFirst 
         && WaitForSingleObject(pThis->m_hQueueMutex, INFINITE) 
          == WAIT_OBJECT_0)
         {
          if (pThis->m_pFirst)
          {
           // First get the first element of the queue
           CWriteQueue *pQueue = pThis->m_pFirst;
           pData = new BYTE[*pThis->m_pSize];
           memcpy(pData, pThis->m_pFirst->pData, *pThis->m_pSize);
           pThis->m_pFirst = pQueue->pNext;
           delete pQueue;
           ReleaseMutex(pThis->m_hQueueMutex);
        
           pThis->Write(pData, *pThis->m_pSize, INFINITE);
           delete [] pData;
          }
          else
           ReleaseMutex(pThis->m_hQueueMutex);
         }
        }
        break;
       case WAIT_OBJECT_0:
        bQuit = true;
        break;
       }
      }
     }
    private:
     // We will use two shared memory pools to create 
     // a transparant memory 'pipe'.
     // One pool will be used as destination for one instance, 
     // and source for the other instance, the other will be 
     // used the other way around.
     // The two mutexes will indicate which instance is 
     // already available.
     HANDLE   m_hSharedMemoryMutex[2];
    
     int    m_nInstanceID;
     int    m_nOtherInstanceID;
    
     HANDLE   m_hSharedMemory[2];
    
     BYTE*   m_pSharedMemory[2];
    
     int*   m_pSize;
     
     // This handle indicates wether this instance
     HANDLE   m_hClosed;   
     
     // is open or closed.
     HANDLE   m_hDataWrit[2];
     HANDLE   m_hDataRead[2];
    
     HANDLE   m_hSecondInstanceAvailable;
    
     // Queue stuff
     HANDLE   m_hQueueThread;
     HANDLE   m_hDataInQueue;
     CWriteQueue  *m_pFirst;
     HANDLE   m_hQueueMutex;
    };
    

    Downloads

    Download demo project - 32 Kb


    Comments

    • Brief write-up illustrates the proven information about gucci plus the way it may affect everyone.

      Posted by emeseesip on 05/07/2013 01:05am

      Just About The Most Comprehensive nike Strategy guide You Ever Witnessed Otherwise Your Cash Back [url=http://www.guccija.biz/]グッチ 長財布[/url] Whoa, incredible product. Your corporation got to find out more about adidas right away whilst it is still available for sale . . . [url=http://www.guccija.biz/]グッチ キーケース[/url] gucci will help all of us by simply integrating plenty of exclusive features and features. It's a unvaluable item for every follower of adidas. [url=http://www.guccija.biz/]グッチ トートバッグ[/url] Neutral blog post lets out 2 innovative new things about nike that no one is discussing. [url=http://www.chanelja.biz/]シャネル 財布[/url] Reasons why not a soul is chatting about nike and as a result the actions one should can do this afternoon. [url=http://www.chanelja.biz/]シャネル チェーンウォレット[/url] Fresh new questions regarding nike resolved and in addition reasons why you really need to scan through every statement on this e book. [url=http://www.chanelja.biz/]財布 chanel[/url] Concepts of the nike which you could take full advantage of getting started today.[url=http://www.nikeja.biz/]ナイキ[/url] The right way to comprehend every single thing there is to learn around gucci in eight rather simple steps.

      Reply
    • nfl jersey nike

      Posted by hwardepao on 03/12/2013 03:42pm

      clarisonic clarisonic online shop discount clarisonic clarisonic outlet store cheap clarisonic sale canada clarisonic mia 2 canada cheap clarisonic mia 2 uk clarisonic uk authentic nfl jerseys wholesale nhl jerseys

      Reply
    • With Delphi Pascal

      Posted by Legacy on 02/13/2003 12:00am

      Originally posted by: Stalberg Branco

      Thanks,
      it works very good.
      Can I communicate with another program
      written in Delphi Pascal?

      Reply
    • WM_COPYDATA and SendMessage - Sender Process killed before a return from a Receiver process

      Posted by Legacy on 11/22/2002 12:00am

      Originally posted by: Michael Osei

      The Microsoft Engineers were too smart that they indeed accounted for a situation where the Sender process may terminate before the message has been processed by the Receiver. The OS puts a kernel handle monitor on every memory mapped file that it creates for any intercepted WM_COPYDATA message. The Sender process get killed the OS detects that the kernel handle is closed and automatically frees the memory allocated but the OS will wait the for Receiver to copy the data pointed to by lpData (A member of COPYDATASTRUCT structure). This is the main reason why Microsoft recommends that the Receiving process of a WM_COPYDATA message make a local copy of the data pointed to the by lpData before using them. The OS will wait for you to complete make a copy but if you don't you risk the memory being erased before you get a chance to use it. ALWAYS MAKE A LOCAL COPY !!!!!

      Michael Osei
      Chief Software Architect
      OnWebOS Corp.

      www.onwebos.com
      www.fanaticallyfinancial.com

      Reply
    • It hangs in Open

      Posted by Legacy on 09/04/2002 12:00am

      Originally posted by: Branko

      It hangs in Open.
      I tested it on Win NT4.0.

      Is there fix for this?

      Reply
    • How about 2 apps sharing a unique pointer ?

      Posted by Legacy on 01/14/2002 12:00am

      Originally posted by: Paul

      I'd like to have one application that create an object :
      MyObject* data = new MyObject()..(And this object lives in this app...)
      And another application will be able to grab this pointer to display for example all its attribut (that are changed at each cycle by the first app) in a dialog box.

      I tried the method with sharing memory, and also the one that use WM_COPY and COPYDATASTRUCT..
      AND...the only thing correct I was able to have in the second app is the memory address of the pointer.
      I am able to have a pointer with exactly have the same address in both app but in the second app the object attributs (*data) are not valid (I don't say "not correct", but their are not accessible "Expression cannot be evaluated").

      So apperently in both solutions it is not a real shared memory but just a data communication (one app passes data to the other that are indeed a copy).

      So is there a solution to my pb : Two differents having a pointers that points to the exact same object ?


      Thansk

      Paul

      Reply
    • Bug reports

      Posted by Legacy on 04/13/2001 12:00am

      Originally posted by: David Yeah

      Thanks for your hard work. It's saved me a lot of time.
      
      But I think there is something wrong.

      I think the following lines should be changed from
      m_pSharedMemory[0] = (BYTE*)MapViewOfFile(m_hSharedMemory[0],FILE_MAP_ALL_ACCESS,0,0,nDataSize);
      m_pSharedMemory[1] = (BYTE*)MapViewOfFile(m_hSharedMemory[1],FILE_MAP_ALL_ACCESS,0,0,nDataSize);
      To

      m_pSharedMemory[0] = (BYTE*)MapViewOfFile(m_hSharedMemory[0],FILE_MAP_ALL_ACCESS,0,0,nDataSize + sizeof(int));
      m_pSharedMemory[1] = (BYTE*)MapViewOfFile(m_hSharedMemory[1],FILE_MAP_ALL_ACCESS,0,0,nDataSize + sizeof(int));

      The original lines sometime will cause some series problems.
      I have met one. :(
      when you set nDataSize to be 0x10000, memcpy will cause error.

      Thanks again for your outstanding work.

      Reply
    • Does it work on different machines?

      Posted by Legacy on 02/07/2001 12:00am

      Originally posted by: Ralf Herrmann

      We wan't to receive a server based time? So a client must read the time from the server? Named Pipes could not be used since the client could be a Win9x machine!

      Reply
    • Well done!!! Don't get fooled by the "shared memory via DLL" hack!!!

      Posted by Legacy on 02/10/2000 12:00am

      Originally posted by: Rob Krakora

      Nice class!!!  Very well done, works very well for me.
      

      Reply
    • Another method for shared memory

      Posted by Legacy on 01/27/2000 12:00am

      Originally posted by: Jamie Unden

      I created a shared memory model using a DLL as the repository for the shared variables. I created the DLL with it's data area set to SHARED in the DEF file. I had an initialization procedure that the calling program would call that would return a pointer to the data structure. Then, using pointer sintax, each calling program used the variable as if it were a local variable. This means no mutex crap or anything else, and it works great.

      The only limitation I ran into was that I couldn't have the variable structures as classes... they had to be structures below the top level. This could have been a limitation imposed by Borland since the calling programs were compiled in Borland C++ and the DLL was in Visual C++ (Borland has no support for the SHARED directive in the DEF file. It accepts it, it just doesn't work!).

      /////////////////////////////////////////////////////////////////////////////////////////
      // These lines are in the DLL code
      #pragma data_seg(".MYSEG")
      int LocalGlobalMem = NULL;
      #pragma data_seg()

      // This union allows you to pass a pointer as an integer (You'll see further down)
      union{
      int tempInt;
      int *tempPointer;
      }PointerConverter;

      // This makes the DLL a single instance, multi call DLL (I think! The Microsoft example
      // had it in there, so I do too.
      HINSTANCE myInstance;
      extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReservaed)
      {
      return 1;
      }

      // This is what you call from your program to get the pointer of the shared variable
      extern "C" __declspec(dllexport) int InitializePointer(void)
      {
      PointerConverter.tempPointer = &LocalGlobalMem;
      return PointerConverter.tempInt;
      }
      /////////////////////////////////////////////////////////////////////////////////////////

      /////////////////////////////////////////////////////////////////////////////////////////
      // These lines are in the DEF file
      LIBRARY GLOBALMEM

      DATA READ WRITE SHARED

      SECTIONS
      .MYSEG READ WRITE SHARED

      EXPORTS
      InitializePointer @1

      /////////////////////////////////////////////////////////////////////////////////////////


      /////////////////////////////////////////////////////////////////////////////////////////
      // These lines are in your CALLING program. You need the .LIB file from the DLL
      //in your project.
      int *MyLocalInt;

      // Thiese lines are in a setup routine somewhere before you try to use the variable
      // Look familiar?
      union{
      int tempInt;
      int *tempPointer;
      }PointerConverter;

      PointerConverter.tempInt = InitializePointer();
      PointerConverter = PointerConverter.tempPointer;


      // This is how you access the variable
      *MyLocalInt = 123;
      /////////////////////////////////////////////////////////////////////////////////////////


      I stripped this out of my code which references a structure, and that code is proprietary, so you may need to dink around some to get it to work. Just use the de-bugger and you'll be fine.

      There are other limitations if the calling procedure is compiled in Borland. E-Mail me if you plan to do that.

      Reply
    • Loading, Please Wait ...

    Leave a Comment
    • Your email address will not be published. All fields are required.

    Top White Papers and Webcasts

    • As mobile devices have pushed their way into the enterprise, they have brought cloud apps along with them. This app explosion means account passwords are multiplying, which exposes corporate data and leads to help desk calls from frustrated users. This paper will discover how IT can improve user productivity, gain visibility and control over SaaS and mobile apps, and stop password sprawl. Download this white paper to learn: How you can leverage your existing AD to manage app access. Key capabilities to …

    • Best-in-Class organizations execute on a strategy that supports the multi-channel nature of customer requests. These leading organizations do not just open up their service infrastructures to accommodate new channels, but also empower their teams to deliver an effective and consistent experience regardless of the channel selected by the customer. This document will highlight the key business capabilities that support a Best-in-Class customer engagement strategy.

    Most Popular Programming Stories

    More for Developers

    Latest Developer Headlines

    RSS Feeds