1121 lines
28 KiB
C++
1121 lines
28 KiB
C++
// --------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// --------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
|
|
#define MAX_VAR 20
|
|
|
|
|
|
//
|
|
// Distributed drawing 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;
|
|
m_iRadioDS = 0;
|
|
m_strRemoteComputerName = _T("");
|
|
//}}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_STATIC_C_LABEL, m_cComputerLabel);
|
|
DDX_Control(pDX, IDCANCEL, m_CancelButton);
|
|
DDX_Control(pDX, IDC_STATIC_Q_LABEL, m_cQueueLabel);
|
|
DDX_Control(pDX, IDC_STATIC_CHOOSE_MESSAGE, m_cMessageFrame);
|
|
DDX_Control(pDX, IDC_STATIC_CHOOSE_DS, m_cDsFrame);
|
|
DDX_Control(pDX, IDC_RADIO_EXPRESS, m_cRadioExpress);
|
|
DDX_Control(pDX, IDC_RADIO_DS, m_cRadioDS);
|
|
DDX_Control(pDX, IDC_EDIT_FRIEND_COMPUTER, m_cComputerInput);
|
|
DDX_Control(pDX, IDC_EDIT_FRIEND, m_cQueueInput);
|
|
DDX_Control(pDX, IDC_CONTINUE, m_cContinueButton);
|
|
DDX_Control(pDX, IDC_DRAWAREA_SCRIBLLE, m_drawScribble);
|
|
DDX_Control(pDX, IDC_BUTTON_ATTACH, m_btnAttach);
|
|
DDX_Text(pDX, IDC_EDIT_FRIEND, m_strFriend);
|
|
DDV_MaxChars(pDX, m_strFriend, 50);
|
|
DDX_Radio(pDX, IDC_RADIO_EXPRESS, m_iDelivery);
|
|
DDX_Radio(pDX, IDC_RADIO_DS, m_iRadioDS);
|
|
DDX_Text(pDX, IDC_EDIT_FRIEND_COMPUTER, m_strRemoteComputerName);
|
|
DDV_MaxChars(pDX, m_strRemoteComputerName, 50);
|
|
DDX_Control(pDX, IDC_RADIO_WORKGROUP, m_cRadioWorkgroup);
|
|
DDX_Control(pDX, IDC_RADIO_RECOVERABLE, m_cRadioRecoverable);
|
|
//}}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()
|
|
ON_BN_CLICKED(IDC_CONTINUE, OnConnect)
|
|
ON_EN_CHANGE(IDC_EDIT_FRIEND_COMPUTER, OnChangeEditFriendComputer)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDisdrawDlg message handlers
|
|
|
|
|
|
BOOL LocateQueue(CString m_strLabel, WCHAR *wcsFormatName, DWORD dwNumChars)
|
|
{
|
|
//
|
|
// Set restrictions to locate the drawing queue with the label specified.
|
|
//
|
|
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+1];
|
|
size_t nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsLabel,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0]),
|
|
m_strLabel,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0])-1
|
|
);
|
|
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 queue specified.
|
|
//
|
|
cProps = 0;
|
|
QUEUEPROPID aPropId[1];
|
|
MQCOLUMNSET Column;
|
|
|
|
aPropId[cProps] = PROPID_Q_INSTANCE;
|
|
cProps++;
|
|
|
|
Column.cCol = cProps;
|
|
Column.aCol = aPropId;
|
|
|
|
|
|
//
|
|
// Locate the queue specified.
|
|
//
|
|
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 queue located.
|
|
//
|
|
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+1];
|
|
if (!LocateQueue(m_strLogin, wcsFormatName, sizeof(wcsFormatName)/sizeof(wcsFormatName[0])))
|
|
{
|
|
//
|
|
// Form the pathname of the receiving queue.
|
|
//
|
|
char mbsPathName[MQ_MAX_Q_NAME_LEN+1];
|
|
DWORD dwNumChars = sizeof(mbsPathName);
|
|
GetComputerName(mbsPathName, &dwNumChars);
|
|
strcat_s(mbsPathName, sizeof(mbsPathName), "\\");
|
|
strcat_s(mbsPathName, sizeof(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+1];
|
|
size_t nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsPathName,
|
|
sizeof(wcsPathName)/sizeof(wcsPathName[0]),
|
|
mbsPathName,
|
|
sizeof(wcsPathName)/sizeof(wcsPathName[0])-1
|
|
);
|
|
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+1];
|
|
nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsLabel,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0]),
|
|
m_strLogin,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0])-1
|
|
);
|
|
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 = sizeof(wcsFormatName)/sizeof(wcsFormatName[0]);
|
|
hr = MQCreateQueue(NULL, &propsQueue, wcsFormatName, &dwNumChars);
|
|
|
|
|
|
//
|
|
// If the receiving queue already exists, obtain its format name.
|
|
//
|
|
if (hr == MQ_ERROR_QUEUE_EXISTS)
|
|
{
|
|
dwNumChars = sizeof(wcsFormatName)/sizeof(wcsFormatName[0]);
|
|
hr = MQPathNameToFormatName(wcsPathName, wcsFormatName, &dwNumChars);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Open the receiving queue (it may be necessary to retry due to replication latency).
|
|
//
|
|
int iCount = 0;
|
|
while ((hr = MQOpenQueue(wcsFormatName,
|
|
MQ_RECEIVE_ACCESS,
|
|
MQ_DENY_NONE ,
|
|
&m_hqIncoming)) == MQ_ERROR_QUEUE_NOT_FOUND)
|
|
{
|
|
|
|
Sleep (500);
|
|
iCount++;
|
|
if (iCount >=30)
|
|
{
|
|
//
|
|
// A period of 15 seconds past without locating the queue.
|
|
// We allow the user to stop attempts to open the queue.
|
|
//
|
|
int iRes = MessageBox("The receiving queue was not located.\nDo you want to continue searching?",
|
|
NULL,
|
|
MB_YESNO);
|
|
if(iRes == IDYES)
|
|
{
|
|
//
|
|
// Continue searching.
|
|
//
|
|
iCount =0;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Stop searching.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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+1];
|
|
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 multibyte null-terminated string.
|
|
//
|
|
UINT uNumChars = aPropVar[1].ulVal/sizeof(WCHAR);
|
|
size_t nResultedStringLength=0;
|
|
wcstombs_s(
|
|
&nResultedStringLength,
|
|
mbsBody,
|
|
sizeof(mbsBody),
|
|
wcsBody,
|
|
uNumChars
|
|
);
|
|
|
|
//
|
|
// Add the keystroke to the drawing.
|
|
//
|
|
if (uNumChars == 1)
|
|
{
|
|
pDrawDlg->m_drawScribble.AddKeystroke(mbsBody);
|
|
}
|
|
|
|
//
|
|
// Add the line received to the drawing.
|
|
//
|
|
else
|
|
{
|
|
sscanf_s(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 box. The framework does this automatically
|
|
// when the application's main window is not a dialog box.
|
|
SetIcon(m_hIcon, TRUE); // Use the large icon.
|
|
SetIcon(m_hIcon, FALSE); // Use the small icon.
|
|
|
|
|
|
//
|
|
// Display the logon name in the title bar.
|
|
//
|
|
SetWindowText(m_strLogin);
|
|
|
|
|
|
//
|
|
// No queues are open yet.
|
|
//
|
|
m_hqIncoming = NULL;
|
|
m_hqOutgoing = NULL;
|
|
|
|
//
|
|
// Establish connection mode.
|
|
//
|
|
m_fDsEnabledLocaly = IsDsEnabledLocaly();
|
|
|
|
if (!m_fDsEnabledLocaly)
|
|
{
|
|
//
|
|
// The computer is DS-disabled. This is the right place to open the receiving queue
|
|
// because it is a local private queue. This operation is not time-consuming.
|
|
//
|
|
//
|
|
// Open the receiving queue and receive incoming messages.
|
|
//
|
|
|
|
if (OpenPrivateReceiveQueue())
|
|
{
|
|
startReceiveUpdateThread();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The receiving queue could not be opened.
|
|
//
|
|
MessageBox("The receiving queue cannot be opened.");
|
|
}
|
|
|
|
}
|
|
|
|
initializeUi(m_fDsEnabledLocaly);
|
|
|
|
return TRUE; // Return TRUE unless you set the focus to a control.
|
|
}
|
|
|
|
|
|
// If you add a minimize button to your dialog box, 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 the icon in the 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 function 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 queue, if there is one.
|
|
//
|
|
if (m_hqOutgoing != NULL)
|
|
{
|
|
//
|
|
// Prepare the message properties to send the message.
|
|
//
|
|
DWORD cProps = 0;
|
|
MQMSGPROPS propsMessage;
|
|
MQPROPVARIANT aPropVar[5];
|
|
MSGPROPID aPropId[5];
|
|
|
|
WCHAR wcsBody[1];
|
|
swprintf_s(wcsBody, sizeof(wcsBody)/sizeof(wcsBody[0]), 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+1];
|
|
swprintf_s(wcsLabel, sizeof(wcsLabel)/sizeof(wcsLabel[0]), 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 queue, if there is one.
|
|
//
|
|
if (m_hqOutgoing != NULL)
|
|
{
|
|
//
|
|
// Prepare the message properties to send the message.
|
|
//
|
|
DWORD cProps = 0;
|
|
MQMSGPROPS propsMessage;
|
|
MQPROPVARIANT aPropVar[5];
|
|
MSGPROPID aPropId[5];
|
|
|
|
WCHAR wcsBody[MAX_MSG_BODY_LEN+1];
|
|
swprintf_s(wcsBody, sizeof(wcsBody)/sizeof(wcsBody[0]), 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+1];
|
|
swprintf_s(wcsLabel, sizeof(wcsLabel)/sizeof(wcsLabel[0]), 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()
|
|
{
|
|
|
|
//
|
|
// The constructor changes the cursor to an hourglass.
|
|
// When the object goes out of scope, the cursor changes back to normal.
|
|
//
|
|
CWaitCursor wait ;
|
|
WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN+1];
|
|
|
|
if(m_fDsEnabledLocaly && m_iRadioDS == 0)
|
|
{
|
|
//
|
|
// Connect with a remote computer in standard mode.
|
|
//
|
|
|
|
//
|
|
// Obtain the name of the friend queue.
|
|
//
|
|
UpdateData(TRUE);
|
|
m_strFriend.MakeUpper();
|
|
//
|
|
// Make sure the friend queue exists.
|
|
//
|
|
if (!LocateQueue(m_strFriend, wcsFormatName, sizeof(wcsFormatName)/sizeof(wcsFormatName[0])) )
|
|
{
|
|
AfxMessageBox("No such queue exists.");
|
|
return;
|
|
}
|
|
|
|
|
|
}
|
|
else if(!m_fDsEnabledLocaly || m_iRadioDS == 1)
|
|
{
|
|
//
|
|
// Connect with a remote computer in direct mode.
|
|
//
|
|
|
|
//
|
|
// Obtain the name of the friend queue and the remote computer.
|
|
//
|
|
UpdateData(TRUE);
|
|
m_strFriend.MakeUpper();
|
|
m_strRemoteComputerName.MakeUpper();
|
|
|
|
|
|
//
|
|
// Make sure the friend queue exists. Since we are connecting to
|
|
// a private queue, we cannot obtain its format name from the
|
|
// directory service (DS), so we will asuume it exists and try to open it.
|
|
//
|
|
|
|
//
|
|
// Open the friend queue for sending (in direct mode)
|
|
// Please Note:
|
|
// Opening a remote queue for sending will always
|
|
// succeed even if the queue or computer does not exist.
|
|
// When working in direct mode, we must use private queues.
|
|
// We cannot know whether a given private queue on another computer exists or not,
|
|
// so here we will just assume that it does. To make the application more robust,
|
|
// an acknowledgement queue should be created on the local computer, and
|
|
// a request for acknowledgement messages should be added to the messages sent.
|
|
// Then the application can notify the user when a negative adknowledgement message
|
|
// (NACK) is received.
|
|
//
|
|
|
|
|
|
//
|
|
// Compose the format name.
|
|
//
|
|
char mbsFormatName[MAX_FORMAT_NAME_LEN+1];
|
|
sprintf_s(mbsFormatName, sizeof(mbsFormatName), "DIRECT=OS:%s\\private$\\%s",m_strRemoteComputerName,m_strFriend);
|
|
size_t nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsFormatName,
|
|
sizeof(wcsFormatName)/sizeof(wcsFormatName[0]),
|
|
mbsFormatName,
|
|
sizeof(mbsFormatName)-1
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Open the remote queue for sending.
|
|
// The action is the same in both cases.
|
|
//
|
|
HANDLE hqNewFriend;
|
|
HRESULT hr = MQOpenQueue(wcsFormatName, MQ_SEND_ACCESS, MQ_DENY_NONE , &hqNewFriend);
|
|
if (FAILED(hr))
|
|
AfxMessageBox("The remote queue cannot be opened.");
|
|
|
|
else
|
|
{
|
|
//
|
|
// Close the previous friend queue and update the title bar.
|
|
//
|
|
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()
|
|
{
|
|
|
|
m_btnAttach.EnableWindow(TRUE);
|
|
}
|
|
|
|
|
|
void CDisdrawDlg::OnClose()
|
|
{
|
|
// TODO: Add your message handler code here and/or call default.
|
|
|
|
//
|
|
// Close any open queues.
|
|
//
|
|
if (m_hqIncoming != NULL)
|
|
MQCloseQueue(m_hqIncoming);
|
|
|
|
if (m_hqOutgoing != NULL)
|
|
MQCloseQueue(m_hqOutgoing);
|
|
|
|
CDialog::OnClose();
|
|
}
|
|
|
|
|
|
BOOL CDisdrawDlg::IsDsEnabledLocaly()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine checks whether the local computer is DS-enabled
|
|
or DS-disabled.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE - DS-enabled.
|
|
FALSE - DS-disabled.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
MQPRIVATEPROPS PrivateProps;
|
|
QMPROPID aPropId[MAX_VAR];
|
|
MQPROPVARIANT aPropVar[MAX_VAR];
|
|
DWORD cProp;
|
|
HRESULT hr;
|
|
//
|
|
// Prepare a DS-enabled property.
|
|
//
|
|
cProp = 0;
|
|
|
|
aPropId[cProp] = PROPID_PC_DS_ENABLED;
|
|
aPropVar[cProp].vt = VT_NULL;
|
|
++cProp;
|
|
//
|
|
// Create a PRIVATEPROPS structure.
|
|
//
|
|
PrivateProps.cProp = cProp;
|
|
PrivateProps.aPropID = aPropId;
|
|
PrivateProps.aPropVar = aPropVar;
|
|
PrivateProps.aStatus = NULL;
|
|
|
|
//
|
|
// Retrieve the information.
|
|
//
|
|
|
|
|
|
//
|
|
// This code is used to detect a DS connection.
|
|
// This code is designed to allow compilation on both
|
|
// Windows NT 4.0 and Windows 2000.
|
|
//
|
|
HINSTANCE hMqrtLibrary = GetModuleHandle(TEXT("mqrt.dll"));
|
|
ASSERT(hMqrtLibrary != NULL);
|
|
|
|
typedef HRESULT (APIENTRY *MQGetPrivateComputerInformation_ROUTINE)(LPCWSTR , MQPRIVATEPROPS*);
|
|
MQGetPrivateComputerInformation_ROUTINE pfMQGetPrivateComputerInformation =
|
|
(MQGetPrivateComputerInformation_ROUTINE)GetProcAddress(hMqrtLibrary,
|
|
"MQGetPrivateComputerInformation");
|
|
if(pfMQGetPrivateComputerInformation == NULL)
|
|
{
|
|
//
|
|
// There is no entry point in the DLL that matches this routine.
|
|
// It must be an old version of mqrt.dll -> product one.
|
|
// It will be OK to handle this case as the case of DS-enabled computer.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
hr = pfMQGetPrivateComputerInformation(
|
|
NULL,
|
|
&PrivateProps);
|
|
if(FAILED(hr))
|
|
{
|
|
//
|
|
// We were not able to determine whether the computer is DS-enabled or DS-disabled.
|
|
// Notify the user and assume the worst case (i.e., the computer is DS-disasbled).
|
|
//
|
|
MessageBox("No DS connection can be detected.");
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if(PrivateProps.aPropVar[0].boolVal == 0)
|
|
{
|
|
//
|
|
// DS-disabled.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CDisdrawDlg::initializeUi(BOOL fConnectedToDS)
|
|
{
|
|
if(fConnectedToDS)
|
|
{
|
|
//
|
|
// Show the relevant controls for a DS-enabled computer.
|
|
//
|
|
m_cDsFrame.ShowWindow(SW_SHOW);
|
|
m_cRadioDS.ShowWindow(SW_SHOW);
|
|
m_cRadioWorkgroup.ShowWindow(SW_SHOW);
|
|
m_cContinueButton.ShowWindow(SW_SHOW);
|
|
m_CancelButton.ShowWindow(SW_SHOW);
|
|
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//Show the relevant controls for a DS-disabled computer.
|
|
//
|
|
m_cMessageFrame.ShowWindow(SW_SHOW);
|
|
m_cRadioExpress.ShowWindow(SW_SHOW);
|
|
m_cRadioRecoverable.ShowWindow(SW_SHOW);
|
|
m_cQueueInput.ShowWindow(SW_SHOW);
|
|
m_cQueueLabel.ShowWindow(SW_SHOW);
|
|
m_cComputerInput.ShowWindow(SW_SHOW);
|
|
m_cComputerLabel.ShowWindow(SW_SHOW);
|
|
m_CancelButton.ShowWindow(SW_SHOW);
|
|
m_btnAttach.ShowWindow(SW_SHOW);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void CDisdrawDlg::OnConnect()
|
|
{
|
|
//
|
|
// See what the user selected.
|
|
//
|
|
UpdateData(TRUE);
|
|
if(m_iRadioDS == 0)
|
|
{
|
|
//
|
|
// The DS is enabled locally, and the user chose to connect in standard mode.
|
|
//
|
|
|
|
CWaitCursor wait ;
|
|
|
|
//
|
|
// Open the receiving queue and receive incoming messages.
|
|
//
|
|
|
|
if (OpenReceiveQueue())
|
|
{
|
|
startReceiveUpdateThread();
|
|
}
|
|
else
|
|
{
|
|
AfxMessageBox("The receiving queue cannot be opened.");
|
|
}
|
|
|
|
|
|
//
|
|
// Hide relevant controls.
|
|
//
|
|
m_cDsFrame.ShowWindow(SW_HIDE);
|
|
m_cRadioDS.ShowWindow(SW_HIDE);
|
|
m_cRadioWorkgroup.ShowWindow(SW_HIDE);
|
|
m_cContinueButton.ShowWindow(SW_HIDE);
|
|
|
|
//
|
|
// Show relevant controls.
|
|
//
|
|
|
|
m_cMessageFrame.ShowWindow(SW_SHOW);
|
|
m_cRadioExpress.ShowWindow(SW_SHOW);
|
|
m_cRadioRecoverable.ShowWindow(SW_SHOW);
|
|
m_cQueueInput.ShowWindow(SW_SHOW);
|
|
m_cQueueLabel.ShowWindow(SW_SHOW);
|
|
|
|
m_CancelButton.ShowWindow(SW_SHOW);
|
|
m_btnAttach.ShowWindow(SW_SHOW);
|
|
|
|
}
|
|
else//m_iRadio == 1
|
|
{
|
|
//
|
|
// The DS is enabled locally, and the user chose to connect in direct mode.
|
|
//
|
|
//
|
|
// The constructor changes the cursor to an hourglass.
|
|
// When the object goes out of scope, the cursor changes back to normal.
|
|
//
|
|
CWaitCursor wait ;
|
|
|
|
//
|
|
// Open the receiving private queue and receive incoming messages.
|
|
//
|
|
|
|
if (OpenPrivateReceiveQueue())
|
|
{
|
|
startReceiveUpdateThread();
|
|
}
|
|
else
|
|
{
|
|
AfxMessageBox("The receiving queue cannot be opened.");
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Hide relevant controls.
|
|
//
|
|
m_cDsFrame.ShowWindow(SW_HIDE);
|
|
m_cRadioDS.ShowWindow(SW_HIDE);
|
|
m_cRadioWorkgroup.ShowWindow(SW_HIDE);
|
|
m_cContinueButton.ShowWindow(SW_HIDE);
|
|
|
|
//
|
|
// Show relevant controls.
|
|
//
|
|
m_cMessageFrame.ShowWindow(SW_SHOW);
|
|
m_cRadioExpress.ShowWindow(SW_SHOW);
|
|
m_cRadioRecoverable.ShowWindow(SW_SHOW);
|
|
m_cQueueInput.ShowWindow(SW_SHOW);
|
|
m_cQueueLabel.ShowWindow(SW_SHOW);
|
|
m_cComputerInput.ShowWindow(SW_SHOW);
|
|
m_cComputerLabel.ShowWindow(SW_SHOW);
|
|
m_CancelButton.ShowWindow(SW_SHOW);
|
|
m_btnAttach.ShowWindow(SW_SHOW);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
BOOL CDisdrawDlg::OpenPrivateReceiveQueue()
|
|
{
|
|
|
|
//
|
|
// Compose the pathname to the receiving queue and make it private.
|
|
//
|
|
char mbsPathName[MQ_MAX_Q_NAME_LEN+1];
|
|
DWORD dwNumChars = sizeof(mbsPathName);
|
|
GetComputerName(mbsPathName, &dwNumChars);
|
|
strcat_s(mbsPathName, sizeof(mbsPathName), "\\");
|
|
strcat_s(mbsPathName, sizeof(mbsPathName), "private$");
|
|
strcat_s(mbsPathName, sizeof(mbsPathName), "\\");
|
|
strcat_s(mbsPathName, sizeof(mbsPathName), m_strLogin);
|
|
|
|
|
|
//
|
|
// Prepare the properties of the receiving queue.
|
|
//
|
|
DWORD cProps = 0;
|
|
QUEUEPROPID aPropId[3];
|
|
MQPROPVARIANT aPropVar[3];
|
|
MQQUEUEPROPS propsQueue;
|
|
|
|
WCHAR wcsPathName[MQ_MAX_Q_NAME_LEN+1];
|
|
size_t nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsPathName,
|
|
sizeof(wcsPathName)/sizeof(wcsPathName[0]),
|
|
mbsPathName,
|
|
sizeof(wcsPathName)/sizeof(wcsPathName[0])-1
|
|
);
|
|
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+1];
|
|
nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsLabel,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0]),
|
|
m_strLogin,
|
|
sizeof(wcsLabel)/sizeof(wcsLabel[0])-1
|
|
);
|
|
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.
|
|
//
|
|
WCHAR wcsFormatName[MAX_FORMAT_NAME_LEN+1];
|
|
dwNumChars = sizeof(wcsFormatName)/sizeof(wcsFormatName[0]);
|
|
HRESULT hr = MQCreateQueue(NULL, &propsQueue, wcsFormatName, &dwNumChars);
|
|
|
|
|
|
if (FAILED(hr) && hr != MQ_ERROR_QUEUE_EXISTS)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if( hr == MQ_ERROR_QUEUE_EXISTS)
|
|
{
|
|
//
|
|
// If the receiving queue already exists, obtain its format name.
|
|
//
|
|
|
|
WCHAR wcsLoginName[MQ_MAX_Q_NAME_LEN+1];
|
|
nResultedStringLength=0;
|
|
mbstowcs_s(
|
|
&nResultedStringLength,
|
|
wcsLoginName,
|
|
sizeof(wcsLoginName)/sizeof(wcsLoginName[0]),
|
|
m_strLogin,
|
|
sizeof(wcsLoginName)/sizeof(wcsLoginName[0])-1
|
|
);
|
|
swprintf_s(wcsFormatName, sizeof(wcsFormatName)/sizeof(wcsFormatName[0]), L"DIRECT=OS:.\\private$\\%s",wcsLoginName);
|
|
|
|
}
|
|
|
|
//
|
|
// Open the receiving queue (a local private queue).
|
|
//
|
|
|
|
hr = MQOpenQueue(wcsFormatName,
|
|
MQ_RECEIVE_ACCESS,
|
|
MQ_DENY_NONE ,
|
|
&m_hqIncoming);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
void CDisdrawDlg::OnChangeEditFriendComputer()
|
|
{
|
|
//
|
|
// Allow specifying another remote computer.
|
|
//
|
|
m_btnAttach.EnableWindow(TRUE);
|
|
|
|
}
|
|
|
|
|
|
void CDisdrawDlg::startReceiveUpdateThread()
|
|
{
|
|
DWORD dwThreadID=NULL;
|
|
CreateThread(NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE)ReceiveUpdates,
|
|
this,
|
|
0,
|
|
&dwThreadID);
|
|
if( dwThreadID == NULL)
|
|
{
|
|
MessageBox("The system failed to initialize receiving queue.\nNo Drawing will be received");
|
|
}
|
|
|
|
}
|