Figure 1   Sieve

Sieve.h: Declaration of the CSieve


 #ifndef __SIEVE_H_
 #define __SIEVE_H_
 
 #include "resource.h"       // main symbols
 
 typedef struct tagTHREADPARMS {
     IStream** ppStream;
     int nMax;
 } THREADPARMS;
 
 /////////////////////////////////////////////////////////////////////////////
 // CSieve
 class ATL_NO_VTABLE CSieve : 
     public CComObjectRootEx<CComSingleThreadModel>,
     public CComCoClass<CSieve, &CLSID_Sieve>,
     public ISieve
 {
 public:
     CSieve()
     {
     }
 
 DECLARE_REGISTRY_RESOURCEID(IDR_SIEVE)
 DECLARE_NOT_AGGREGATABLE(CSieve)
 
 DECLARE_PROTECT_FINAL_CONSTRUCT()
 
 BEGIN_COM_MAP(CSieve)
     COM_INTERFACE_ENTRY(ISieve)
 END_COM_MAP()
 
 // ISieve
 public:
     STDMETHOD(CountPrimes)(/*[in]*/ int nMax, /*[in]*/ ISieveCallback* 
                            pCallback);
 };
 
 #endif //__SIEVE_H_
Sieve.cpp: Implementation of CSieve

 #include "stdafx.h"
 #include "AsyncServer.h"
 #include "Sieve.h"
 
 DWORD WINAPI ThreadFunc (LPVOID pThreadParms);
 
 /////////////////////////////////////////////////////////////////////////////
 // CSieve
 
 STDMETHODIMP CSieve::CountPrimes(int nMax, ISieveCallback *pCallback)
 {
     //
     // Marshal the interface pointer into a stream object.
     //
     IStream** ppStream = new IStream*;
     HRESULT hr = CoMarshalInterThreadInterfaceInStream (IID_ISieveCallback,
         pCallback, ppStream);
 
     if (FAILED (hr))
         return hr;
 
     //
     // Start a new thread and pass it nMax and ppStream.
     //
     THREADPARMS* ptp = new THREADPARMS;
     ptp->ppStream = ppStream;
     ptp->nMax= nMax;
 
     DWORD dwThreadID;
     HANDLE hThread = CreateThread (NULL, 0, ThreadFunc, ptp, 0, &dwThreadID);
 
     if (hThread == NULL) {
         (*ppStream)->Release ();
         return E_OUTOFMEMORY;
     }
 
     CloseHandle (hThread); // Don't need this anymore
 
     //
     // Return now so the caller won't have to wait.
     //
     return S_OK;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Thread function (global scope)
 
 DWORD WINAPI ThreadFunc (LPVOID pThreadParms)
 {
     CoInitialize (NULL);
 
     //
     // Make a local copy of the thread parameters passed in THREADPARMS.
     //
     THREADPARMS* ptp = (THREADPARMS*) pThreadParms;
     IStream** ppStream = ptp->ppStream;
     int nMax = ptp->nMax;
     delete ptp;
 
     //
     // Unmarshal the ISieveCallback interface pointer.
     //
     ISieveCallback* pCallback;
     CoGetInterfaceAndReleaseStream (*ppStream, IID_ISieveCallback,
         (void**) &pCallback);
     delete ppStream;
 
     //
     // Count prime numbers between 2 and nMax.
     //
     int nCount = 0;
     PBYTE pBuffer = new BYTE[nMax + 1];
 
     if (pBuffer != NULL) {
         ::FillMemory (pBuffer, nMax + 1, 1);
 
         int nLimit = 2;
         while (nLimit * nLimit < nMax)
             nLimit++;
 
         for (int i=2; i<=nLimit; i++) {
             if (pBuffer[i]) {
                 for (int k=i + i; k<=nMax; k+=i)
                     pBuffer[k] = 0;
             }
         }
 
         for (i=2; i<=nMax; i++)
             if (pBuffer[i])
                 nCount++;
 
         delete[] pBuffer;
     }
 
     //
     // Pass the result back to the caller and end this thread.
     //
     pCallback->CountPrimes_Result (nCount);
     pCallback->Release ();
     CoUninitialize ();
     return 0;
 }

Figure 3   AsyncClientDlg

AsyncClientDlg.h: header file


 #if !defined(AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_)
 #define AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_
 
 #if _MSC_VER > 1000
 #pragma once
 #endif // _MSC_VER > 1000
 
 /////////////////////////////////////////////////////////////////////////////
 // CAsyncClientDlg dialog
 
 class CAsyncClientDlg : public CDialog
 {
 // Construction
 public:
     void DisplayResult (int nCount);
     CAsyncClientDlg(CWnd* pParent = NULL);    // standard constructor
 
 // Dialog Data
     //{{AFX_DATA(CAsyncClientDlg)
     enum { IDD = IDD_ASYNCCLIENT_DIALOG };
         // NOTE: the ClassWizard will add data members here
     //}}AFX_DATA
 
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CAsyncClientDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL
 
 // Implementation
 protected:
     ISieve* m_pSieve;
     HICON m_hIcon;
 
     // Generated message map functions
     //{{AFX_MSG(CAsyncClientDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     afx_msg void OnGo();
     afx_msg void OnDestroy();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
 
     BEGIN_INTERFACE_PART (Callback, ISieveCallback)
         virtual HRESULT __stdcall CountPrimes_Result (int nCount);
     END_INTERFACE_PART (Callback)
     DECLARE_INTERFACE_MAP()
 };
 
 //{{AFX_INSERT_LOCATION}}
 // Microsoft Visual C++ will insert additional declarations immediately before 
 // the previous line.
 
 #endif
 //!defined(AFX_ASYNCCLIENTDLG_H__749DBF78_016B_11D3_8E53_006008A82731__INCLUDED_)
AsyncClientDlg.cpp: implementation file

 #include "stdafx.h"
 #include "AsyncServer.h"
 #include "AsyncServer_i.c"
 #include "AsyncClient.h"
 #include "AsyncClientDlg.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 /////////////////////////////////////////////////////////////////////////////
 // CAsyncClientDlg dialog
 
 CAsyncClientDlg::CAsyncClientDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CAsyncClientDlg::IDD, pParent)
 {
     //{{AFX_DATA_INIT(CAsyncClientDlg)
         // NOTE: the ClassWizard will add member initialization here
     //}}AFX_DATA_INIT
     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
     m_pSieve = NULL;
 }
 
 void CAsyncClientDlg::DoDataExchange(CDataExchange* pDX)
 {
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAsyncClientDlg)
         // NOTE: the ClassWizard will add DDX and DDV calls here
     //}}AFX_DATA_MAP
 }
 
 BEGIN_MESSAGE_MAP(CAsyncClientDlg, CDialog)
     //{{AFX_MSG_MAP(CAsyncClientDlg)
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     ON_BN_CLICKED(IDC_GO, OnGo)
     ON_WM_DESTROY()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 BEGIN_INTERFACE_MAP(CAsyncClientDlg, CDialog)
     INTERFACE_PART(CAsyncClientDlg, IID_ISieveCallback, Callback)
 END_INTERFACE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CAsyncClientDlg message handlers
 
 BOOL CAsyncClientDlg::OnInitDialog()
 {
     CDialog::OnInitDialog();
 
     SetIcon(m_hIcon, TRUE);         // Set big icon
     SetIcon(m_hIcon, FALSE);        // Set small icon
     
     //
     // Create a Sieve object instance.
     //
     HRESULT hr = ::CoCreateInstance (CLSID_Sieve, NULL, CLSCTX_SERVER,
         IID_ISieve, (void**) &m_pSieve);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CoCreateInstance failed"), _T ("Error"),
             MB_ICONSTOP);
         GetDlgItem (IDC_GO)->EnableWindow (FALSE);
     }
     return TRUE;  // return TRUE  unless you set the focus to a control
 }
 
 void CAsyncClientDlg::OnPaint() 
 {
     if (IsIconic())
     {
         CPaintDC dc(this); // device context for painting
 
         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
 
         // Center icon in client rectangle
         int cxIcon = GetSystemMetrics(SM_CXICON);
         int cyIcon = GetSystemMetrics(SM_CYICON);
         CRect rect;
         GetClientRect(&rect);
         int x = (rect.Width() - cxIcon + 1) / 2;
         int y = (rect.Height() - cyIcon + 1) / 2;
 
         // Draw the icon
         dc.DrawIcon(x, y, m_hIcon);
     }
     else
     {
         CDialog::OnPaint();
     }
 }
 
 HCURSOR CAsyncClientDlg::OnQueryDragIcon()
 {
     return (HCURSOR) m_hIcon;
 }
 
 void CAsyncClientDlg::OnDestroy() 
 {
     //
     // Release the ISieve interface pointer.
     //
     CDialog::OnDestroy();
     if (m_pSieve != NULL) {
         m_pSieve->Release ();
         m_pSieve = NULL;
     }    
 
     //
     // Forcibly break extant ISieveCallback connections (if any).
     //
     ISieveCallback* pSieveCallback;
     HRESULT hr = m_xCallback.QueryInterface (IID_ISieveCallback,
         (void**) &pSieveCallback);
     ASSERT (SUCCEEDED (hr));
     m_xCallback.Release (); // Undo the AddRef performed by QueryInterface
     CoDisconnectObject (pSieveCallback, NULL);
 }
 
 void CAsyncClientDlg::OnGo() 
 {
     SetDlgItemText (IDC_RESULT, _T (""));
     GetDlgItem (IDC_GO)->EnableWindow (FALSE);
 
     //
     // Get the address of our callback object's ISieveCallback interface.
     //
     ISieveCallback* pSieveCallback;
     HRESULT hr = m_xCallback.QueryInterface (IID_ISieveCallback,
         (void**) &pSieveCallback);
     ASSERT (SUCCEEDED (hr));
     m_xCallback.Release (); // Undo the AddRef performed by QueryInterface
 
     //
     // Call the Sieve object and provide an ISieveCallback interface pointer.
     //
     m_pSieve->CountPrimes (10000000, pSieveCallback);
 }
 
 void CAsyncClientDlg::DisplayResult(int nCount)
 {
     SetDlgItemInt (IDC_RESULT, nCount);
     GetDlgItem (IDC_GO)->EnableWindow (TRUE);
 }
 
 ///////////////////////////////////////////////////////////////////////
 // XCallback methods
 
 ULONG __stdcall CAsyncClientDlg::XCallback::AddRef ()
 {
     METHOD_PROLOGUE (CAsyncClientDlg, Callback)
     return pThis->ExternalAddRef ();
 }
 
 ULONG __stdcall CAsyncClientDlg::XCallback::Release ()
 {
     METHOD_PROLOGUE (CAsyncClientDlg, Callback)
     return pThis->ExternalRelease ();
 }
 
 HRESULT __stdcall CAsyncClientDlg::XCallback::QueryInterface (REFIID riid,
     void** ppv)
 {
     METHOD_PROLOGUE (CAsyncClientDlg, Callback)
     return pThis->ExternalQueryInterface (&riid, ppv);
 }
 
 HRESULT __stdcall CAsyncClientDlg::XCallback::CountPrimes_Result (int nCount)
 {
     METHOD_PROLOGUE (CAsyncClientDlg, Callback)
     pThis->DisplayResult (nCount);
     return S_OK;
 }

Figure 5   StringClient Excerpts

StringClientDlg.h: header file


 #if !defined(AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_)
 #define AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_
 
 #if _MSC_VER > 1000
 #pragma once
 #endif // _MSC_VER > 1000
 
 /////////////////////////////////////////////////////////////////////////////
 // CStringClientDlg dialog
 
 class CStringClientDlg : public CDialog
 {
 // Construction
 public:
     static HWND m_hTargetWnd;
     static BOOL CALLBACK EnumWindowsProc (HWND hWnd, LPARAM lParam);
     CStringClientDlg(CWnd* pParent = NULL);    // standard constructor
 
 // Dialog Data
     //{{AFX_DATA(CStringClientDlg)
     enum { IDD = IDD_STRINGCLIENT_DIALOG };
         // NOTE: the ClassWizard will add data members here
     //}}AFX_DATA
 
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CStringClientDlg)
     protected:
     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
     //}}AFX_VIRTUAL
 
 // Implementation
 protected:
     HWND PidToHwnd (DWORD dwProcessID);
     IStringCache* m_pStringCache;
     HICON m_hIcon;
 
     // Generated message map functions
     //{{AFX_MSG(CStringClientDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     afx_msg void OnOptionsGetString();
     afx_msg void OnOptionsSetString();
     afx_msg void OnOptionsNewProcess();
     afx_msg void OnOptionsExit();
     afx_msg void OnDestroy();
     afx_msg BOOL OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct);
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
 };
 
 //{{AFX_INSERT_LOCATION}}
 // Microsoft Visual C++ will insert additional declarations immediately before 
 // the previous line.
 
 #endif // !defined(AFX_STRINGCLIENTDLG_H__59D16A78_0134_11D3_8E53_006008A82731__INCLUDED_)
StringClientDlg.cpp: implementation file

 #include "stdafx.h"
 #include "StringServer.h"
 #include "StringServer_i.c"
 #include "StringClient.h"
 #include "StringClientDlg.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 HWND CStringClientDlg::m_hTargetWnd = NULL;
 
 /////////////////////////////////////////////////////////////////////////////
 // CStringClientDlg dialog
 
 CStringClientDlg::CStringClientDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CStringClientDlg::IDD, pParent)
 {
     //{{AFX_DATA_INIT(CStringClientDlg)
         // NOTE: the ClassWizard will add member initialization here
     //}}AFX_DATA_INIT
     // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
     m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
     m_pStringCache = NULL;
 }
 
 void CStringClientDlg::DoDataExchange(CDataExchange* pDX)
 {
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CStringClientDlg)
         // NOTE: the ClassWizard will add DDX and DDV calls here
     //}}AFX_DATA_MAP
 }
 
 BEGIN_MESSAGE_MAP(CStringClientDlg, CDialog)
     //{{AFX_MSG_MAP(CStringClientDlg)
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     ON_COMMAND(ID_OPTIONS_GET_STRING, OnOptionsGetString)
     ON_COMMAND(ID_OPTIONS_SET_STRING, OnOptionsSetString)
     ON_COMMAND(ID_OPTIONS_NEW_PROCESS, OnOptionsNewProcess)
     ON_COMMAND(ID_OPTIONS_EXIT, OnOptionsExit)
     ON_WM_DESTROY()
     ON_WM_COPYDATA()
     //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
 // CStringClientDlg message handlers
 
 BOOL CStringClientDlg::OnInitDialog()
 {
     CDialog::OnInitDialog();
 
     SetIcon(m_hIcon, TRUE);         // Set big icon
     SetIcon(m_hIcon, FALSE);        // Set small icon
     
     //
     // Disable the menu items used to pass and test interface pointers.
     //
     GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_GRAYED);
     GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_GRAYED);
     GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_GRAYED);
 
     //
     // Instantiate a string cache object if this instance of StringClient
     // wasn't launched by another instance of StringClient.
     //
     CStringClientApp* pMyApp = (CStringClientApp*) AfxGetApp ();
 
     if (pMyApp->CreateStringCache ()) {
         HRESULT hr = ::CoCreateInstance (CLSID_StringCache, NULL,
             CLSCTX_SERVER, IID_IStringCache, (void**) &m_pStringCache);
 
         if (FAILED (hr))
             MessageBox (_T ("CoCreateInstance failed"), _T ("Error"),
                 MB_ICONSTOP);
         else { // Enable the menu items
             GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_ENABLED);
             GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_ENABLED);
             GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_ENABLED);
         }
     }
     return TRUE;  // return TRUE  unless you set the focus to a control
 }
 
 void CStringClientDlg::OnPaint() 
 {
     if (IsIconic())
     {
         CPaintDC dc(this); // device context for painting
 
         SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
 
         // Center icon in client rectangle
         int cxIcon = GetSystemMetrics(SM_CXICON);
         int cyIcon = GetSystemMetrics(SM_CYICON);
         CRect rect;
         GetClientRect(&rect);
         int x = (rect.Width() - cxIcon + 1) / 2;
         int y = (rect.Height() - cyIcon + 1) / 2;
 
         // Draw the icon
         dc.DrawIcon(x, y, m_hIcon);
     }
     else
     {
         CDialog::OnPaint();
     }
 }
 
 HCURSOR CStringClientDlg::OnQueryDragIcon()
 {
     return (HCURSOR) m_hIcon;
 }
 
 void CStringClientDlg::OnOptionsGetString() 
 {
     ASSERT (m_pStringCache != NULL);    
 
     USES_CONVERSION;
     wchar_t* pBuffer = NULL;
     HRESULT hr = m_pStringCache->GetString (&pBuffer);
     if (SUCCEEDED (hr)) {
         LPTSTR pString = W2T (pBuffer);
         SetDlgItemText (IDC_STRING, pString);
         ::CoTaskMemFree (pBuffer);
     }
 }
 
 void CStringClientDlg::OnOptionsSetString() 
 {
     ASSERT (m_pStringCache != NULL);    
 
     USES_CONVERSION;
     CString string;
     GetDlgItemText (IDC_STRING, string);
     m_pStringCache->SetString (T2W (string));
 }
 
 void CStringClientDlg::OnOptionsNewProcess() 
 {
     ASSERT (m_pStringCache != NULL);    
 
     //
     // Allocate memory to hold a marshaled IStringCache interface pointer.
     //
     ULONG ulSize;
     HRESULT hr = ::CoGetMarshalSizeMax (&ulSize, IID_IStringCache,
         m_pStringCache, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CoGetMarshalSizeMax failed"), _T ("Error"), 
             MB_ICONEXCLAMATION);
         return;
     }
 
     HGLOBAL hGlobal = ::GlobalAlloc (GMEM_MOVEABLE, (DWORD) ulSize);
 
     if (hGlobal == NULL) {
         MessageBox (_T ("Insufficient memory to marshal interface"),
             _T ("Error"),  MB_ICONEXCLAMATION);
         return;
     }
 
     //
     // Marshal the interface pointer into the memory block.
     //
     IStream* pStream;
     hr = ::CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CreateStreamOnHGlobal failed"), _T ("Error"), 
             MB_ICONEXCLAMATION);
         ::GlobalFree (hGlobal);
         return;
     }
 
     hr = ::CoMarshalInterface (pStream, IID_IStringCache, m_pStringCache,
         MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CoMarshalInterface failed"), _T ("Error"), 
             MB_ICONEXCLAMATION);
         pStream->Release ();
         return;
     }
 
     //
     // Launch another instance of StringClient and wait for it to create a
     // window.
     //
     TCHAR szCmd[MAX_PATH + 16];
     ::GetModuleFileName (AfxGetInstanceHandle (), szCmd,
         sizeof (szCmd) / sizeof (TCHAR));
     ::lstrcat (szCmd, _T (" /NoCreate"));
 
     STARTUPINFO si;
     ::ZeroMemory (&si, sizeof (STARTUPINFO));
     si.cb = sizeof (STARTUPINFO);
     PROCESS_INFORMATION pi;
 
     if (!::CreateProcess (NULL, szCmd, NULL, NULL, FALSE,
         NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi)) {
         MessageBox (_T ("CreateProcess failed"), _T ("Error"), 
             MB_ICONEXCLAMATION);
         pStream->Release ();
         return;
     }
 
     ::WaitForInputIdle (pi.hProcess, INFINITE);
     ::CloseHandle (pi.hThread);
     ::CloseHandle (pi.hProcess);
 
     //
     // Convert the process ID returned by CreateProcess into a window handle
     // and pass the marshaled interface pointer to the new process.
     //
     HWND hWnd = PidToHwnd (pi.dwProcessId);
     ASSERT (hWnd != NULL);
 
     COPYDATASTRUCT cds = { 0, (DWORD) ulSize, ::GlobalLock (hGlobal) };
     ::SendMessage (hWnd, WM_COPYDATA, (WPARAM) m_hWnd, (LPARAM) &cds);
     ::GlobalUnlock (hGlobal);
 
     //
     // Clean up by releasing the stream and freeing the memory block.
     //
     pStream->Release ();
 }
 
 void CStringClientDlg::OnOptionsExit() 
 {
     PostMessage (WM_CLOSE, 0, 0);    
 }
 
 void CStringClientDlg::OnDestroy() 
 {
     CDialog::OnDestroy();
     if (m_pStringCache != NULL) {    
         m_pStringCache->Release ();
         m_pStringCache = NULL;    
     }
 }
 
 HWND CStringClientDlg::PidToHwnd(DWORD dwProcessID)
 {
     m_hTargetWnd = NULL;
     ::EnumWindows (EnumWindowsProc, (LPARAM) dwProcessID);
     return m_hTargetWnd;
 }
 
 BOOL CALLBACK CStringClientDlg::EnumWindowsProc(HWND hWnd, LPARAM lParam)
 {
     DWORD dwProcessID;
     ::GetWindowThreadProcessId (hWnd, &dwProcessID);
     if (dwProcessID == (DWORD) lParam) {
         m_hTargetWnd = hWnd;
         return FALSE;
     }
     return TRUE;
 }
 
 BOOL CStringClientDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) 
 {
     BOOL bResult = CDialog::OnCopyData(pWnd, pCopyDataStruct);
 
     //
     // Make a local copy of the data encapsulated in the message.
     //
     HGLOBAL hGlobal = ::GlobalAlloc (GMEM_MOVEABLE, pCopyDataStruct->cbData);
 
     if (hGlobal == NULL) {
         MessageBox (_T ("Insufficient memory to unmarshal interface"),
             _T ("Error"), MB_ICONEXCLAMATION);
         return bResult;
     }
 
     PBYTE pData = (PBYTE) ::GlobalLock (hGlobal);
     ::CopyMemory (pData, pCopyDataStruct->lpData, pCopyDataStruct->cbData);
     ::GlobalUnlock (hGlobal);
 
     //
     // Unmarshal the interface pointer.
     //
     IStream* pStream;
     HRESULT hr = CreateStreamOnHGlobal (hGlobal, TRUE, &pStream);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CreateStreamOnHGlobal failed"), _T ("Error"), 
             MB_ICONEXCLAMATION);
         ::GlobalFree (hGlobal);
         return bResult;
     }
 
     IStringCache* pStringCache;
     hr = CoUnmarshalInterface (pStream, IID_IStringCache,
         (void**) &pStringCache);
 
     if (FAILED (hr)) {
         MessageBox (_T ("CoUnmarshalInterface failed"), _T ("Error"),
             MB_ICONEXCLAMATION);
         pStream->Release ();
         return bResult;
     }
 
     //
     // Cache the marshaled interface pointer and enable menu items.
     //
     m_pStringCache = pStringCache;
     GetMenu ()->EnableMenuItem (ID_OPTIONS_GET_STRING, MF_ENABLED);
     GetMenu ()->EnableMenuItem (ID_OPTIONS_SET_STRING, MF_ENABLED);
     GetMenu ()->EnableMenuItem (ID_OPTIONS_NEW_PROCESS, MF_ENABLED);
 
     //
     // Clean up and return.
     //
     pStream->Release ();
     return bResult;
 }