Figure 1 CDevice

Device.h

  // Device.h : Declaration of the CDevice

#ifndef __DEVICE_H_
#define __DEVICE_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CDevice

class ATL_NO_VTABLE CDevice : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CDevice, &CLSID_Device>,
    public IDeviceStatus,
    public IDeviceControl,
    public IStdMarshalInfo
{
public:
    CDevice()
    {
    }

DECLARE_REGISTRY_RESOURCEID(IDR_DEVICE)
DECLARE_NOT_AGGREGATABLE(CDevice)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CDevice)
    COM_INTERFACE_ENTRY(IDeviceStatus)
    COM_INTERFACE_ENTRY(IDeviceControl)
    COM_INTERFACE_ENTRY(IStdMarshalInfo)
END_COM_MAP()

// IDeviceStatus
public:
    STDMETHOD(GetCurrentPosition)(/*[out]*/ DWORD* pdwPos);
    STDMETHOD(GetLastPosition)(/*[out]*/ DWORD* pdwPos);

// IDeviceControl
    STDMETHOD(SetPosition)(/*[in]*/ DWORD dwPos, /*[out]*/ DWORD*    
        pdwOldPos);

// IStdMarshalInfo
    STDMETHOD(GetClassForHandler)(ULONG dwDestContext, 
    VOID * pvDestContext, GUID * pClsid);
};

#endif //__DEVICE_H_

Device.cpp

  // Device.cpp : Implementation of CDevice
#include "stdafx.h"
#include "DeviceServer.h"
#include "Device.h"

const CLSID CLSID_DeviceHandler =
    {0xE5B12275,0xDBF9,0x4F4D,{0xB3,0xA0,0x3E,0xFA,0xD4,0xA3,0x9D,0x1D}};

/////////////////////////////////////////////////////////////////////////////
// CDevice

STDMETHODIMP CDevice::GetLastPosition(DWORD *pdwPos)
{
    *pdwPos = GetCurrentProcessId ();
    return S_OK;
}

STDMETHODIMP CDevice::GetCurrentPosition(DWORD *pdwPos)
{
    *pdwPos = GetCurrentProcessId ();
    return S_OK;
}

STDMETHODIMP CDevice::SetPosition(DWORD dwPos, DWORD* pdwOldPos)
{
    *pdwOldPos = GetCurrentProcessId ();
    return S_OK;
}

STDMETHODIMP CDevice::GetClassForHandler(ULONG dwDestContext,
    VOID * pvDestContext, GUID * pClsid)
{
    *pClsid = CLSID_DeviceHandler;
    return S_OK;
}

Figure 3 CDeviceHandler

DeviceHandler.h

  // DeviceHandler.h : Declaration of the CDeviceHandler

#ifndef __DEVICEHANDLER_H_
#define __DEVICEHANDLER_H_

#include "resource.h"       // main symbols

/////////////////////////////////////////////////////////////////////////////
// CDeviceHandler

class ATL_NO_VTABLE CDeviceHandler : 
    public CComObjectRootEx<CComMultiThreadModel>,
    public CComCoClass<CDeviceHandler, &CLSID_DeviceHandler>,
    public IDeviceStatus
{
public:
    CDeviceHandler()
    {
    }

HRESULT FinalConstruct ();

DECLARE_GET_CONTROLLING_UNKNOWN()
DECLARE_REGISTRY_RESOURCEID(IDR_DEVICEHANDLER)
DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CDeviceHandler)
    COM_INTERFACE_ENTRY(IDeviceStatus)
    COM_INTERFACE_ENTRY_AGGREGATE_BLIND (m_spUnkInner.p)
END_COM_MAP()

// IDeviceStatus
public:
    STDMETHOD(GetCurrentPosition)(/*[out]*/ DWORD* pdwPos);
    STDMETHOD(GetLastPosition)(/*[out]*/ DWORD* pdwPos);

    CComPtr<IUnknown> m_spUnkInner;
};

#endif //__DEVICEHANDLER_H_

DeviceHandler.cpp

  // DeviceHandler.cpp : Implementation of CDeviceHandler
#include "stdafx.h"
#include "DeviceHandlerServer.h"
#include "DeviceHandler.h"

/////////////////////////////////////////////////////////////////////////////
// CDeviceHandler

STDMETHODIMP CDeviceHandler::GetLastPosition(DWORD *pdwPos)
{
    //
    // Handle this call locally.
    //
    *pdwPos = GetCurrentProcessId ();
    return S_OK;
}

STDMETHODIMP CDeviceHandler::GetCurrentPosition(DWORD *pdwPos)
{
    //
    // Delegate this call to the device object.
    //
    HRESULT hr;
    IDeviceStatus* pDeviceStatus;
    hr = m_spUnkInner->QueryInterface (IID_IDeviceStatus, (void**) 
        &pDeviceStatus);

    if (FAILED (hr))
        return hr;

    hr = pDeviceStatus->GetCurrentPosition (pdwPos);
    pDeviceStatus->Release ();
    return hr;
}

HRESULT CDeviceHandler::FinalConstruct ()
{
    //
    // Aggregate COM's proxy manager.
    //
    return CoGetStdMarshalEx (GetControllingUnknown (), SMEXF_HANDLER,
        &m_spUnkInner);
}

DeviceHandler.rgs

  HKCR
{
    DeviceHandlerServer.DeviceHandler.1 = s 'DeviceHandler Class'
    {
        CLSID = s '{E5B12275-DBF9-4F4D-B3A0-3EFAD4A39D1D}'
    }
    DeviceHandlerServer.DeviceHandler = s 'DeviceHandler Class'
    {
        CLSID = s '{E5B12275-DBF9-4F4D-B3A0-3EFAD4A39D1D}'
        CurVer = s 'DeviceHandlerServer.DeviceHandler.1'
    }
    NoRemove CLSID
    {
        ForceRemove {E5B12275-DBF9-4F4D-B3A0-3EFAD4A39D1D} =  
            s'DeviceHandler Class'
        {
            ProgID = s 'DeviceHandlerServer.DeviceHandler.1'
            VersionIndependentProgID =  
                s'DeviceHandlerServer.DeviceHandler'
            InprocHandler32 = s '%MODULE%'
            {
                val ThreadingModel = s 'Both'
            }
            'TypeLib' = s '{2DC1B111-F396-44CB-88C7-4FE8DF8FC331}'
        }
    }
}

Figure 5 CDeviceClientDlg

DeviceClientDlg.h

  // DeviceClientDlg.h : header file
//

#if !defined(AFX_DEVICECLIENTDLG_H__BE6BB82F_1D6E_4D76_948C_C829534E00F3__INCLUDED_)
#define AFX_DEVICECLIENTDLG_H__BE6BB82F_1D6E_4D76_948C_C829534E00F3__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

/////////////////////////////////////////////////////////////////////////////
// CDeviceClientDlg dialog

class CDeviceClientDlg : public CDialog
{
// Construction
public:
    CDeviceClientDlg(CWnd* pParent = NULL);    // standard constructor

// Dialog Data
    //{{AFX_DATA(CDeviceClientDlg)
    enum { IDD = IDD_DEVICECLIENT_DIALOG };
        // NOTE: the ClassWizard will add data members here
    //}}AFX_DATA

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CDeviceClientDlg)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

// Implementation
protected:
    void ReportError (HRESULT hr);
    IDeviceStatus* m_pDeviceStatus;
    IDeviceControl* m_pDeviceControl;
    HICON m_hIcon;

    // Generated message map functions
    //{{AFX_MSG(CDeviceClientDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void OnGetLastPosition();
    afx_msg void OnGetCurrentPosition();
    afx_msg void OnSetPosition();
    afx_msg void OnDestroy();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately 
// before the previous line.

#endif // !defined(AFX_DEVICECLIENTDLG_H__BE6BB82F_1D6E_4D76_948C_C829534E00F3__INCLUDED_)

DeviceClientDlg.cpp

  // DeviceClientDlg.cpp : implementation file
//

#include "stdafx.h"
#include "DeviceServer.h"
#include "DeviceServer_i.c"
#include "DeviceClient.h"
#include "DeviceClientDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDeviceClientDlg dialog

CDeviceClientDlg::CDeviceClientDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CDeviceClientDlg::IDD, pParent)
{
    //{{AFX_DATA_INIT(CDeviceClientDlg)
        // 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_pDeviceStatus = NULL;
    m_pDeviceControl = NULL;
}

void CDeviceClientDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CDeviceClientDlg)
        // NOTE: the ClassWizard will add DDX and DDV calls here
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDeviceClientDlg, CDialog)
    //{{AFX_MSG_MAP(CDeviceClientDlg)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_GETLASTPOS, OnGetLastPosition)
    ON_BN_CLICKED(IDC_GETCURPOS, OnGetCurrentPosition)
    ON_BN_CLICKED(IDC_SETPOS, OnSetPosition)
    ON_WM_DESTROY()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDeviceClientDlg message handlers

BOOL CDeviceClientDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    SetIcon(m_hIcon, TRUE);         // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon
    
    //
    // Display the process ID in the dialog.
    //
    DWORD dwPID = ::GetCurrentProcessId ();

    CString string;
    string.Format (_T ("Process ID = %lu"), dwPID);
    SetDlgItemText (IDC_PID, string);

    //
    // Create a device object and cache an IDeviceStatus interface pointer.
    //
    HRESULT hr = ::CoCreateInstance (CLSID_Device, NULL, CLSCTX_SERVER,
        IID_IDeviceStatus, (void**) &m_pDeviceStatus);    

    if (FAILED (hr)) {
        ReportError (hr);
        GetDlgItem (IDC_GETLASTPOS)->EnableWindow (FALSE);
        GetDlgItem (IDC_GETCURPOS)->EnableWindow (FALSE);
        GetDlgItem (IDC_SETPOS)->EnableWindow (FALSE);
        return TRUE;
    }

    //
    // Cache an IDeviceControl interface pointer, too.
    //
    hr = m_pDeviceStatus->QueryInterface (IID_IDeviceControl,
        (void**) &m_pDeviceControl);

    if (FAILED (hr)) {
        ReportError (hr);
        GetDlgItem (IDC_GETLASTPOS)->EnableWindow (FALSE);
        GetDlgItem (IDC_GETCURPOS)->EnableWindow (FALSE);
        GetDlgItem (IDC_SETPOS)->EnableWindow (FALSE);
        m_pDeviceStatus->Release ();
        return TRUE;
    }

    return TRUE;
}

void CDeviceClientDlg::OnPaint() 
{
    if (IsIconic())
    {
        CPaintDC dc(this);

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

        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;

        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialog::OnPaint();
    }
}

HCURSOR CDeviceClientDlg::OnQueryDragIcon()
{
    return (HCURSOR) m_hIcon;
}

void CDeviceClientDlg::OnDestroy() 
{
    //
    // Release interface pointers before shutting down.
    //
    if (m_pDeviceStatus != NULL) {
        m_pDeviceStatus->Release ();
        m_pDeviceStatus = NULL;
    }

    if (m_pDeviceControl != NULL) {
        m_pDeviceControl->Release ();
        m_pDeviceControl = NULL;
    }

    CDialog::OnDestroy();
}

void CDeviceClientDlg::OnGetLastPosition() 
{
    ASSERT (m_pDeviceStatus != NULL);    

    //
    // Call IDeviceStatus::GetLastPosition.
    //
    DWORD dwPos;
    HRESULT hr = m_pDeviceStatus->GetLastPosition (&dwPos);

    if (FAILED (hr)) {
        ReportError (hr);
        return;
    }

    //
    // Display the last device position (actually the process ID).
    //
    CString string;
    string.Format (_T ("Process ID = %lu"), dwPos);
    MessageBox (string);
}

void CDeviceClientDlg::OnGetCurrentPosition() 
{
    ASSERT (m_pDeviceStatus != NULL);    

    //
    // Call IDeviceStatus::GetCurrentPosition.
    //
    DWORD dwPos;
    HRESULT hr = m_pDeviceStatus->GetCurrentPosition (&dwPos);

    if (FAILED (hr)) {
        ReportError (hr);
        return;
    }

    //
    // Display the current device position (actually the process ID).
    //
    CString string;
    string.Format (_T ("Process ID = %lu"), dwPos);
    MessageBox (string);
}

void CDeviceClientDlg::OnSetPosition() 
{
    ASSERT (m_pDeviceControl != NULL);    

    //
    // Call IDeviceControl::SetPosition.
    //
    DWORD dwOldPos;
    HRESULT hr = m_pDeviceControl->SetPosition (1234, &dwOldPos);

    if (FAILED (hr)) {
        ReportError (hr);
        return;
    }

    //
    // Display the old device position (actually the process ID).
    //
    CString string;
    string.Format (_T ("Process ID = %lu"), dwOldPos);
    MessageBox (string);
}

/////////////////////////////////////////////////////////////////////////////
// Helper function for error reporting

void CDeviceClientDlg::ReportError(HRESULT hr)
{
    LPTSTR pszMessage;

    if (::FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0, (LPTSTR) &pszMessage,
        0, NULL)) {
        MessageBox (pszMessage, _T ("Error"));
        ::LocalFree (pszMessage);
    }
}