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.

// RDSDlg.cpp : implementation file
//

#include "stdafx.h"
#include "RDS.h"
#include "RDSDlg.h"

#include "..\Common.h"
#include "..\TCPSocket.h"
#include "..\Packet.h"
#include "..\Registry.h"
#include "afxole.h"
#import "msxml3.dll"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// Use a HTTP request to a well known server that echo's back the public IP address
void GetPublicIP(CString & csIP);

// CRDSDlg dialog
CRDSDlg::CRDSDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CRDSDlg::IDD, pParent), m_TrayIcon(IDR_MAINFRAME),
	m_csPassword(_T("pass")), m_iPort(8370), m_nBitCount(32), 
	m_bUseCompression(TRUE), m_bAC(FALSE), m_bZLib(TRUE), m_pMTC(NULL),
	m_nCompThreads(0), m_cxWidth(0), m_cyHeight(0), m_dxWidthScalar(0), m_dyHeightScalar(0),
	m_iXScale(1), m_iYScale(1), m_nGridThreads(1), m_nIncoming(0), m_bAcceptUpdate(FALSE),
	m_bColors(TRUE), m_bBW(FALSE), m_bLBW(FALSE), m_bImgDIB(FALSE), m_bImgPNG(TRUE),
	m_nSessionId((UINT)-1), m_dwEnableLUA(0), m_bStarted(FALSE)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	// Get the band resolution
	m_cxStart = GetSystemMetrics(SM_XVIRTUALSCREEN);
	m_cyStart = GetSystemMetrics(SM_YVIRTUALSCREEN);
	m_cxWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
	m_cyHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);

	// Set the number of threads
	CPU cpu;
	m_nCompThreads = cpu.GetNbProcs();

	// Temporarily disable UAC
	CRegistry Registry(TRUE,FALSE);
	if (Registry.Open("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"))
	{
		if (!Registry.Read("EnableLUA",m_dwEnableLUA) || m_dwEnableLUA != 0)
			Registry.Write("EnableLUA",(DWORD)0);
	}
}

CRDSDlg::~CRDSDlg()
{
	// Restore the UAC
	if (m_dwEnableLUA != 0)
	{
		CRegistry Registry(TRUE,FALSE);
		if (Registry.Open("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"))
			Registry.Write("EnableLUA",m_dwEnableLUA);
	}
}

void CRDSDlg::OnOK()
{
	EndDialog(IDOK);
}

void CRDSDlg::EnableControls(BOOL bOffline)
{
	// Update the controls
	m_UseCompression.EnableWindow(bOffline);
	m_AC.EnableWindow(bOffline && m_bUseCompression);
	m_ZLib.EnableWindow(bOffline && m_bUseCompression);
	m_ACThreads.EnableWindow(bOffline && m_bUseCompression);
	m_StaticACThreads.EnableWindow(bOffline && m_bUseCompression);
	m_Start.EnableWindow(bOffline);
	m_Stop.EnableWindow(!bOffline);
	m_StaticPassword.EnableWindow(bOffline);
	m_StaticPort.EnableWindow(bOffline);
	m_Password.EnableWindow(bOffline);
	m_Port.EnableWindow(bOffline);
	m_BW.EnableWindow(bOffline);
	m_LBW.EnableWindow(bOffline);
	m_Colors.EnableWindow(bOffline);
	m_ImgDIB.EnableWindow(bOffline);
	m_ImgPNG.EnableWindow(bOffline);
	m_StaticXScale.EnableWindow(bOffline);
	m_XScale.EnableWindow(bOffline);
	m_StaticYScale.EnableWindow(bOffline);
	m_YScale.EnableWindow(bOffline);
	m_StaticGridThreads.EnableWindow(bOffline);
	m_GridThreads.EnableWindow(bOffline);
}

void CRDSDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_PWD, m_Password);
	DDX_Control(pDX, IDC_PORT, m_Port);
	DDX_Control(pDX, IDC_START, m_Start);
	DDX_Control(pDX, IDC_STOP, m_Stop);
	DDX_Control(pDX, IDC_STATIC_PWD, m_StaticPassword);
	DDX_Control(pDX, IDC_STATIC_PORT, m_StaticPort);
	DDX_Control(pDX, IDC_RGB, m_Colors);
	DDX_Control(pDX, IDC_BW, m_BW);
	DDX_Control(pDX, IDC_LBW, m_LBW);
	DDX_Control(pDX, IDC_IMGDIB, m_ImgDIB);
	DDX_Control(pDX, IDC_IMGPNG, m_ImgPNG);
	DDX_Control(pDX, IDC_COMPRESSION, m_UseCompression);
	DDX_Control(pDX, IDC_AC, m_AC);
	DDX_Control(pDX, IDC_ZLIB, m_ZLib);
	DDX_Control(pDX, IDC_ACTHREADS, m_ACThreads);
	DDX_Control(pDX, IDC_STATIC_ACTHREADS, m_StaticACThreads);
	DDX_Control(pDX, IDC_XSCALE, m_XScale);
	DDX_Control(pDX, IDC_STATIC_XSCALE, m_StaticXScale);
	DDX_Control(pDX, IDC_YSCALE, m_YScale);
	DDX_Control(pDX, IDC_STATIC_YSCALE, m_StaticYScale);
	DDX_Control(pDX, IDC_STATIC_GRIDTHREADS, m_StaticGridThreads);
	DDX_Control(pDX, IDC_GRIDTHREADS, m_GridThreads);

	DDX_Text(pDX, IDC_PWD, m_csPassword);
	DDV_MaxChars(pDX, m_csPassword, 255);
	DDX_Text(pDX, IDC_PORT, m_iPort);
	DDV_MinMaxInt(pDX, m_iPort, 1025, 65535);
	DDX_Check(pDX, IDC_RGB, m_bColors);
	DDX_Check(pDX, IDC_BW, m_bBW);
	DDX_Check(pDX, IDC_LBW, m_bLBW);
	DDX_Check(pDX, IDC_IMGDIB, m_bImgDIB);
	DDX_Check(pDX, IDC_IMGPNG, m_bImgPNG);
	DDX_Check(pDX, IDC_COMPRESSION, m_bUseCompression);
	DDX_Check(pDX, IDC_AC, m_bAC);
	DDX_Check(pDX, IDC_ZLIB, m_bZLib);
	DDX_Text(pDX, IDC_ACTHREADS, m_nCompThreads);
	DDV_MinMaxInt(pDX, m_nCompThreads, 0, 4);
	DDX_Text(pDX, IDC_XSCALE, m_iXScale);
	DDV_MinMaxInt(pDX, m_iXScale, 1, 4);
	DDX_Text(pDX, IDC_YSCALE, m_iYScale);
	DDV_MinMaxInt(pDX, m_iYScale, 1, 4);
	DDX_Text(pDX, IDC_GRIDTHREADS, m_nGridThreads);
	DDV_MinMaxInt(pDX, m_nGridThreads, 1, min(m_iXScale * m_iYScale,MAXTHREADS));
}

BEGIN_MESSAGE_MAP(CRDSDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_MESSAGE(WM_ACCEPTCONN, &CRDSDlg::OnAcceptConn)
	ON_MESSAGE(WM_RECEIVEDATA, &CRDSDlg::OnReceiveData)
	ON_MESSAGE(WM_CLOSECONN, &CRDSDlg::OnCloseConn)
	ON_MESSAGE(WM_NOTIFY_TRAY, &CRDSDlg::OnNotifyTray)
	ON_COMMAND(ID_RDS_RESTORE, &CRDSDlg::OnRestore)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_START, &CRDSDlg::OnStart)
	ON_BN_CLICKED(IDC_STOP, &CRDSDlg::OnStop)
	ON_BN_CLICKED(IDC_COMPRESSION, &CRDSDlg::OnCompression)
	ON_BN_CLICKED(IDC_AC, &CRDSDlg::OnCompressionChoice)
	ON_BN_CLICKED(IDC_ZLIB, &CRDSDlg::OnCompressionChoice)
	ON_BN_CLICKED(IDC_BW, &CRDSDlg::OnBW)
	ON_BN_CLICKED(IDC_LBW, &CRDSDlg::OnLBW)
	ON_WM_TIMER()
	ON_MESSAGE(WM_IMAGECOMPRESS, &CRDSDlg::OnImageCompress)
	ON_MESSAGE(WM_IMAGEREFRESH, &CRDSDlg::OnImageRefresh)
	ON_MESSAGE(WM_IMAGERENEW, &CRDSDlg::OnImageRenew)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

// CRDSDlg message handlers
BOOL CRDSDlg::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

	// Enable the controls
	EnableControls();

	// Set up tray icon
	m_TrayIcon.SetNotificationWnd(this,WM_NOTIFY_TRAY);
	m_TrayIcon.SetIcon(IDR_MAINFRAME);

	// Get the IP address of the host
	CString csIP;
	GetPublicIP(csIP);

	// Set the dialog title
	CString csTitle;
	csTitle.Format("Desktop Server: %s",csIP);
	SetWindowText(csTitle);

	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 CRDSDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<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 function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CRDSDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Operator telling us that there is a pending connection to accept
LRESULT CRDSDlg::OnAcceptConn(WPARAM wParam,LPARAM lParam)
{
	// If this is a main connection then stop the timers and refresh threads until the connection is established
	if (!m_bAcceptUpdate)
	{
		// Allow a new connection
		CTCPSocket * pAccept = new CTCPSocket;
		CTCPSocket & Accept = *pAccept;

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

		// Accept the remote connection
		m_Operator.Accept(Accept);
		if (!Accept.InitTP())
			return 0;

		// Store the connection
		m_setAccept.insert(pAccept);

		// Send the number of compression threads
		CPacket Packet(m_nCompThreads,0);
		Accept << Packet;

		// Test for limiting connections to the server
		if (m_setAccept.size() >= 64)
		{
			// Don't accept anymore connections
			m_Operator.AsyncSelect(FD_READ | FD_CLOSE);
		}
	}
	else
	{
		// Allow a new connection that will be served screen updates
		CTCPSocket * pAccept = new CTCPSocket;
		CTCPSocket & Accept = *pAccept;

		// Accept the remote connection
		m_Operator.Accept(Accept);
		if (!Accept.InitTP())
			return 0;

		// Get the refresh thread that will handle this incoming connection
		size_t iPos = m_vRefreshThread.size() - m_nIncoming;
		CRefreshThread * pRefreshThread = m_vRefreshThread[iPos];

		// Assign the socket
		pRefreshThread->PostThreadMessage(WM_ACCEPTCONN,(WPARAM)pAccept->Detach(),(LPARAM)iPos);
		delete pAccept;

		// Update the incoming connections left to process
		m_nIncoming--;

		// Update the accept switch
		m_bAcceptUpdate = m_nIncoming == 0 ? FALSE : TRUE;
	}

	return 1;
}

// Connection telling us that there is pending data to receive
LRESULT CRDSDlg::OnReceiveData(WPARAM wParam,LPARAM lParam)
{
	// Get the connection
	CTCPSocket * pAccept = (CTCPSocket *)wParam;
	CTCPSocket & Accept = *pAccept;

	// Receive a packet of data
	CPacket Packet;
	Accept >> Packet;

	// Process the packet
	if (Packet.m_ucPacketType == 4)
	{
		// Get the collection of KB messages
		std::vector<CKBMsg> & vKBMsg = Packet.m_vKBMsg;

		// Get the count of messages
		UINT nKBMsg = (UINT)vKBMsg.size();

		// Issue each command
		for (UINT iKBMsg = 0;iKBMsg < nKBMsg;++iKBMsg)
		{
			// Get the command
			CKBMsg & KBMsg = vKBMsg[iKBMsg];

			// Execute the command
			SetKBMessage(KBMsg.m_wWM,KBMsg.m_nChar,KBMsg.m_nRepCnt,KBMsg.m_nFlags);
		}
	}
	else if (Packet.m_ucPacketType == 5)
	{
		// Get the collection of mouse messages
		std::vector<CMouseMsg> & vMouseMsg = Packet.m_vMouseMsg;

		// Get the count of messages
		UINT nMouseMsg = (UINT)vMouseMsg.size();

		// Issue each command
		for (UINT iMouseMsg = 0;iMouseMsg < nMouseMsg;++iMouseMsg)
		{
			// Get the command
			CMouseMsg & MouseMsg = vMouseMsg[iMouseMsg];

			// Execute the mouse command
			SetMouseMessage(MouseMsg.m_wWM,MouseMsg.m_MousePosition,MouseMsg.m_nFlags,MouseMsg.m_zDelta);
		}
	}
	else if (Packet.m_ucPacketType == 1)
	{
		// Reset the incoming connections
		m_nIncoming = 0;
		m_bAcceptUpdate = FALSE;

		// Create the verification packet
		CPacket Packet2(m_csPassword,0);

		// Test the submitted password hash
		if (Packet.m_csPasswordHash != Packet2.m_csPasswordHash)
		{
			// Close the connection
			Accept.Close();

			// Clean up and remove the closed connection
			std::set<CTCPSocket *>::iterator itAccept = m_setAccept.find(pAccept);
			if (itAccept != m_setAccept.end())
			{
				// Clean up the closed connection
				CTCPSocket * pAccept = *itAccept;
				delete pAccept;

				// Remove the closed connection
				m_setAccept.erase(itAccept);
			}
			return 0;
		}
		else
		{
			// Build the display attribute packet
			CPacket Packet(m_cxWidth,m_cyHeight,m_nBitCount,m_nThreads);

			// Send the packet
			Accept << Packet;
		}
	}
	else if (Packet.m_ucPacketType == 10)
	{
		if (Packet.m_nCompThreads != m_nCompThreads)
		{
			// Close the connection
			Accept.Close();

			// Clean up and remove the closed connection
			std::set<CTCPSocket *>::iterator itAccept = m_setAccept.find(pAccept);
			if (itAccept != m_setAccept.end())
			{
				// Clean up the closed connection
				CTCPSocket * pAccept = *itAccept;
				delete pAccept;

				// Remove the closed connection
				m_setAccept.erase(itAccept);
			}
			return 0;
		}
		else if (Packet.m_nSessionId == m_nSessionId - 1)
		{
			// Signifies the completion of the handshaking of the main connection
#if defined(_DEBUG)
			DebugMsg("Session Id: %d\n",Packet.m_nSessionId);
#endif
		}
		else
		{
			// Prepare for connections (Grid threads + cursor thread)
			m_bAcceptUpdate = TRUE;
			m_nIncoming = m_nThreads;

			// Build the final verification packet
			--m_nSessionId;
			CPacket Packet2(m_nCompThreads,m_nSessionId);

			// Send the packet
			Accept << Packet2;
		}
	}
	else
		return 0;
	return 1;
}

// Execute the mouse command
void CRDSDlg::SetMouseMessage(WORD wMM,CPoint MousePosition,UINT nFlags,short zDelta)
{
	// Read the mouse packet
	int mx = (int)((double)MousePosition.x * m_dxWidthScalar);
	int my = (int)((double)MousePosition.y * m_dyHeightScalar);

	#if defined(_DEBUG)
	DebugMsg("MM=%d (%d,%d)\n",wMM,mx,my);
	#endif

	// The time stamp of the operation in milli-seconds
	DWORD dwTime = 0;

	// Extract the flags
	bool bCtrl = nFlags & 1 ? true : false;
	bool bShift = nFlags & 2 ? true : false;

	// Set the number of commands in the input
	int nInputs = 0;

	// Test for the virtual keys
	if (bCtrl)
		nInputs += 2;
	if (bShift)
		nInputs += 2;

	// For the loop termination
	int nHalf = nInputs / 2;

	// Test for the mouse message
	nInputs += 1;
	if (wMM == WM_LBUTTONDBLCLK || wMM == WM_MBUTTONDBLCLK || wMM == WM_RBUTTONDBLCLK)
		nInputs += 3;

	// Create the maximum number of user inputs
	std::auto_ptr<INPUT> UserInput(new INPUT[nInputs]);
	INPUT * pUserInput = UserInput.get();

	// Set the starting input
	int nStart = 0;

	// Set the virtual keydown(s) for the mouse message
	if (bCtrl)
	{
		INPUT & KeyBoardInput = *(pUserInput + (nStart++));
		SetMouseKB(KeyBoardInput,VK_CONTROL,true);
	}
	if (bShift)
	{
		INPUT & KeyBoardInput = *(pUserInput + (nStart++));
		SetMouseKB(KeyBoardInput,VK_SHIFT,true);
	}

	// Build up the mouse input
	int iInput = nStart;
	for (;iInput < (nInputs - nHalf);iInput++,dwTime += 100)
	{
		// Get the mouse input
		INPUT & MouseInput = pUserInput[iInput];

		// Set the input type
		MouseInput.type = INPUT_MOUSE;

		// Get the mouse input structure
		MOUSEINPUT & Mouse = MouseInput.mi;

		// Set the absolute coordinates
		Mouse.dx = mx;
		Mouse.dy = my;

		// Set the mouse data
		Mouse.mouseData = zDelta;

		// Set the flags
		Mouse.dwFlags = MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_ABSOLUTE;

		// Set the button(s)
		if (wMM == WM_LBUTTONDBLCLK)
		{
			if (iInput == 0 || iInput == 2)
				Mouse.dwFlags |= MOUSEEVENTF_LEFTDOWN;
			else if (iInput == 1 || iInput == 3)
				Mouse.dwFlags |= MOUSEEVENTF_LEFTUP;
		}
		else if (wMM == WM_LBUTTONDOWN)
			Mouse.dwFlags |= MOUSEEVENTF_LEFTDOWN;
		else if (wMM == WM_LBUTTONUP)
			Mouse.dwFlags |= MOUSEEVENTF_LEFTUP;
		else if (wMM == WM_MBUTTONDBLCLK)
		{
			if (iInput == 0 || iInput == 2)
				Mouse.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
			else if (iInput == 1 || iInput == 3)
				Mouse.dwFlags |= MOUSEEVENTF_MIDDLEUP;
		}
		else if (wMM == WM_MBUTTONDOWN)
			Mouse.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
		else if (wMM == WM_MBUTTONUP)
			Mouse.dwFlags |= MOUSEEVENTF_MIDDLEUP;
		else if (wMM == WM_MOUSEWHEEL)
			Mouse.dwFlags |= MOUSEEVENTF_WHEEL;
		else if (wMM == WM_MOUSEMOVE)
			Mouse.dwFlags |= MOUSEEVENTF_MOVE;
		else if (wMM == WM_RBUTTONDBLCLK)
		{
			if (iInput == 0 || iInput == 2)
				Mouse.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
			else if (iInput == 1 || iInput == 3)
				Mouse.dwFlags |= MOUSEEVENTF_RIGHTUP;
		}
		else if (wMM == WM_RBUTTONDOWN)
			Mouse.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
		else if (wMM == WM_RBUTTONUP)
			Mouse.dwFlags |= MOUSEEVENTF_RIGHTUP;

		// Set the time
		Mouse.time = dwTime;
	}

	// Set the new starting place
	nStart = iInput;

	// Set the virtual keydown(s) for the mouse message
	if (bCtrl)
	{
		INPUT & KeyBoardInput = *(pUserInput + (nStart++));
		SetMouseKB(KeyBoardInput,VK_CONTROL,false);
	}
	if (bShift)
	{
		INPUT & KeyBoardInput = *(pUserInput + (nStart++));
		SetMouseKB(KeyBoardInput,VK_SHIFT,false);
	}

	// Send the user inputs for the mouse
	SendInput(nInputs,pUserInput,sizeof(INPUT));
}

// Execute the keyboard command
void CRDSDlg::SetKBMessage(WORD wMM,UINT nChar,UINT nRepCnt,UINT nFlags)
{
	// Create the maximum number of user inputs
	std::auto_ptr<INPUT> UserInput(new INPUT[nRepCnt]);
	INPUT * pUserInput = UserInput.get();

	// Build up the keyboard input
	for (UINT iInput = 0;iInput < nRepCnt;++iInput)
	{
		// Get the keyboard input
		INPUT & KeyBoardInput = pUserInput[iInput];

		// Set the input type
		KeyBoardInput.type = INPUT_KEYBOARD;

		// Get the keyboard input structure
		KEYBDINPUT & KeyBoard = KeyBoardInput.ki;

		// Set the scan code
		KeyBoard.wScan = 0;

		// Set the ignored fields
		KeyBoard.dwExtraInfo = 0;
		KeyBoard.time = 0;

		// Test for the key being pressed or released
		KeyBoard.dwFlags = wMM == WM_KEYDOWN ? 0 : KEYEVENTF_KEYUP;

		// Set the character
		KeyBoard.wVk = nChar;
	}

	// Send the user input for the keyboard
	SendInput(nRepCnt,pUserInput,sizeof(INPUT));
}

// Set the virtual keycode for the mouse message
void CRDSDlg::SetMouseKB(INPUT & KeyBoardInput,WORD wVk,bool bDown)
{
	// Set the input type
	KeyBoardInput.type = INPUT_KEYBOARD;

	// Get the keyboard input structure
	KEYBDINPUT & KeyBoard = KeyBoardInput.ki;

	// Set the scan code
	KeyBoard.wScan = 0;

	// Set the ignored fields
	KeyBoard.dwExtraInfo = 0;
	KeyBoard.time = 0;

	// Test for the key being pressed or released
	KeyBoard.dwFlags = bDown ? 0 : KEYEVENTF_KEYUP;

	// Set the virtual key
	KeyBoard.wVk = wVk;
}

// Refresh Thread event signaling to compress a screen update
LRESULT CRDSDlg::OnImageCompress(WPARAM wParam,LPARAM lParam) 
{
	// Get the image information structure for compression
	CompressionInfo & Info = *(CompressionInfo *)wParam;
	char * pInBuffer = Info.m_pInBuffer;
	DWORD dwSrcBytes = Info.m_dwSrcBytes;
	char * & pOutBuffer = *(Info.m_ppOutBuffer);
	DWORD & dwOutBytes = *(Info.m_pdwOutBytes);

	// Test for the multi-threaded compressor
	BOOL bMTC = m_nCompThreads ? TRUE : FALSE;

	// The temporary output buffer
	char * pTempBuffer = NULL;

	// Test for MT/ST compression
	if (bMTC)
	{
		// Set the type of encoder to the multi-threaded compressor
		m_pMTC->SetEncoder(m_bAC);

		// Last parameter determines either Multithreaded arithmentic encoding or zlib
		m_pMTC->SetBuffer(pInBuffer,dwSrcBytes,TRUE);
		m_pMTC->Encode();

		// Get the temporary output buffer
		m_pMTC->GetBuffer(&pTempBuffer,&dwOutBytes,TRUE);
	}
	else
	{
		// Test for the compression type
		if (m_bAC)
		{
			// Single-threaded arithmetic encoding
			m_AE.SetBuffer(pInBuffer,dwSrcBytes);
			m_AE.EncodeBuffer();
			m_AE.GetBuffer(pTempBuffer,dwOutBytes);
		}
		else
		{
			// Single-threaded ZLIB encoding
			m_ZL.SetBuffer(pInBuffer,dwSrcBytes,TRUE);
			m_ZL.EncodeBuffer();
			m_ZL.GetBuffer(pTempBuffer,dwOutBytes);
		}
	}

	// Copy to the output buffer
	memcpy(pOutBuffer,pTempBuffer,dwOutBytes);

	return 1;
}

// Refresh thread event signaling that a screen update was sent
LRESULT CRDSDlg::OnImageRefresh(WPARAM wParam,LPARAM lParam) 
{
	// Only send if the system is in a running state
	if (m_bStarted)
	{
		// Get the thread
		CRefreshThread * pRefreshThread = (CRefreshThread *)wParam;

		// Get the image packet
		CPacket * pDIBPacket = (CPacket *)lParam;

		// Queue the image for sending
		pRefreshThread->PostThreadMessage(WM_IMAGESEND,(WPARAM)pDIBPacket,0);
	}
	return 1;
}

// Refresh thread event signaling to start the next sequence
LRESULT CRDSDlg::OnImageRenew(WPARAM wParam,LPARAM lParam) 
{
	// Only send if the system is in a running state
	if (m_bStarted)
	{
		// Get the thread
		CRefreshThread * pRefreshThread = (CRefreshThread *)wParam;

		// Queue the image for sending
		pRefreshThread->PostThreadMessage(WM_IMAGEREFRESH,0,0);
	}
	return 1;
}

// Client connection has been closed
LRESULT CRDSDlg::OnCloseConn(WPARAM wParam,LPARAM lParam)
{
	// Get the connection
	CTCPSocket * pAccept = (CTCPSocket *)wParam;

	// Clean up and remove the closed connection
	std::set<CTCPSocket *>::iterator itAccept = m_setAccept.find(pAccept);
	if (itAccept != m_setAccept.end())
	{
		// Clean up the closed connection
		CTCPSocket * pAccept = *itAccept;
		delete pAccept;

		// Remove the closed connection
		m_setAccept.erase(itAccept);
	}

	// Test for limiting connections to the server
	if (m_setAccept.size() < 64)
	{
		// Accept connections again
		m_Operator.AsyncSelect(FD_ACCEPT | FD_READ | FD_CLOSE);
	}

	return 1;
}

// Handle notification from tray icon
LRESULT CRDSDlg::OnNotifyTray(WPARAM uID,LPARAM lEvent)
{
	// Let tray icon do default stuff
	return m_TrayIcon.OnNotifyTray(uID,lEvent);
}

// Hide the window for close notifications
void CRDSDlg::OnClose()
{
	ShowWindow(SW_HIDE);
}

// Show the window for the "restore" menu item
void CRDSDlg::OnRestore()
{
	ShowWindow(SW_SHOW);
}

// Start the listener
void CRDSDlg::OnStart()
{
	// Update the UI entries
	BOOL bUpdate = UpdateData();
	if (!bUpdate)
		return;

	// Set the started status
	m_bStarted = TRUE;

	// Disable the UI
	EnableControls(FALSE);

	// Get the bit count
	m_nBitCount = m_bColors ? 32 : (!m_bLBW ? 8 : 4);

	// Get the scalars for converting mouse coordinates
	m_dxWidthScalar = 65535.0 / (double)m_cxWidth;
	m_dyHeightScalar = 65535.0 / (double)m_cyHeight;

	// Create a listener
	m_Operator.Create(m_iPort);
	m_Operator.SetParent(GetSafeHwnd());

	// Create the threads to test for screen updates
	CreateRefreshThreads();

	// Hide the dialog
	OnClose();
}

// Stop the listener
void CRDSDlg::OnStop()
{
	// Set the started status
	m_bStarted = FALSE;

	// Clean up the refresh threads
	DeleteRefreshThreads();

	// Remove the inactive connection
	std::set<CTCPSocket *>::iterator itAccept;
	for (itAccept = m_setAccept.begin();itAccept != m_setAccept.end();++itAccept)
	{
		// Get the connection
		CTCPSocket * pAccept = *itAccept;
		CTCPSocket & Accept = *pAccept;

		// Shutdown and close the connection
		Accept.ShutDown();
		Accept.Close();

		// Clean up the connection
		delete pAccept;
	}

	// Remove all the closed connections
	m_setAccept.clear();

	// Shutdown and close the operator
	m_Operator.ShutDown();
	m_Operator.Close();

	// Enable the controls
	EnableControls();
}

// Compression UI
void CRDSDlg::OnCompression()
{
	// Update the IU entries
	UpdateData();

	// Enable/disable the compression choices
	m_AC.EnableWindow(m_bUseCompression);
	m_ZLib.EnableWindow(m_bUseCompression);

	// Enable/disable the AC threads
	m_StaticACThreads.EnableWindow(m_bUseCompression);
	m_ACThreads.EnableWindow(m_bUseCompression);
}

void CRDSDlg::OnCompressionChoice()
{
	// Update the IU entries
	UpdateData();

	// Enable/disable the AC threads
	m_StaticACThreads.EnableWindow(m_bUseCompression);
	m_ACThreads.EnableWindow(m_bUseCompression);
}

// Black and White compresses better as a DIB than PNG
void CRDSDlg::OnBW()
{
	m_bBW = TRUE;
	m_bColors = FALSE;
	m_bImgDIB = TRUE;
	m_bImgPNG = FALSE;
	UpdateData(FALSE);
}

// Low bandwidth is Black and White at 4BPP
void CRDSDlg::OnLBW()
{
	// Disable the controls for low bandwidth
	m_BW.EnableWindow(m_bLBW);
	m_Colors.EnableWindow(m_bLBW);
	m_ImgDIB.EnableWindow(m_bLBW);
	m_ImgPNG.EnableWindow(m_bLBW);
	m_StaticACThreads.EnableWindow(m_bLBW);
	m_ACThreads.EnableWindow(m_bLBW);

	// Set the control values
	m_bBW = TRUE;
	m_bColors = FALSE;
	m_bImgDIB = TRUE;
	m_bImgPNG = FALSE;
	m_bLBW = !m_bLBW;

	// Such a low amount of data that better compression should be achieved in 1 thread
	m_nCompThreads = 0;

	// Update the controls based on the new values
	UpdateData(FALSE);
}

// Create the refresh threads
void CRDSDlg::CreateRefreshThreads()
{
	// Create the multithreaded compressor
	if (m_nCompThreads)
		m_pMTC = new CDriveMultiThreadedCompression(m_nCompThreads);

	// Create the rectangular comparison region
	CPoint TopLeft(m_cxStart,m_cyStart);
	CPoint BottomRight(m_cxWidth / m_iXScale + m_cxStart,m_cyHeight / m_iYScale + m_cyStart);
	CRect DIBRect = CRect(TopLeft,BottomRight);

	// Calculate how many areas are updated per thread
	int nGrids = m_iXScale * m_iYScale;
	int nGridsPerThread = nGrids / m_nGridThreads;

	// Test for an uneven amount (some threads do more work than others...)
	if (nGridsPerThread * m_nGridThreads != nGrids)
		nGridsPerThread++;

	// Build the update areas per thread
	bool bThreads = true;
	for (int iThread = 0;iThread < m_nGridThreads;++iThread)
	{
		// Build the collection of rectangular regions that one thread processes
		std::vector<CRect> vDIBRect;
		for (int iGridsPerThread = 0;iGridsPerThread < nGridsPerThread;++iGridsPerThread)
		{
			// There could be an odd number of regions in the last thread
			if (bThreads)
			{
				// Debugging
				TopLeft = DIBRect.TopLeft();
				BottomRight = DIBRect.BottomRight();
				DebugMsg("Thread=%d Rect=(%d,%d)-(%d,%d)\n",iThread,TopLeft.x,TopLeft.y,BottomRight.x,BottomRight.y);
				
				// Store the rectangle
				vDIBRect.push_back(DIBRect);

				// Update the rectangle
				DIBRect.OffsetRect(DIBRect.Width(),0);

				// Boundary test
				if (DIBRect.left == (m_cxWidth + m_cxStart))
				{
					// Reset the boundary
					DIBRect.left = m_cxStart;
					DIBRect.right = m_cxWidth / m_iXScale + m_cxStart;

					// Update the rectangle
					DIBRect.OffsetRect(0,DIBRect.Height());

					// Boundary test
					if (DIBRect.top == (m_cyHeight + m_cyStart))
						bThreads = false;
				}
			}
		}

		if (!vDIBRect.empty())
		{
			// Create the refresh thread
			m_vRefreshThread.push_back(new CRefreshThread(GetSafeHwnd(),vDIBRect,m_nBitCount,m_bImgDIB,m_bUseCompression,m_bAC,iThread,m_nCompThreads));
		}
	}

	// Create the cursor thread
	m_vRefreshThread.push_back(new CRefreshThread(GetSafeHwnd(),std::vector<CRect>(),0,FALSE,FALSE,FALSE,0,0));

	// Count the threads
	m_nThreads = (DWORD)m_vRefreshThread.size();

	// Start the first sequence 
	std::vector<CRefreshThread *>::iterator itThread = m_vRefreshThread.begin();
	for (;itThread != m_vRefreshThread.end();++itThread)
	{
		CRefreshThread * pRefreshThread = *itThread;
		pRefreshThread->PostThreadMessage(WM_IMAGEREFRESH,0,0);
	}
}

// Clean up the refresh threads
void CRDSDlg::DeleteRefreshThreads()
{
	// Clean up the refresh threads
	std::vector<CRefreshThread *>::iterator itThreadPtr;
	for (itThreadPtr = m_vRefreshThread.begin();itThreadPtr != m_vRefreshThread.end();++itThreadPtr)
	{
		CRefreshThread * pRefreshThread = *itThreadPtr;
		pRefreshThread->PostThreadMessage(WM_ENDTHREAD,0,0);
		DWORD dwWait = WaitForSingleObject(pRefreshThread->m_hThread,5000);
		if (dwWait != WAIT_OBJECT_0)
			TerminateThread(pRefreshThread->m_hThread,1);
	}

	// Clear the vector of threads
	m_vRefreshThread.clear();

	// Clean up the multi-threaded compressor
	if (m_pMTC)
	{
		// Delete the multithreaded compressor
		delete m_pMTC;
		m_pMTC = NULL;
	}
}

// Use a HTTP request to a well known server that echo's back the public IP address
void GetPublicIP(CString & csIP) 
{
	// Initialize COM
	bool bInit = false;
	if (SUCCEEDED(CoInitialize(NULL)))
	{
		// COM was initialized
		bInit = true;

		// Create a HTTP request object
		MSXML2::IXMLHTTPRequestPtr HTTPRequest;
		HRESULT hr = HTTPRequest.CreateInstance("MSXML2.XMLHTTP");
		if (SUCCEEDED(hr))
		{
			// Build a request to a web site that returns the public IP address
			VARIANT Async;
			Async.vt = VT_BOOL;
			Async.boolVal = VARIANT_FALSE;
			CComBSTR ccbRequest = L"http://whatismyipaddress.com/";

			// Open the request
			if (SUCCEEDED(HTTPRequest->raw_open(L"GET",ccbRequest,Async)))
			{
				// Send the request
				if (SUCCEEDED(HTTPRequest->raw_send()))
				{
					// Get the response
					CString csRequest = HTTPRequest->GetresponseText();

					// Parse the IP address
					CString csMarker = "<!-- contact us before using a script to get your IP address -->";
					int iPos = csRequest.Find(csMarker);
					if (iPos != -1)
					{
						iPos += csMarker.GetLength();
						int iPos2 = csRequest.Find(csMarker,iPos);
						if (iPos2 != -1)
						{
							// Build the IP address
							int nCount = iPos2 - iPos;
							csIP = csRequest.Mid(iPos,nCount);
						}
					}
				}
			}
		}
	}

	// Unitialize COM
	if (bInit)
		CoUninitialize();
}

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)

Share

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
Web01 | 2.8.140814.1 | Last Updated 20 Sep 2013
Article Copyright 2000 by Andy Bantly
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid