Inter-Process Communication Using WM_COPYDATA

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

IPC Demo Image

Recently I have seen numerous postings on the message board asking about options for inter-process communication.  One of the many techniques available is the standard SendMessage function with the WM_COPYDATA message.  There seems to be very few samples available regarding this so I decided to write one.  Once I got into this, I found two other samples : Paul DiLascia’s TRACEWIN project in the MSJ and Spy in the SDK samples.  At least, now will have its own.:)

To use WM_COPYDATA, SendMessage is called as follows :

SendMessage( hwReceiver, WM_COPYDATA, (WPARAM) hwSender, (LPARAM) pData ); 

The actual data passed in the LPARAM argument (pData) is a pointer to a data structure that is defined as follows :

typedef struct {
    DWORD dwData;  // a 32-bit data item passed to the receiver
    DWORD cbData;  // the size in bytes of the data pointed to be lpData
    PVOID lpData;  // pointer to the data passed to the receiver or null } COPYDATASTRUCT;

Note that SendMessage must be used, PostMessage can not be.  Also, as stated in the docs about WM_COPYDATA, the receiving process should consider the data received to be read-only and valid only during the processing of the message.

My little demo project illustrated above takes data from one dialog-based app and sends it to a second one.  It just so happens that they are two instances of the same app but this was done to simplify downloading and building of the project. 

Following is the code used to send the message.  In this case, the data consists of a text string, the line to display it on, and the foreground and background colors to display it in.  The data is loaded into my custom message structure (MsgCmd) and then loaded into the COPYDATASTRUCT.

int CIpcSendDlg::SendIpcMsg( int cmd, int line, COLORREF fgclr,
                             COLORREF bgclr, ccp txt ) {
    MsgCmd msg;

    msg.command = cmd;
    msg.line = line;
    msg.fgcolor = fgclr;
    msg.bgcolor = bgclr;
    strcpy( msg.text, txt, MSGCMD_TEXTSIZE );


    cds.dwData = 0;
    cds.cbData = sizeof( msg );
    cds.lpData = &msg;

   return ::SendMessage( m_hWndRecv, WM_COPYDATA,
                         (WPARAM) m_hWnd, (LPARAM) &cds ); }

Following is part of the code that handles the message in the receiving dialog.  There are three commands it can receive : display a text line, terminate, and a timing command.

BOOL CIpcRecvDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pData) {
    // if size doesn't match we don't know what this is

    if( pData->cbData == sizeof( MsgCmd ) )
        MsgCmd msg;
        memcpy( &msg, pData->lpData, sizeof( MsgCmd ) );

        // process the message

        return TRUE;
    return CDialog::OnCopyData(pWnd, pData); }

While WM_COPYDATA can be used to used for IPC, as illustrated by this demo, it is not without some caveats.  Among them are : a handle to the receiving window must be acquired and SendMessage must be used which means that the message must be processed by the receiver before the sending process continues.

There is a system menu option that allows one to test the performance of the WM_COPYDATA message transmission.  On my 300 MHz PII 10000 messages were sent in 0.377 seconds on NT v4.0 SP3.  What is more interesting is that on 98 the time was 2.104 seconds for 10000 messages.  I guess this illustrates that NT has a much faster context switch than 98.  I would judge the performance to be adequate for many applications but not necessarily all, especially on 98.

Points of note concerning this demo are : starting up the app causes it to start another copy of itself.  The first app becomes the sender and the second becomes the receiver.  Mutexes are used to determine which app is which and to prevent additional instances.  Exiting the sender causes it to pass a terminate message to the receiver so both will shut down at the ‘same’ time.  Since I don’t much like the registry, I have used my own little profile handling class (CProfile) to save the positions of the windows when they terminate and to restore them when they initialize.  The profile (IpcDemo.ini) is stored in the system’s %WINDIR%.  Feel free to delete it if you want.  A batch file called _makeme.bat is included so that downloaders can quickly build the app from the command line.  The batch file requires an ‘r’ argument for a release build and a ‘d’ argument for a debug build.  The executable will be placed in objrel or objdbg respectively.  I have placed the following comment in the code modules ‘//@tabs=4’ in the naive hope that intelligent code editors will recognize this and adjust their tabstops accordingly.  I hope to develop one (using code from this site, of course 🙂 that will.

In the spirit of code reuse, Norm Alomond’s CLabel class is used to display text in the receiver dialog, James R. Twine’s CColorPickerCB class was used to select colors in the sender dialog, and my own CElapsed high resolution timer class was used for timing.  All of these are described in articles on this site.

This code was developed using Visual C++ v5.0 on NT v4.0 SP3. and it has been tested on 98.

Download demo project – 24 KB

More by Author

Previous article
Next article

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read