DRAWDLG.CPP

// drawdlg.cpp : implementation file 
//

#include "stdafx.h"
#include "disdraw.h"
#include "drawdlg.h"

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

#define MAX_FORMAT_NAME_LEN 256

//
// Distributed draw queue type
//
CLSID guidDrawType =
{ 0x151ceac0, 0xacb5, 0x11cf, { 0x8b, 0x51, 0x00, 0x20, 0xaf, 0x92, 0x95, 0x46 } };

/////////////////////////////////////////////////////////////////////////////
// CDisdrawDlg dialog

CDisdrawDlg::CDisdrawDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDisdrawDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CDisdrawDlg)
m_strFriend = _T("");
m_iDelivery = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CDisdrawDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CDisdrawDlg)
DDX_Control(pDX, IDC_DRAWAREA_SCRIBLLE, m_drawScribble);
DDX_Control(pDX, IDC_BUTTON_ATTACH, m_btnAttach);
DDX_Text(pDX, IDC_EDIT_FRIEND, m_strFriend);
DDX_Radio(pDX, IDC_RADIO_EXPRESS, m_iDelivery);
//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CDisdrawDlg, CDialog)
//{{AFX_MSG_MAP(CDisdrawDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON_ATTACH, OnButtonAttach)
ON_EN_CHANGE(IDC_EDIT_FRIEND, OnChangeEditFriend)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDisdrawDlg message handlers


BOOL LocateQueue(CString m_strLabel, WCHAR *wcsFormatName, DWORD dwNumChars)
{
//
// Set restrictions to locate the draw queue with the specified label
//
DWORD cProps = 0;
MQPROPERTYRESTRICTION aPropRestriction[2];
MQRESTRICTION Restriction;

aPropRestriction[cProps].rel = PREQ;
aPropRestriction[cProps].prop = PROPID_Q_TYPE;
aPropRestriction[cProps].prval.vt = VT_CLSID;
aPropRestriction[cProps].prval.puuid = &guidDrawType;
cProps++;

WCHAR wcsLabel[MQ_MAX_Q_LABEL_LEN];
mbstowcs(wcsLabel, m_strLabel, MQ_MAX_Q_LABEL_LEN);
aPropRestriction[cProps].rel = PREQ;
aPropRestriction[cProps].prop = PROPID_Q_LABEL;
aPropRestriction[cProps].prval.vt = VT_LPWSTR;
aPropRestriction[cProps].prval.pwszVal = wcsLabel;
cProps++;

Restriction.cRes = cProps;
Restriction.paPropRes = aPropRestriction;


//
// Request the queue instance for the specified queue
//
cProps = 0;
QUEUEPROPID aPropId[1];
MQCOLUMNSET Column;

aPropId[cProps] = PROPID_Q_INSTANCE;
cProps++;

Column.cCol = cProps;
Column.aCol = aPropId;


//
// Locate the specified queue
//
HANDLE hEnum;
BOOL fFound = FALSE;
HRESULT hr = MQLocateBegin(NULL, &Restriction, &Column, NULL, &hEnum);
if (!FAILED(hr))
{
MQPROPVARIANT aPropVar[1];
DWORD cQueue = 1;
hr = MQLocateNext(hEnum, &cQueue, aPropVar);
if (!FAILED(hr) && cQueue > 0)
{
//
// Obtain the format name for the located queue
//
hr = MQInstanceToFormatName(aPropVar[0].puuid, wcsFormatName, &dwNumChars);
MQFreeMemory(aPropVar[0].puuid);
if (!FAILED(hr))
fFound = TRUE;
}

MQLocateEnd(hEnum);
}


return fFound;
}


BOOL CDisdrawDlg::OpenReceiveQueue()
{
//
// Do not create the receiving queue if it already exists in the enterprise
//
HRESULT hr;
WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN];
if (!LocateQueue(m_strLogin, wcsFormatName, MAX_FORMAT_NAME_LEN))
{
//
// Form the pathname to the receiving queue
//
char mbsPathName[MQ_MAX_Q_NAME_LEN];
DWORD dwNumChars = MQ_MAX_Q_NAME_LEN;
GetComputerName(mbsPathName, &dwNumChars);
strcat(mbsPathName, "\\");
strcat(mbsPathName, m_strLogin);


//
// Prepare the receiving queue properties
//
DWORD cProps = 0;
QUEUEPROPID aPropId[3];
MQPROPVARIANT aPropVar[3];
MQQUEUEPROPS propsQueue;

WCHAR wcsPathName[MQ_MAX_Q_NAME_LEN];
mbstowcs(wcsPathName, mbsPathName, MQ_MAX_Q_NAME_LEN);
aPropId[cProps] = PROPID_Q_PATHNAME;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsPathName;
cProps++;

aPropId[cProps] = PROPID_Q_TYPE;
aPropVar[cProps].vt = VT_CLSID;
aPropVar[cProps].puuid = &guidDrawType;
cProps++;

WCHAR wcsLabel[MQ_MAX_Q_LABEL_LEN];
mbstowcs(wcsLabel, m_strLogin, MQ_MAX_Q_LABEL_LEN);
aPropId[cProps] = PROPID_Q_LABEL;
aPropVar[cProps].vt = VT_LPWSTR;
aPropVar[cProps].pwszVal = wcsLabel;
cProps++;

propsQueue.cProp= cProps;
propsQueue.aPropID= aPropId;
propsQueue.aPropVar = aPropVar;
propsQueue.aStatus= NULL;


//
// Create the receiving queue
//
dwNumChars = MAX_FORMAT_NAME_LEN;
hr = MQCreateQueue(NULL, &propsQueue, wcsFormatName, &dwNumChars);


//
// If the receiving queue already exists, obtain its format name
//
if (hr == MQ_ERROR_QUEUE_EXISTS)
hr = MQPathNameToFormatName(wcsPathName, wcsFormatName, &dwNumChars);

if (FAILED(hr))
return FALSE;
}

//
// Open the receiving queue (may need to retry due to replication latency)
//
while ((hr = MQOpenQueue(wcsFormatName,
MQ_RECEIVE_ACCESS,
0, &m_hqIncoming)) == MQ_ERROR_QUEUE_NOT_FOUND)
Sleep (500);

if (FAILED(hr))
return FALSE;


return TRUE;
}


DWORD ReceiveUpdates(CDisdrawDlg *pDrawDlg)
{
//
// Prepare the message properties to receive
//
DWORD cProps = 0;
MQMSGPROPS propsMessage;
MQPROPVARIANT aPropVar[2];
MSGPROPID aPropId[2];

WCHAR wcsBody[MAX_MSG_BODY_LEN];
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

aPropId[cProps]= PROPID_M_BODY_SIZE;
aPropVar[cProps].vt= VT_UI4;
cProps++;

propsMessage.cProp = cProps;
propsMessage.aPropID = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus = NULL;


//
// Keep receiving updates sent to the incoming queue
//
HRESULT hr;
LINE line;
char mbsBody[MAX_MSG_BODY_LEN + 1];

while (TRUE)
{
//
// Synchronously receive a message from the incoming queue
//
hr = MQReceiveMessage(pDrawDlg->m_hqIncoming, INFINITE,
MQ_ACTION_RECEIVE, &propsMessage,
NULL, NULL, NULL, NULL);

//
// Determine if the message contains a keystroke or a line
//
if (!FAILED(hr))
{
//
// Convert the body to a multi-byte null-terminated string
//
UINT uNumChars = aPropVar[1].ulVal/sizeof(WCHAR);
wcstombs(mbsBody, wcsBody, uNumChars);
mbsBody[uNumChars] = '\0';

//
// Add the keystroke to the drawing
//
if (uNumChars == 1)
{
pDrawDlg->m_drawScribble.AddKeystroke(mbsBody);
}

//
// Add the received line to the drawing
//
else
{
sscanf(mbsBody, "%07ld%07ld%07ld%07ld",
&line.ptStart.x, &line.ptStart.y,
&line.ptEnd.x, &line.ptEnd.y);
pDrawDlg->m_drawScribble.AddLine(line);
}
}
}


return 0;
}


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

// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE);// Set big icon
SetIcon(m_hIcon, FALSE);// Set small icon

// TODO: Add extra initialization here

//
// Display the login name in the window title
//
SetWindowText(m_strLogin);


//
// No queues are open yet
//
m_hqIncoming = NULL;
m_hqOutgoing = NULL;


//
// Open the receiving queue and receive incoming messages
//
DWORD dwThreadID;
if (OpenReceiveQueue())
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceiveUpdates, this, 0,
&dwThreadID);
else
AfxMessageBox("Cannot open receiving queue.");


return TRUE; // return TRUE unless you set the focus to a control
}


// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.

void CDisdrawDlg::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();
}
}


// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CDisdrawDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}


void CDisdrawDlg::SendKeystroke(UINT uChar)
{
//
// Send the keystroke to the friend, if any
//
if (m_hqOutgoing != NULL)
{
//
// Prepare the message properties to send
//
DWORD cProps = 0;
MQMSGPROPS propsMessage;
MQPROPVARIANT aPropVar[5];
MSGPROPID aPropId[5];

WCHAR wcsBody[1];
swprintf(wcsBody, L"%c", uChar);
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

WCHAR wcsLabel[MQ_MAX_MSG_LABEL_LEN];
swprintf(wcsLabel, L"Key: %c", uChar);
aPropId[cProps]= PROPID_M_LABEL;
aPropVar[cProps].vt= VT_LPWSTR;
aPropVar[cProps].pwszVal= wcsLabel;
cProps++;

UpdateData(TRUE);
aPropId[cProps]= PROPID_M_DELIVERY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= m_iDelivery;
cProps++;

aPropId[cProps]= PROPID_M_PRIORITY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= 4;
cProps++;

aPropId[cProps]= PROPID_M_BODY_TYPE;
aPropVar[cProps].vt= VT_UI4;
aPropVar[cProps].lVal= (long)VT_BSTR;
cProps++;

propsMessage.cProp = cProps;
propsMessage.aPropID = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus = NULL;


//
// Send the message to the outgoing queue
//
MQSendMessage(m_hqOutgoing, &propsMessage, NULL);
}
}


void CDisdrawDlg::SendMouseMovement(LINE line)
{
//
// Send the line to the friend, if any
//
if (m_hqOutgoing != NULL)
{
//
// Prepare the message properties to send
//
DWORD cProps = 0;
MQMSGPROPS propsMessage;
MQPROPVARIANT aPropVar[5];
MSGPROPID aPropId[5];

WCHAR wcsBody[MAX_MSG_BODY_LEN];
swprintf(wcsBody, L"%07ld%07ld%07ld%07ld",
line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);
aPropId[cProps]= PROPID_M_BODY;
aPropVar[cProps].vt= VT_UI1 | VT_VECTOR;
aPropVar[cProps].caub.cElems = sizeof(wcsBody);
aPropVar[cProps].caub.pElems = (UCHAR *)wcsBody;
cProps++;

WCHAR wcsLabel[MQ_MAX_MSG_LABEL_LEN];
swprintf(wcsLabel, L"%ld,%ld To %ld,%ld",
line.ptStart.x, line.ptStart.y, line.ptEnd.x, line.ptEnd.y);
aPropId[cProps]= PROPID_M_LABEL;
aPropVar[cProps].vt= VT_LPWSTR;
aPropVar[cProps].pwszVal= wcsLabel;
cProps++;

UpdateData(TRUE);
aPropId[cProps]= PROPID_M_DELIVERY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= m_iDelivery;
cProps++;

aPropId[cProps]= PROPID_M_PRIORITY;
aPropVar[cProps].vt= VT_UI1;
aPropVar[cProps].bVal= 3;
cProps++;

aPropId[cProps]= PROPID_M_BODY_TYPE;
aPropVar[cProps].vt= VT_UI4;
aPropVar[cProps].lVal= (long)VT_BSTR;
cProps++;

propsMessage.cProp = cProps;
propsMessage.aPropID = aPropId;
propsMessage.aPropVar = aPropVar;
propsMessage.aStatus = NULL;


//
// Send the message to the outgoing queue
//
MQSendMessage(m_hqOutgoing, &propsMessage, NULL);
}
}


void CDisdrawDlg::OnButtonAttach()
{
// TODO: Add your control notification handler code here

//
// Obtain the name of the friend
//
UpdateData(TRUE);
m_strFriend.MakeUpper();


//
// Make sure the friend queue exists
//
WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN];
if (!LocateQueue(m_strFriend, wcsFormatName, MAX_FORMAT_NAME_LEN))
{
AfxMessageBox("No such friend, sorry...");
return;
}


//
// Open the friend queue for sending
//
HANDLE hqNewFriend;
HRESULT hr = MQOpenQueue(wcsFormatName, MQ_SEND_ACCESS, 0, &hqNewFriend);
if (FAILED(hr))
AfxMessageBox("Cannot open friend queue.");

else
{
//
// Close the previous friend and update the window title
//
if (m_hqOutgoing != NULL)
MQCloseQueue(m_hqOutgoing);
m_hqOutgoing = hqNewFriend;
CString strTitle = m_strLogin + " connected to " + m_strFriend;
SetWindowText(strTitle);
m_btnAttach.EnableWindow(FALSE);
}
}


void CDisdrawDlg::OnChangeEditFriend()
{
// TODO: Add your control notification handler code here

m_btnAttach.EnableWindow(TRUE);
}


void CDisdrawDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default

//
// Close the open queues, if any
//
if (m_hqIncoming != NULL)
MQCloseQueue(m_hqIncoming);

if (m_hqOutgoing != NULL)
MQCloseQueue(m_hqOutgoing);

CDialog::OnClose();
}