Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Remote Control PCs

, 20 Sep 2013
Two projects that work together to remote control PCs across a LAN.
// Remote Desktop System - remote controlling of multiple PC's
// Copyright (C) 2000-2009 GravyLabs LLC

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

#include "stdafx.h"
#include "RDV.h"
#include "RDVDoc.h"
#include "RDVView.h"
#include "NewConnectionDlg.h"
#include "afxole.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CRDVView

IMPLEMENT_DYNCREATE(CRDVView, CScrollView)

BEGIN_MESSAGE_MAP(CRDVView, CScrollView)
	ON_COMMAND(ID_FILE_PRINT, &CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CScrollView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CScrollView::OnFilePrintPreview)

	// Socket notifications
	ON_MESSAGE(WM_MAKECONN, &CRDVView::OnMakeConn)
	ON_MESSAGE(WM_RECEIVEDATA, &CRDVView::OnReceiveData)
	ON_MESSAGE(WM_CLOSECONN, &CRDVView::OnCloseConn)

	// Socket messages
	ON_WM_LBUTTONDBLCLK()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MBUTTONDBLCLK()
	ON_WM_MBUTTONDOWN()
	ON_WM_MBUTTONUP()
	ON_WM_MOUSEWHEEL()
	ON_WM_MOUSEMOVE()
	ON_WM_RBUTTONDBLCLK()
	ON_WM_RBUTTONDOWN()
	ON_WM_RBUTTONUP()
	ON_WM_KEYDOWN()
	ON_WM_KEYUP()

	// Cursor
	ON_WM_SETCURSOR()

	// Timer
	ON_WM_TIMER()

	// Background
	ON_WM_ERASEBKGND()

	// Resizing
	ON_WM_SIZE()

	// Shrink to fit
	ON_COMMAND(ID_VIEW_ZOOM, &CRDVView::OnViewZoom)
	ON_UPDATE_COMMAND_UI(ID_VIEW_ZOOM, &CRDVView::OnUpdateViewZoom)
END_MESSAGE_MAP()

// CRDVView construction/destruction
CRDVView::CRDVView()
{
	m_cxWidth = 0;
	m_cyHeight = 0;
	m_nBitCount = 0;
	m_nGridThreads = 0;
	m_iGridThread = 0;
	m_nCompThreads = 0;
	m_bConnected = FALSE;
	m_pMTC = NULL;
	m_bViewZoom = TRUE;
	m_nSessionId = 0;
}

CRDVView::~CRDVView()
{
	// Delete the multithreaded AC driver
	if (m_pMTC)
	{
		delete m_pMTC;
		m_pMTC = NULL;
	}
}

BOOL CRDVView::DestroyWindow()
{
	return CView::DestroyWindow();
}

// Initialize the scrollbars
void CRDVView::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();
	m_ViewSize.cx = m_ViewSize.cy = 0;
	SetScrollSizes(MM_TEXT,m_ViewSize);
}

BOOL CRDVView::PreCreateWindow(CREATESTRUCT& cs)
{
	return CScrollView::PreCreateWindow(cs);
}

// CRDVView drawing
void CRDVView::OnDraw(CDC* pDC)
{
	// Update the display
	pDC->BitBlt(0,0,m_cxWidth,m_cyHeight,(CDC *)m_DIB,0,0,SRCCOPY);
}

// CRDVView printing
BOOL CRDVView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// Print only 1 page, the current frame
	pInfo->SetMaxPage(1);

	// default preparation
	return DoPreparePrinting(pInfo);
}

void CRDVView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
}

void CRDVView::OnPrint(CDC * pDC,CPrintInfo * pInfo)
{
	// Get the printer width and height in pixels
	int cxPrintWidth = pDC->GetDeviceCaps(HORZRES);
	int cyPrintHeight = pDC->GetDeviceCaps(VERTRES);

	// Stretch the bitmap to the area of the printer
	StretchBlt(pDC->GetSafeHdc(),0,0,cxPrintWidth,cyPrintHeight,m_DIB,0,0,m_cxWidth,m_cyHeight,SRCCOPY);
}

void CRDVView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
}

// CRDVView diagnostics
#ifdef _DEBUG
void CRDVView::AssertValid() const
{
	CScrollView::AssertValid();
}

void CRDVView::Dump(CDumpContext& dc) const
{
	CScrollView::Dump(dc);
}

CRDVDoc * CRDVView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CRDVDoc)));
	return (CRDVDoc *)m_pDocument;
}
#endif //_DEBUG

// Connect to the server
void CRDVView::ConnectServer()
{
	// Get the connection specifics
	CString csIp = GetDocument()->m_csIp;
	CString csPort = GetDocument()->m_csPort;

	// Create the outbound connection
	m_Socket.Create();

	// Set the parent for receiving socket notifications
	m_Socket.SetParent(GetSafeHwnd());

	// Attempt to connect to the server
	m_Socket.Connect(csIp,atoi(csPort));
}

// Disconnect from the server
void CRDVView::DisconnectServer()
{
	if (m_bConnected)
	{
		// Close the connection
		m_Socket.ShutDown();
		m_Socket.Close();

		// Delete the sink threads
		DeleteSinkThreads();

		// Stop the KB and Mouse timer
		KillTimer(4);

		// Update the connected status
		m_bConnected = FALSE;
	}

	// Clean up the GDI
	CleanupGDI();
}

// Connection has been made
LRESULT CRDVView::OnMakeConn(WPARAM wParam,LPARAM lParam)
{
	int nErrorCode = (int)lParam;
	if (nErrorCode)
	{
		// Get the error message
		CString csErrorMsg;
		switch( nErrorCode )
		{
			case WSAEADDRINUSE:
			{
				csErrorMsg = "The specified address is in use.\n";
				break;
			}
			case WSAEADDRNOTAVAIL:
			{
				csErrorMsg = "The specified address is not available from the local machine.\n";
				break;
			}
			case WSAEAFNOSUPPORT:
			{
				csErrorMsg = "Addresses in the specified family cannot be used with this socket.\n";
				break;
			}
			case WSAEDESTADDRREQ:
			{
				csErrorMsg = "A destination address is required.\n";
				break;
			}
			case WSAEFAULT:
			{
				csErrorMsg = "The lpSockAddrLen argument is incorrect.\n";
				break;
			}
			case WSAEINVAL:
			{
				csErrorMsg = "The socket is already bound to an address.\n";
				break;
			}
			case WSAEISCONN:
			{
				csErrorMsg = "The socket is already connected.\n";
				break;
			}
			case WSAEMFILE:
			{
				csErrorMsg = "No more file descriptors are available.\n";
				break;
			}
			case WSAENETUNREACH:
			{
				csErrorMsg = "The network cannot be reached from this host at this time.\n";
				break;
			}
			case WSAENOBUFS:
			{
				csErrorMsg = "No buffer space is available. The socket cannot be connected.\n";
				break;
			}
			case WSAENOTCONN:
			{
				csErrorMsg = "The socket is not connected.\n";
				break;
			}
			case WSAENOTSOCK:
			{
				csErrorMsg = "The descriptor is a file, not a socket.\n";
				break;
			}
			case WSAETIMEDOUT:
			{
				csErrorMsg = "The attempt to connect timed out without establishing a connection. \n";
				break;
			}
			case WSAECONNREFUSED:
			default:
			{
				csErrorMsg = "The attempt to connect was forcefully rejected.\n";
				break;
			}
		}

		// Notify
		AfxMessageBox(csErrorMsg);

		// Set the timer to close the document
		SetTimer(1,0,NULL);
	}
	return lParam;
}

// Received data event from the desktop server, now read the data
LRESULT CRDVView::OnReceiveData(WPARAM wParam,LPARAM lParam)
{
	// Receive the data
	CPacket * pPacket = NULL;

	// Receive the packet of data
	if (lParam)
	{
		// Image has been received in a thread and sent
		pPacket = (CPacket *)lParam;
	}
	else
	{
		// Read the packet from the main connection
		pPacket = new CPacket;
		CPacket & Packet = *pPacket;
		m_Socket >> Packet;
	}

	// Make it easy to work with either packet
	CPacket & Packet = *pPacket;

	// Process the data
	if (Packet.m_ucPacketType == 7)
	{
		// Create the temporary DIB
		CDIBFrame DIBPacket;
		DIBPacket.Init(Packet.m_Rect.Width(),Packet.m_Rect.Height(),m_nBitCount);

		// Point to the internal buffer of the DIB
		char * pBuffer = (LPSTR)DIBPacket;
		DWORD dwBytes = Packet.m_dwBytes;

		// Test for compression
		if (Packet.m_bUseCompression)
		{
			// Test for multi-threaded compression
			if (Packet.m_nCompThreads)
			{
				// Set the type of encoder
				m_pMTC->SetEncoder(Packet.m_bAC);

				// Multithreaded arithmetic decoding
				m_pMTC->SetBuffer(Packet.m_pBuffer,Packet.m_bAC ? Packet.m_dwBytes : Packet.m_dwSrcBytes,FALSE);
				m_pMTC->Decode();

				// Get the output buffer
				m_pMTC->GetBuffer(&pBuffer,&dwBytes,FALSE,FALSE);
			}
			else
			{
				if (Packet.m_bAC)
				{
					// Single-threaded arithmetic encoding
					m_AC.DecodeBuffer(Packet.m_pBuffer,Packet.m_dwBytes,&pBuffer,&dwBytes,FALSE);
				}
				else
				{
					// Single-threaded zlib uncompressing
					dwBytes = Packet.m_dwSrcBytes;
					m_ZLib.DecodeBuffer(Packet.m_pBuffer,Packet.m_dwBytes,pBuffer,dwBytes,FALSE);
				}
			}
		}
		else
		{
			// Copy the uncompressed DIB to the output
			memcpy(pBuffer,Packet.m_pBuffer,dwBytes);
		}

		// Get the image type
		BOOL bDIB = Packet.m_bDIB;

		// Get the reset attribute
		BOOL bXOR = Packet.m_bXOR;

		// Get the delta DIB
		CDIBFrame & XOR = m_XOR;

		// Get the viewed DIB
		CDIBFrame & DIB = m_DIB;

		// Get the rectangular coordinates
		CRect Rect = Packet.m_Rect;

		// Check for a PNG
		if (!bDIB)
		{
			// JPEG support
			COleStreamFile ImageStream;

			// Prepare the Image for memory stream serialization
			if (ImageStream.CreateMemoryStream())
			{
				// Write the packet to the stream
				ImageStream.Write(pBuffer,dwBytes);
				IStream * pImageStream = ImageStream.GetStream();

				// Load the image from the stream
				CImage ImageDIB;
				if (pImageStream && SUCCEEDED(ImageDIB.Load(pImageStream)))
				{
					// Transfer the PNG to a DIB
					ImageDIB.BitBlt(DIBPacket,0,0,SRCCOPY);

					// Release the stream
					pImageStream->Release();
				}
			}
		}

		if (bXOR)
		{
			// XOR the current desktop with the last
			BitBlt(DIB,Rect.left,Rect.top,Rect.Width(),Rect.Height(),DIBPacket,0,0,SRCINVERT);
		}
		else
		{
			// Copy over the current desktop
			BitBlt(DIB,Rect.left,Rect.top,Rect.Width(),Rect.Height(),DIBPacket,0,0,SRCCOPY);
		}

		// Update the last DIB
		BitBlt(XOR,0,0,m_cxWidth,m_cyHeight,DIB,0,0,SRCCOPY);

		// Update the screen
		InvalidateRect(NULL,FALSE);
		UpdateWindow();
	}
	else if (Packet.m_ucPacketType == 3) // Cursor
	{
		// Get the cursor data
		DWORD dwXHotSpot = Packet.m_dwXHotSpot;
		DWORD dwYHotSpot = Packet.m_dwYHotSpot;
		int nWidth = Packet.m_bmWidth;
		int nHeight = Packet.m_bmHeight;
		WORD bmMaskPlanes = Packet.m_bmMaskPlanes;
		WORD bmMaskBitsPixel = Packet.m_bmMaskBitsPixel;
		WORD bmColorPlanes = Packet.m_bmColorPlanes;
		WORD bmColorBitsPixel = Packet.m_bmColorBitsPixel;
		BYTE * pMaskBits = NULL;
		BYTE * pColorBits = NULL;
		if (Packet.m_MaskBits.size())
			pMaskBits = &(Packet.m_MaskBits[0]);
		if (Packet.m_ColorBits.size())
			pColorBits = &(Packet.m_ColorBits[0]);

		// Create a cursor
		m_Cursor.CreateCursor(dwXHotSpot,dwYHotSpot,nWidth,nHeight,bmMaskPlanes,bmMaskBitsPixel,bmColorPlanes,bmColorBitsPixel,pMaskBits,pColorBits);

		// Set the cursor
		SetClassLong(m_hWnd,-12,(LONG)(HCURSOR)m_Cursor);
		SetCursor(m_Cursor);
	}
	else if (Packet.m_ucPacketType == 2) // Display characteristics
	{
		// Free the previous multithread AC encoder
		if (m_pMTC)
		{
			delete m_pMTC;
			m_pMTC = NULL;
		}

		// Create the multithreaded AC driver
		m_pMTC = new CDriveMultiThreadedCompression(m_nCompThreads);

		// Display characteristics
		m_cxWidth = Packet.m_cxScreen;
		m_cyHeight = Packet.m_cyScreen;
		m_nBitCount = Packet.m_nBitCount;
		m_nGridThreads = Packet.m_nGridThreads;
		m_iGridThread = 0;

		// Set the new scroll sizes
		m_ViewSize.cx = m_cxWidth;
		m_ViewSize.cy = m_cyHeight;

		// Handle the initial condition
		if (m_bViewZoom)
		{
			// Provide a full view of the server with scroll bars
			SetScrollSizes(MM_TEXT,m_ViewSize);
			GetParentFrame()->RecalcLayout();
			ResizeParentToFit();
		}
		else
		{
			// Shrink to fit the server in the client window
			SetScaleToFitSize(m_ViewSize);
			GetParentFrame()->RecalcLayout();
		}

		// Setup the GDI
		SetupGDI();

		// Send the verification
		CPacket Packet2(m_nCompThreads,0);

		// Send the packet
		m_Socket << Packet2;
	}
	else if (Packet.m_ucPacketType == 10)
	{
		// Get the number of compression threads
		m_nCompThreads = Packet.m_nCompThreads;

		if (Packet.m_nSessionId == 0)
		{
			// Get the password for verification
			CString csPassword = GetDocument()->m_csPassword;

			// If the session is set then this verification to the server that the handshaking is over for the thread connection
			CPacket Packet2(csPassword,m_nSessionId);

			// Send the packet
			m_Socket << Packet2;
		}
		else
		{
			// Set the session id for the main connection and then set the timer for making the other connections
			if (!m_nSessionId)
			{
				// Get the session id
				m_nSessionId = Packet.m_nSessionId;

				// Make server connections for each update rectangle thread
				SetTimer(2,0,NULL);
			}
			else
			{
				// Close the document
				GetDocument()->OnCloseDocument();
			}
		}
	}

	// Clean up the packet if it needed to be created
	if (!lParam)
		delete pPacket;

	return 1;
}

// Connection telling us that a connection has been closed
LRESULT CRDVView::OnCloseConn(WPARAM wParam,LPARAM lParam)
{
	if (m_bConnected)
	{
		// Not connected
		m_bConnected = FALSE;

		// Delete the sink threads
		DeleteSinkThreads();

		// Notify
		AfxMessageBox("The connection to the server has been dropped\n");
	}
	else
	{
		// Notify
		AfxMessageBox("The password is incorrect\n");
	}

	// Set the timer to close the document
	SetTimer(1,0,NULL);

	return 1;
}

// Setup the GDI resources
void CRDVView::SetupGDI()
{
	// Initialize the DIB
	m_DIB.Init(m_cxWidth,m_cyHeight,m_nBitCount);

	// Initialize the delta DIB
	m_XOR.Init(m_cxWidth,m_cyHeight,m_nBitCount);
}

// Clean up the GDI resources
void CRDVView::CleanupGDI()
{
	// Cleanup the DIB band
	m_DIB.DeleteFrame();

	// Cleanup the XOR band
	m_XOR.DeleteFrame();
}

// Clean up the refresh sink threads
void CRDVView::DeleteSinkThreads()
{
	// Clean up the refresh threads
	std::vector<CSocketWndSinkThread *>::iterator itThreadPtr;
	for (itThreadPtr = m_vecSinkThreads.begin();itThreadPtr != m_vecSinkThreads.end();++itThreadPtr)
	{
		CSocketWndSinkThread * pSinkThread = *itThreadPtr;
		pSinkThread->PostThreadMessage(WM_ENDTHREAD,0,0);
		if (WaitForSingleObject(pSinkThread->m_hThread,5000) != WAIT_OBJECT_0)
			TerminateThread(pSinkThread->m_hThread,0);
	}

	// Clear the vector of threads
	m_vecSinkThreads.clear();
}

// Handle left button double click
void CRDVView::OnLButtonDblClk(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_LBUTTONDBLCLK,nFlags,Point);
	CScrollView::OnLButtonDblClk(nFlags,Point);
}

void CRDVView::OnLButtonDown(UINT nFlags,CPoint Point)
{
	// Test the keystate
	if (m_bConnected)
		SendMouseMessage(WM_LBUTTONDOWN,nFlags,Point);
	CScrollView::OnLButtonDown(nFlags,Point);
}

void CRDVView::OnLButtonUp(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_LBUTTONUP,nFlags,Point);
	CScrollView::OnLButtonUp(nFlags,Point);
}

void CRDVView::OnMButtonDblClk(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_MBUTTONDBLCLK,nFlags,Point);
	CScrollView::OnMButtonDblClk(nFlags,Point);
}

void CRDVView::OnMButtonDown(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_MBUTTONDOWN,nFlags,Point);
	CScrollView::OnMButtonDown(nFlags,Point);
}

void CRDVView::OnMButtonUp(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_MBUTTONUP,nFlags,Point);
	CScrollView::OnMButtonUp(nFlags,Point);
}

BOOL CRDVView::OnMouseWheel(UINT nFlags,short zDelta,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_MOUSEWHEEL,nFlags,Point,zDelta);
	return CScrollView::OnMouseWheel(nFlags,zDelta,Point);
}

void CRDVView::OnMouseMove(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_MOUSEMOVE,nFlags,Point);
	CScrollView::OnMouseMove(nFlags,Point);
}

void CRDVView::OnRButtonDblClk(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_RBUTTONDBLCLK,nFlags,Point);
	CScrollView::OnRButtonDblClk(nFlags,Point);
}

void CRDVView::OnRButtonDown(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_RBUTTONDOWN,nFlags,Point);
	CScrollView::OnRButtonDown(nFlags,Point);
}

void CRDVView::OnRButtonUp(UINT nFlags,CPoint Point)
{
	if (m_bConnected)
		SendMouseMessage(WM_RBUTTONUP,nFlags,Point);
	CScrollView::OnRButtonUp(nFlags,Point);
}

void CRDVView::OnKeyDown(UINT nChar,UINT nRepCnt,UINT nFlags)
{
	if (m_bConnected)
		if (nChar < 256)
			SendKeyBoardMessage(WM_KEYDOWN,nChar,nRepCnt,nFlags);
	CScrollView::OnKeyDown(nChar,nRepCnt,nFlags);
}

void CRDVView::OnKeyUp(UINT nChar,UINT nRepCnt,UINT nFlags)
{
	if (m_bConnected)
		if (nChar < 256)
			SendKeyBoardMessage(WM_KEYUP,nChar,nRepCnt,nFlags);
	CScrollView::OnKeyUp(nChar,nRepCnt,nFlags);
}

// Send the mouse message
void CRDVView::SendMouseMessage(WORD wWM,UINT nFlags,CPoint Point,short zDelta)
{
	// Transmit the mouse event
	CPoint MousePosition = GetScrollPosition();

	// Test for zooming
	if (!m_bViewZoom)
	{
		// Get the client area
		CSize size,sizeSb;
		GetTrueClientSize(size,sizeSb);
		CRect Client;
		GetClientRect(&Client);

		// Get floating point versions of the coordinates
		double dPointX = (double)Point.x;
		double dPointY = (double)Point.y;
		double dWidth = (double)m_cxWidth;
		double dHeight = (double)m_cyHeight;
		double dSizeX = (double)Client.Width();
		double dSizeY = (double)Client.Height();

		// Adjust mouse coordinate to match the zoom out aspect ratio
		dPointX = dWidth / dSizeX * dPointX + 0.5;
		dPointY = dHeight / dSizeY * dPointY + 0.5;

		// Assign the new mouse coordinate
		Point.x = (LONG)dPointX;
		Point.y = (LONG)dPointY;
	}
	else
	{
		// Factor in the scroll positions
		Point += MousePosition;
	}

	// Test the keystate
	nFlags = 0;
	if (GetKeyState(VK_CONTROL) & 0x8000)
		nFlags |= 1;
	if (GetKeyState(VK_SHIFT) & 0x8000)
		nFlags |= 2;

	// Add the mouse message
	m_vMouseMsg.push_back(CMouseMsg(wWM,nFlags,Point,zDelta));
}

// Send the keyboard message
void CRDVView::SendKeyBoardMessage(WORD wWM,UINT nChar,UINT nRepCnt,UINT nFlags)
{
	// Add the KB message
	m_vKBMsg.push_back(CKBMsg(wWM,nChar,nRepCnt,nFlags));
}

// Handle the cursor
BOOL CRDVView::OnSetCursor(CWnd * pWnd,UINT nHitTest,UINT message)
{
	// Set the cursor
	if (nHitTest != HTCLIENT || (HCURSOR)m_Cursor == NULL)
		return CView::OnSetCursor(pWnd,nHitTest,message);
	SetClassLong(m_hWnd,-12,(LONG)(HCURSOR)m_Cursor);
	SetCursor(m_Cursor);
	return TRUE;
}

// Timer to handle mixing the windows message pump with thread messages
void CRDVView::OnTimer(UINT_PTR nIDEvent) 
{
	// Make sure we don't grow larger than server
	GetDocument()->UpdateAllViews(NULL);

	// Test the timer for different actions
	switch (nIDEvent)
	{
		// Close the document
		case 1:
		{
			// Kill the timer
			KillTimer(nIDEvent);

			// Close the document
			GetDocument()->OnCloseDocument();
			break;
		}

		case 2:
		{
			// Test for being done making the other connectionss
			if (m_iGridThread == m_nGridThreads)
			{
				// Done making connections, now receive data
				KillTimer(2);

				// Set the timer to send KB and Mouse messages
				SetTimer(4,0,NULL);

				// Send the verification to start getting screen updates
				CPacket Packet(m_nCompThreads,m_nSessionId - 1);

				// Send the packet
				m_Socket << Packet;

				// We are connected
				m_bConnected = TRUE;
			}
			else
			{
				// Create a new thread to manage the connection to the server for DIB updates
				CSocketWndSinkThread * pSinkThread = new CSocketWndSinkThread(&m_arrSinkHandles[m_iGridThread++],GetSafeHwnd(),GetDocument()->m_csIp,GetDocument()->m_csPort);

				// Connect to the server
				pSinkThread->PostThreadMessage(WM_CONNECTSERVER,0,0);

				// Track the connection
				m_vecSinkThreads.push_back(pSinkThread);

				// Kill the timer
				KillTimer(2);

				// Set the timer that tests the connection for being made
				SetTimer(3,0,NULL);
			}

			break;
		}

		// Test the connection
		case 3:
		{
			// Test the connection to the server
			CSocketWndSinkThread * pSinkThread = m_vecSinkThreads[m_vecSinkThreads.size() - 1];
			if (pSinkThread->IsConnected())
			{
				// Stop testing the connection to the server
				KillTimer(3);

				// Make the next connection to the server or start getting refreshes
				SetTimer(2,0,NULL);
			}
			break;
		}

		// KB and Mouse Messages
		case 4:
		{
			// Check for KB messages
			if (m_vKBMsg.size())
			{
				// Build a packet from the collection of KB messages
				CPacket Packet(m_vKBMsg);

				// Clear the collection of KB messages
				m_vKBMsg.clear();

				// Send the packet
				m_Socket << Packet;
			}
			
			// Check for mouse messages
			if (m_vMouseMsg.size())
			{
				// Build a packet from the collection of KB messages
				CPacket Packet(m_vMouseMsg);

				// Clear the collection of KB messages
				m_vMouseMsg.clear();

				// Send the packet
				m_Socket << Packet;
			}

			break;
		}

	default:
		break;
	}
}

// Handle sizing
void CRDVView::OnSize(UINT nType,int cx,int cy)
{
	// Call the base class to handle the scrolling
	CScrollView::OnSize(nType,cx,cy);

	// Update the view and make sure the view fits
	GetDocument()->UpdateAllViews(NULL);
}

// Make sure we don't grow larger than the server
void CRDVView::OnUpdate(CView * pSender,LPARAM lHint,CObject * pHint)
{
	if (m_cxWidth && m_cyHeight)
	{
		GetParentFrame()->RecalcLayout();
		ResizeParentToFit();
	}
}

// Toggle the zoom (either menu or toolbar)
void CRDVView::OnViewZoom()
{
	// Toggle the zoom
	m_bViewZoom = !m_bViewZoom;

	// Handle the changed condition
	if (m_bViewZoom)
	{
		// Provide a full view of the server with scroll bars
		SetScrollSizes(MM_TEXT,m_ViewSize);
		GetParentFrame()->RecalcLayout();
		ResizeParentToFit();
	}
	else
	{
		// Shrink to fit the server in the client window
		SetScaleToFitSize(m_ViewSize);
		GetParentFrame()->RecalcLayout();
	}
}

// Toggle the zoom UI (either menu or toolbar)
void CRDVView::OnUpdateViewZoom(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(m_bConnected);
	pCmdUI->SetCheck(m_bConnected && m_bViewZoom);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

About the Author

Andy Bantly
Founder
United States United States
Working as a software developer since 1989. Started out with Basic, FORTRAN and JCL, moved into Visual Basic 1.0, C, then C++, and now I work mainly in C++ using MFC, Win32, and ATL/COM. I use Microsoft Products only because that is what gives me gainful employment. Through work, I have a lot of experience with HTML, JavaScript, XSL transformations, the XMLHTTP object, PHP 4.x, and simple COM object integrations.
 
I've worked for the University of Oklahoma in the school of meteorology (Go SOONERS!), consulting, and now as a Senior Software Engineer. These things keep my lights on and electricity going. My dream job is to own a bowling alley and rub elbows with pro-bowlers! I'm also an avid pedicab driver and have my own cab. I like the hustle of picking up people in downtown and biking them to their destination.
Follow on   Google+   LinkedIn

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 20 Sep 2013
Article Copyright 2000 by Andy Bantly
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid