Click here to Skip to main content
Click here to Skip to main content

Interprocess communication tutorial

By , 7 Oct 2001
Rate this:
Please Sign up or sign in to vote.

Introduction

In this tutorial I build a dialog-based application with an edit box. When the number of instances of such an application are running, changes made by the user in one edit box are reflected in all other instances. That means, all instances always have the same text in their edit box. It looks like this:

Sample Image

Tutorial contains four steps:

  • Step 1 - posting registered window message with HWND_BROADCAST parameter to notify other instances when text is changed.
  • Step 2 - using memory-mapped file to set the same text in all instances.
  • Step 3 - synchronizing access to common memory block between all instances.
  • Step 4 - advanced synchronization using inter-process Single Writer/Multiple Reader Guard. (New)

The projects are built using MFC, this allows to concentrate on interprocess communication features and not on boring implementation details.

Step 1. Notification.

On this step we build dialog-based application which notifies other instances that text in edit box is changed by user. Dialog which receives such message handles it by some way - for example, adds "*" to text in edit box.

  • Create new MFC dialog-based application. Call in SameText. Remove Cancel button from dialog template; set "Close" text to IDOK button. Add edit box to dialog and set it's ID to IDC_EDIT_BOX. Arrange controls on dialog template.
  • Using MFC Class Wizard, add member CEdit m_edit_box to CSameTextDlg class. Member is connected to IDC_EDIT_BOX edit box.
  • Add protected member BOOL m_bNotify to CSameTextDlg class and set it to TRUE in constructor. This member will be used in EN_CHANGE message handler to prevent program reaction when text in edit box is changed by SetWindowText and not by user.
  • Add next constant to the start of SameTextDlg.cpp file:
    // Registered window message posted when user changes text in edit box
    const UINT    wm_Message = 
        RegisterWindowMessage(_T("CC667211-7CE9-40c5-809A-1DA48E4014C4"));
    
    String constant is copied using GUIDGen utility which comes with Microsoft Visual Studio.
  • Using MFC Class Wizard, add EN_CHANGE message handler to CSameTextDlg class for IDC_EDIT_BOX control. This function is called when user changes text in edit box.
  • Add manually handling of registered window message wm_Message to CSameTextDlg class. Add OnMessageTextChanged function definition to SameTextDlg.h:
        //}}AFX_MSG
        afx_msg LRESULT OnMessageTextChanged(WPARAM wParam, LPARAM lParam);
        DECLARE_MESSAGE_MAP()
    

    Add entry to CSameTextDlg message map in SameTextDlg.cpp:

        //}}AFX_MSG_MAP
        ON_REGISTERED_MESSAGE(wm_Message, OnMessageTextChanged)
    END_MESSAGE_MAP()
    

    Add OnMessageTextChanged function to SameTextDlg.cpp:

    LRESULT CSameTextDlg::OnMessageTextChanged(WPARAM wParam, LPARAM lParam)
    {
    	return 0;
    }
    
  • Write implementation of message handlers:
    // Function is called when text in edit box is changed
    // by user
    void CSameTextDlg::OnChangeEditBox() 
    {
        if ( m_bNotify )        // change is not done by SetWindowText
        {
            // Notify all running instances that text was changed
            ::PostMessage(HWND_BROADCAST, 
                          wm_Message, 	
                          (WPARAM) m_hWnd,
                          0);     
        }
    }
    
    // Handler for registered window message wm_Message
    // which is posted to all top-level windows in the system
    // when user changes text in edit box in any running instance 
    // of this program.
    // wParam contains dialog handler.
    LRESULT CSameTextDlg::OnMessageTextChanged(WPARAM wParam, LPARAM lParam)
    {
        // If message is posted from this instance do nothing
        if ( wParam == (WPARAM) m_hWnd )
            return 0;
    
        CString s;
        m_edit_box.GetWindowText(s);
    
        s += _T("*");
    
        m_bNotify = FALSE;  // prevent handling EN_CHANGE
                            // when text is changed from here and not by user
    
        m_edit_box.SetWindowText(s);    
    
        m_bNotify = TRUE;
    
        return 0;
    }
    

    When the program sets text in the edit box using SetWindowText, the EN_CHANGE message handler is called. I don't want to handle this case and use the m_bNotify member for this. When the program posts a message with the HWMD_BROADCAST parameter, it is posted to all top-level windows in the system, including the window which posted the message. To prevent the handling this message in the same window I wrote window handler in wParam parameter.

  • Build the project and run some number of instances. Change the text in the edit box in any instance - symbol "*" is added to the edit box text in all other instances.

Step 2. Using a memory-mapped file.

In this step I use the project built on step 1 and add to it a named memory-mapped file which contains an edit box text and is available from all running instances.

  • Add the next constants to the start of SameTextDlg.cpp file:
    // number of characters in memory-mapped file
    const DWORD dwMemoryFileSize = 4 * 1024;
    
    // memory-mapped file name
    const LPCTSTR sMemoryFileName = _T("D9287E19-6F9E-45fa-897C-D392F73A0F2F");
    

    The string constant is copied using the GUIDGen utility.

  • Add protected members HANDLE m_hFileMapping and LPVOID m_pViewOfFile to the CSameTextDlg class. Set them to NULL in the class constructor.

  • Using the MFC Class Wizard, add a WM_DESTROY message handler to the CSameTextDlg class. Add the next lines to this function:
        if ( m_hFileMapping )
        {
            if ( m_pViewOfFile )
            {
                UnmapViewOfFile(m_pViewOfFile);
            }
    
            CloseHandle(m_hFileMapping);
        }
    
  • Add the protected function Initialize to the CSameTextDlg class and call it from OnInitDialog.

  • Write the implementation of the Initialize function:
    // initialization
    void CSameTextDlg::Initialize()
    {
        // Ensure that text in edit box will not be longer
        // than memory file. Keep one character for end of string
        m_edit_box.SetLimitText(dwMemoryFileSize - 1);
    
    
        // Create file mapping which can contain dwMemoryFileSize characters
        m_hFileMapping = CreateFileMapping(
            INVALID_HANDLE_VALUE,           // system paging file
            NULL,                           // security attributes
            PAGE_READWRITE,                 // protection
            0,                              // high-order DWORD of size
            dwMemoryFileSize*sizeof(TCHAR), // low-order DWORD of size
            sMemoryFileName);               // name
    
        DWORD dwError = GetLastError();     // if ERROR_ALREADY_EXISTS 
                // this instance is not first (other instance created file mapping)
    
        if ( ! m_hFileMapping )
        {
            MessageBox(_T("Creating of file mapping failed"));
        }
        else
        {
            m_pViewOfFile = MapViewOfFile(
                m_hFileMapping,             // handle to file-mapping object
                FILE_MAP_ALL_ACCESS,        // desired access
                0,
                0,
                0);                         // map all file
    
            if ( ! m_pViewOfFile )
            {
                MessageBox(_T("MapViewOfFile failed"));
            }
    
            // Now we have m_pViewOfFile memory block which is common for
            // all instances of this program
        }
    
        if ( dwError == ERROR_ALREADY_EXISTS )
        {
            // Some other instance is already running,
            // get text from existing file mapping
            GetTextFromMemoryMappedFile();
        }
    }
    

    The CreateFileMapping function creates a file mapping object. The INVALID_HANDLE_VALUE parameter means that this object is backed up in the system paging file and not in any other file. MapViewOfFile maps the file mapping object to the virtual memory of the calling process. Since CreateFileMapping is called with the same name in all program instances, the memory block pointed by m_pViewOfFile is common for all instances and may be used for interprocess communication.

    If program tries to create a named file mapping object which already exists, the call doesn't fail and GetLastError returns ERROR_ALREADY_EXISTS. I use this to fill the edit box when the program starts - if this is not a first instance, it is a good idea to set the same text as in all running instances.

    If we allow the user to enter some number of characters to the edit box and want to keep them in a memory-mapped file, it's size should be doubled in a UNICODE configuration. Therefore I use sizeof(TCHAR) in call to CreateFileMapping.

  • Add the protected function GetTextFromMemoryMappedFile to the CSameTextDlg class and implement it as:
    // Get text from memory mapped file and set it to edit box.
    // Called from:
    // 1) OnMessageTextChanged, when user changed text in some 
    //    other instance of this program
    // 2) OnInitDialog, so that new instance gets text from
    //    running instance
    void CSameTextDlg::GetTextFromMemoryMappedFile()
    {
        if ( m_pViewOfFile  )
        {
            // read text from memory-mapped file
            TCHAR s[dwMemoryFileSize];
            
            lstrcpy(s, (LPCTSTR) m_pViewOfFile);
            
            // Write text to edit box.
            // SetWindowText raises EN_CHANGE event and
            // OnChangeEditBox is called. Ensure that OnChangeEditBox
            // does nothing by setting m_bNotify to FALSE
            m_bNotify = FALSE;  
            
            m_edit_box.SetWindowText(s);    
            
            m_bNotify = TRUE;
        }
    }
    

    This function reads text from the common memory region and sets it to the edit box.

  • Change the implementation of OnMessageTextChanged so that it looks like this:
    LRESULT CSameTextDlg::OnMessageTextChanged(WPARAM wParam, LPARAM lParam)
    {
        // If message is posted from this instance do nothing
        if ( wParam == (WPARAM) m_hWnd )
            return 0;
    
        // Get text from memory mapped file and set it to edit box
        GetTextFromMemoryMappedFile();
    
        return 0;
    }
    
  • Change the implementation of OnChangeEditBox so that it looks like this:
    void CSameTextDlg::OnChangeEditBox() 
    {
        if ( m_bNotify )        // change is not done by SetWindowText
        {
            // write text to memory-mapped file
            if ( m_pViewOfFile )
            {
                TCHAR s[dwMemoryFileSize];
                m_edit_box.GetWindowText(s, dwMemoryFileSize);
                
                lstrcpy( (LPTSTR) m_pViewOfFile, s);
                
                // Notify all running instances that text was changed
                ::PostMessage(HWND_BROADCAST, 
                    wm_Message, 	
                    (WPARAM) m_hWnd,
                    0);    
            }
        }
    }
    

    This function writes text to the common memory region and notifies all other running instances about this change.

  • Build the project and run some number of instances. Change the text in the edit box in any instance - all other instances get the same text.

Step 3. Synchronization.

When the number of processes read/write the same memory block, we need to synchronize access to it. I think this particular program will work fine without synchronization, but more complicated programs should use this feature. In this step I use project build on step 2 and add to it named mutex object.

  • Add a protected member HANDLE m_hMutex to the CSameTextDlg class and set it to NULL in the constructor. Add the next lines:
        if ( m_hMutex )
        {
            CloseHandle(m_hMutex);
        }
    
    to CSameTextDlg::OnDestroy.

  • Add the constants:
    // mutex name
    const LPCTSTR sMutexFileName = _T("4BD625E8-C16F-4836-9F62-60E151CCE3EC");
    
    // maxinum number of milliseconds to wait
    const DWORD dwMaxWait = 200;
    

    to the start of the SameTextDlg.cpp file. The string constant is copied using the GUIDGen utility.

  • Add the next lines to the function CSameTextDlg::Initialize, before if ( dwError == ERROR_ALREADY_EXISTS ):
        // create mutex used to syncronize access to m_pViewOfFile
        // between instances of this program
        m_hMutex = CreateMutex(
            NULL,                       // security attributes
            FALSE,                      // initial owner
            sMutexFileName);            // name
    
        if ( ! m_hMutex )
        {
            MessageBox(_T("CreateMutex failed"));
        }
    

    All instances call CreateMutex with the same name, so they get a handle to the same mutex object which will be used to synchronize access to the common memory block.

  • Change the implementation of functions OnChangeEditBox and GetTextFromMemoryMappedFile so that they look like this:
    // Function is called when text in edit box is changed
    // by user
    void CSameTextDlg::OnChangeEditBox() 
    {
        if ( m_bNotify )        // change is not done by SetWindowText
        {
            // write text to memory-mapped file
            if ( m_pViewOfFile  &&  m_hMutex )
            {
                TCHAR s[dwMemoryFileSize];
                m_edit_box.GetWindowText(s, dwMemoryFileSize);
    
                // get exclusive access to common memory block
                if ( WaitForSingleObject(m_hMutex, dwMaxWait) == WAIT_OBJECT_0 )
                {
                    lstrcpy( (LPTSTR) m_pViewOfFile, s);
                    ReleaseMutex(m_hMutex);
    
                    // Notify all running instances that text was changed
                    ::PostMessage(HWND_BROADCAST, 
                                  wm_Message, 	
                                 (WPARAM) m_hWnd,
                                  0);    
                }
            }
        }
    }
    
    // Get text from memory mapped file and set it to edit box.
    // Called from:
    // 1) OnMessageTextChanged, when user changed text in some 
    //    other instance of this program
    // 2) OnInitDialog, so that new instance gets text from
    //    running instance
    void CSameTextDlg::GetTextFromMemoryMappedFile()
    {
        if ( m_pViewOfFile  && m_hMutex )
        {
            // read text from memory-mapped file
            TCHAR s[dwMemoryFileSize];
    
            // get exclusive access to common memory region
            if ( WaitForSingleObject(m_hMutex, dwMaxWait) == WAIT_OBJECT_0 )
            {
                lstrcpy(s, (LPCTSTR) m_pViewOfFile);
                ReleaseMutex(m_hMutex);
    
                // Write text to edit box.
                // SetWindowText raises EN_CHANGE event and
                // OnChangeEditBox is called. Ensure that OnChangeEditBox
                // does nothing by setting m_bNotify to FALSE
                m_bNotify = FALSE;  
    
                m_edit_box.SetWindowText(s);    
    
                m_bNotify = TRUE;
            }
        }
    }
    

    Now before using the common memory block I get exclusive access to it by waiting for the mutex. After using the block I immediately release the mutex.

Step 4. Advanced synchronization using inter-process Single Writer/Multiple Reader Guard

The synchronization used in step 3 project is not optimal. The best way is to use a single-writer and multiple-reader synchronization.

The book Programming Applications for Microsoft Windows published by Microsoft Press contains the Single Writer/Multiple Reader Guard for in-process synchronization CSWMRG (Copyright (c) 2000 Jeffrey Richter).

My class CIPCReadWriteLock (the Single Writer/Multiple Reader Guard for inter-process communication) is based on Jeffrey Richter's CSWMRG class and may be used for inter-process communication. The description of this class is out of the scope of this tutorial. For details, see:

  • Single Writer/Multiple Reader Guard algorithm - the Jeffrey Richter's boook Programming Applications for Microsoft Windows (Copyright (c) 2000 Jeffrey Richter).
  • Inter-process communication features added to class - CIPCReadWriteLock class source code (included to demo project).

The class CIPCReadWriteLock is initialized with some name and maximum number of milliseconds to wait. It has functions: WaitToRead, WaitToWrite and Done. Using these functions we can get read/write access to common resources and release common resources.

In this step I use the project built on step 3 and replace the mutex with the CIPCReadWriteLock class.

  • Add the files IPCReadWriteLock.h and IPCReadWriteLock.cpp to the project. Add the line
    class CIPCReadWriteLock;
    

    to SameTextDlg.h. Replace the member HANDLE m_hMutex to CIPCReadWriteLock* m_pLock

  • .
  • In the CSameTextDlg constructor replace the line m_hMutex = NULL; with the line m_pLock = NULL;. In the CSameTextDlg::OnDestroy replace releasing of mutex with the lines:
        if ( m_pLock )
        {
            delete m_pLock;
        }
    
  • In the CSameTextDlg::Initialize replace the creating of the mutex with the lines:
        // create the Single Writer/Multiple Reader Guard
        m_pLock = new CIPCReadWriteLock(
            sReadWriteLockName,
            dwMaxWait);
    

    In the start of SameTextDlg.cpp replace the constant name sMutexFileName with sReadWriteLockName.

  • Change the implementation of the functions OnChangeEditBox and GetTextFromMemoryMappedFile so that they look like this:
    // Function is called when text in edit box is changed
    // by user
    void CSameTextDlg::OnChangeEditBox() 
    {
        if ( m_bNotify )        // change is not done by SetWindowText
        {
            // write text to memory-mapped file
            if ( m_pViewOfFile  &&  m_pLock )
            {
                TCHAR s[dwMemoryFileSize];
                m_edit_box.GetWindowText(s, dwMemoryFileSize);
    
                // get write access to common memory block
                if ( m_pLock->WaitToWrite() )
                {
                    lstrcpy( (LPTSTR) m_pViewOfFile, s);
                    m_pLock->Done();
    
                    // Notify all running instances that text was changed
                    ::PostMessage(HWND_BROADCAST, 
                                  wm_Message, 	
                                 (WPARAM) m_hWnd,
                                  0);    
                }
            }
        }
    }
    
    
    
    // Get text from memory mapped file and set it to edit box.
    // Called from:
    // 1) OnMessageTextChanged, when user changed text in some 
    //    other instance of this program
    // 2) OnInitDialog, so that new instance gets text from
    //    running instance
    void CSameTextDlg::GetTextFromMemoryMappedFile()
    {
        if ( m_pViewOfFile &&  m_pLock )
        {
            // read text from memory-mapped file
            TCHAR s[dwMemoryFileSize];
    
            // get read access to common memory region
            if ( m_pLock->WaitToRead() )
            {
                lstrcpy(s, (LPCTSTR) m_pViewOfFile);
                m_pLock->Done();
    
                // Write text to edit box.
                // SetWindowText raises EN_CHANGE event and
                // OnChangeEditBox is called. Ensure that OnChangeEditBox
                // does nothing by setting m_bNotify to FALSE
                m_bNotify = FALSE;  
    
                m_edit_box.SetWindowText(s);    
    
                m_bNotify = TRUE;
            }
        }
    }
    

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Alex Fr
Software Developer
Israel Israel
No Biography provided

Comments and Discussions

 
QuestionCreateFileMapping PinmemberDennis Birke27-Sep-13 9:04 
QuestionWhat if a window was hidden? [modified] PinmemberKEL36-Apr-13 0:52 
AnswerRe: What if a window was hidden? PinmemberKEL36-Apr-13 4:37 
QuestionAmazing program. Thank very much. It is things i am finding Pinmemberbuidinhba27-Nov-12 16:08 
I say i can commute between two dialog in an application. Or two form in TabControl.
If every one have sample about commute between two form (difference class) of TabControl. Please tell me. Thank so much
Bùi Đình Bá - Mechatronic Engineer
-------------------------------------------------------------
 

Questionfunctions PinmemberParallan26-Feb-07 4:11 
QuestionWhy 2 instances using different Mutex name when creating the mutexes still work? Pinmembervic1200024-Nov-06 10:17 
AnswerRe: Why 2 instances using different Mutex name when creating the mutexes still work? PinmemberAlex Fr24-Nov-06 19:30 
QuestionRiddle me this? PinmemberCSherrill16-Oct-06 14:02 
GeneralGood work Pinmembertoreernt20-Sep-06 21:10 
GeneralSingle writer / multi reader Pinmemberwaldermort8-Sep-06 6:01 
GeneralRe: Single writer / multi reader PinmemberAlex Fr8-Sep-06 7:45 
GeneralRe: Single writer / multi reader Pinmemberwaldermort8-Sep-06 18:46 
GeneralCIPCReadWriteLock PinmemberAnthony_Yio22-Nov-05 21:41 
GeneralDo not use Semaphore objects for interprocess communications Pinmemberdima_twin@hotmail.com10-Jul-05 10:11 
QuestionHow can I use this in ATL Com Wizard project PinmemberG.A.24-Jul-04 2:39 
GeneralPostMessage problem Pinmemberpercyvimal25-Dec-03 18:13 
QuestionWhat if Done metod failed? PinmemberMoFo6-Aug-03 3:27 
AnswerRe: What if Done metod failed? PinmemberAlex Farber6-Aug-03 3:44 
GeneralInter program communication under Windows Pinmemberhdus0014-Jul-03 20:23 
GeneralRe: Inter program communication under Windows PinmemberAlex Farber5-Jul-03 18:21 
Generalpointer PinsussAnonymous30-Jun-03 4:53 
GeneralRe: pointer PinmemberAlex Farber30-Jun-03 23:00 
GeneralBetween Application And Dll Pinmember:_Rocket_:24-Sep-02 18:25 
GeneralRe: Between Application And Dll PinmemberAlex F26-Sep-02 3:14 
GeneralRe: Between Application And Dll Pinmember:_Rocket_:26-Sep-02 14:16 
GeneralRe: Between Application And Dll PinmemberPaulo Lima5-Mar-03 7:38 
Questionread write guard using Critical Sections? PinmemberAaron Simmons18-Feb-02 8:24 
AnswerRe: read write guard using Critical Sections? PinmemberAlex Farber18-Feb-02 21:35 
General"Hidden" Windows not receiving HWND_BROADCAST Pinmemberzaq11-Dec-01 22:59 
GeneralRe: "Hidden" Windows not receiving HWND_BROADCAST PinmemberAlex Farber12-Dec-01 0:37 
GeneralRe: "Hidden" Windows not receiving HWND_BROADCAST Pinmemberzaq12-Dec-01 4:33 
GeneralGreat! PinmemberCathy17-Oct-01 14:19 
GeneralWell done, but ... PinmemberGennady Oster14-Oct-01 21:36 
GeneralWell done! PinmemberAnonymous9-Oct-01 1:20 
GeneralWhy is this article called <i>"Interprocess communication tutorial"</i> PinmemberDisappointed Reader5-Oct-01 2:22 
GeneralRe: Why is this article called <i>"Interprocess communication tutorial"</i> PinmemberAnonymous7-Oct-01 23:48 
GeneralRe: Why is this article called <i>"Interprocess communication tutorial"</i> PinmemberAnonymous8-Oct-01 18:47 
GeneralRe: Why is this article called <i>"Interprocess communication tutorial"</i> PinmemberAnother reader9-Oct-01 8:55 
GeneralRe: Why is this article called <i>"Interprocess communication tutorial"</i> PinmemberBraulio Díez8-Oct-01 20:45 
GeneralRe: Why is this article called <i>"Interprocess communication tutorial"</i> PinmemberAnonymous9-Oct-01 7:53 
GeneralThis IS useful PinmemberSreekanth Muralidharan9-May-05 20:14 
GeneralRe: Why is this article called &lt;i&gt;&quot;Interprocess communication tutorial&quot;&lt;/i&gt; Pinmemberekraemer28-Jan-04 21:22 
GeneralRe: Why is this article called &lt;i&gt;&quot;Interprocess communication tutorial&quot;&lt;/i&gt; PinmemberSreekanth Muralidharan9-May-05 20:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 8 Oct 2001
Article Copyright 2001 by Alex Fr
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid