IScribbleServer Interface Methods | |
RegisterClient | At startup, the client creates an instance of the server object and calls this method. The client passes a pointer to its IUnknown interface so that it can receive callbacks from the server. The server returns a cookie that the client uses to identify itself in subsequent calls to the server. |
UnregisterClient | Before being destroyed, the client calls this method to remove itself from the server's notification list. |
AddStroke | When a stroke is drawn, the client (who receives the stroke) calls this method to pass the data—stored as a SafeArray—to the server, which then broadcasts the stroke to each of the other clients. |
RemoveAllStrokes | When the Erase button is clicked, the client calls this method to tell the server to clear the whiteboard. |
IScribbleClient Interface Methods | |
OnNewStroke | Called by the server when another client adds a stroke to the whiteboard. |
OnRemoveAll | Called by the server when any client submits a RemoveAllStrokes request. |
Figure 4 Scribble Client
#import "../ScribSvr/ScribSvr.tlb" no_namespace raw_interfaces_only
class ScribbleCtrl : public CActiveXDocControl
{
friend class CScribServDoc;
public:
ScribbleCtrl();
virtual void OnClose(DWORD dwSaveOption);
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(Client, IScribbleClient)
STDMETHOD(OnRemoveAll)();
STDMETHOD(OnNewStroke)(VARIANT vtStroke);
END_INTERFACE_PART(Client)
enum {
//{{AFX_DISP_ID(ScribbleCtrl)
//}}AFX_DISP_ID
};
protected:
long m_lCookie;
IScribbleServerPtr m_pServer;
...
//{{AFX_MSG(ScribbleCtrl)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
//}}AFX_MSG
//{{AFX_DISPATCH(ScribbleCtrl)
//}}AFX_DISPATCH
//{{AFX_EVENT(ScribbleCtrl)
//}}AFX_EVENT
DECLARE_DYNCREATE(ScribbleCtrl)
};
ScribbleCtrl::ScribbleCtrl()
{
InitializeIIDs(&IID_DScribble, &IID_DScribbleEvents);
SetInitialSize(200, 200);
AddDocTemplate(new CActiveXDocTemplate(
RUNTIME_CLASS(CScribServDoc),
RUNTIME_CLASS(CScribbleFrame),
RUNTIME_CLASS(CScribbleView)));
m_pServer.CreateInstance("ExtremeCpp.ScribbleServer");
}
int ScribbleCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CActiveXDocControl::OnCreate(lpCreateStruct) == -1)
return -1;
if (m_pServer == NULL)
AfxMessageBox("Could not locate Server.");
else
{
IUnknown* lpUnknown = NULL;
ExternalQueryInterface(&IID_IUnknown, (void**) &lpUnknown);
m_pServer->RegisterClient(lpUnknown, &m_lCookie);
lpUnknown->Release();
}
return 0;
}
void ScribbleCtrl::OnClose(DWORD dwSaveOption)
{
if (m_pServer)
m_pServer->UnregisterClient(m_lCookie);
CActiveXDocControl::OnClose(dwSaveOption);
}
BEGIN_INTERFACE_MAP(ScribbleCtrl, CActiveXDocControl)
INTERFACE_PART(ScribbleCtrl, IID_IScribbleClient, Client)
END_INTERFACE_MAP()
STDMETHODIMP ScribbleCtrl::XClient::OnRemoveAll()
{
METHOD_PROLOGUE(ScribbleCtrl, Client)
CScribServDoc* pDoc = DYNAMIC_DOWNCAST(CScribServDoc,
pThis->m_pFrameWnd->GetActiveDocument());
pDoc->OnNewDocument();
pDoc->UpdateAllViews(NULL);
return S_OK;
}
STDMETHODIMP ScribbleCtrl::XClient::OnNewStroke(VARIANT vtStroke)
{
METHOD_PROLOGUE(ScribbleCtrl, Client)
CScribServDoc* pDoc = DYNAMIC_DOWNCAST(CScribServDoc,
pThis->m_pFrameWnd->GetActiveDocument());
pDoc->AddExternalStroke(vtStroke);
return S_OK;
}
Figure 5 CObject to VARIANT Conversion
void CScribServDoc::MakeVariantFromObject(CObject* pObject, VARIANT& vtArray)
{
ASSERT(pObject);
CMemFile memFile;
CArchive archive(&memFile, CArchive::store);
pObject->Serialize(archive);
archive.Close();
char sTemp[20];
UINT nLength = memFile.GetLength();
UCHAR* pBuffer = memFile.Detach();
long* pArray = (long*) pBuffer;
CString strAscii = itoa(nLength, sTemp, 10);
for (UINT nLoop = 0; nLoop < nLength / sizeof(long); nLoop++)
{
strAscii += '@'; // Separator character
strAscii += ultoa(pArray[nLoop], sTemp, 16);
}
COleSafeArray safeArray;
safeArray.CreateOneDim(VT_UI1, strAscii.GetLength(),
strAscii.LockBuffer());
vtArray = safeArray.Detach();
delete [] pBuffer;
}
CObject* CScribServDoc::MakeObjectFromVariant(
CRuntimeClass* pObjType, VARIANT& vtArray)
{
char* pNextNum;
COleSafeArray safeArray;
safeArray.Attach(vtArray);
safeArray.AccessData((void**) &pNextNum);
UINT nLength = (UINT) strtoul(pNextNum, &pNextNum, 10) / sizeof(long) + 1;
ULONG* pArray = new ULONG[nLength];
for (UINT nCount = 0; nCount < nLength; nCount++)
pArray[nCount] = strtoul(++pNextNum, &pNextNum, 16);
safeArray.UnaccessData();
safeArray.Detach();
CMemFile memFile((BYTE*) pArray, nLength * sizeof(long));
CArchive archive(&memFile, CArchive::load);
CObject* pObject = pObjType->CreateObject();
pObject->Serialize(archive);
archive.Close();
delete [] pArray;
return pObject;
}
Figure 6 Whiteboard Server
// Server.cpp : Implementation of CScribbleServer
#include "stdafx.h"
#include "ScribSvr.h"
#include "Server.h"
/////////////////////////////////////////////////////////////////////////////
// CScribbleServer
VariantVector CScribbleServer::m_strokes;
ClientVector CScribbleServer::m_clients;
long CScribbleServer::m_lCookieCount = 0;
STDMETHODIMP CScribbleServer::RegisterClient(IUnknown* lpUnknown, long* pCookie)
{
if (lpUnknown == NULL || pCookie == NULL)
return E_POINTER;
IScribbleClient* lpClient = NULL;
HRESULT hr = lpUnknown->QueryInterface(
IID_IScribbleClient, (void**) &lpClient);
if (!SUCCEEDED(hr))
return hr;
CClientInfo client(lpClient, m_lCookieCount++);
m_clients.push_back(client);
*pCookie = client.lCookie;
VariantVector::iterator item;
for (item = m_strokes.begin(); item != m_strokes.end(); item++)
hr = lpClient->OnNewStroke(*item);
return S_OK;
}
STDMETHODIMP CScribbleServer::UnregisterClient(long lCookie)
{
ClientVector::iterator item;
for (item = m_clients.begin(); item != m_clients.end(); item++)
{
if (item->lCookie == lCookie)
{
item->lpClient->Release();
m_clients.erase(item);
return S_OK;
}
}
return E_INVALIDARG;
}
STDMETHODIMP CScribbleServer::AddStroke(
long lCookie, VARIANT vtStroke, long* pIndex)
{
ATLTRACE("Added new stroke\n");
_ASSERTE(vtStroke.vt & VT_ARRAY);
m_strokes.push_back(vtStroke);
ClientVector::iterator item;
for (item = m_clients.begin(); item != m_clients.end(); item++)
{
if (lCookie != item->lCookie)
{
try
{
item->lpClient->OnNewStroke(vtStroke);
}
catch(...)
{
}
}
}
return S_OK;
}
STDMETHODIMP CScribbleServer::RemoveAllStrokes()
{
ATLTRACE("Removed all strokes\n");
m_strokes.clear();
ClientVector::iterator item;
for (item = m_clients.begin(); item != m_clients.end(); item++)
{
try
{
item->lpClient->OnRemoveAll();
}
catch(...)
{
}
}
return S_OK;
}
}