Click here to Skip to main content
15,886,873 members
Articles / Desktop Programming / MFC

Remote Control PCs

Rate me:
Please Sign up or sign in to vote.
4.60/5 (150 votes)
20 Sep 2013GPL3 885.3K   63.9K   489  
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.

// TCPSocket.cpp : implementation file
// Implementation of AsyncSocket derived class.
// This class only works with versions of windows that recognize the TransmitPackets API function call
//

#include "stdafx.h"
#include "TCPSocket.h"
#include "Packet.h"

CTCPSocket::CTCPSocket() : baseclass(),
	m_bListening(FALSE),m_hWndParent(NULL),TransmitPackets(NULL)
{
}

CTCPSocket::~CTCPSocket()
{
}

// Initialize the transmit packet function pointer
BOOL CTCPSocket::InitTP()
{
	// Create the transmit packet function pointer
	DWORD len;
	static const GUID guid = WSAID_TRANSMITPACKETS;
	if (WSAIoctl(m_hSocket,SIO_GET_EXTENSION_FUNCTION_POINTER,(void*)&guid,sizeof(guid),&TransmitPackets,sizeof(TransmitPackets),&len,NULL,NULL))
		return FALSE;
	return TRUE;
}

// Create a new socket
BOOL CTCPSocket::Create(int nSocketPort)
{
	BOOL bCreate = FALSE;
	if (nSocketPort == 0)
	{
		// Create the outbound connection
		bCreate = baseclass::Create();

		// Receive notifications for connections, reading data, and disconnections
		AsyncSelect(FD_CONNECT | FD_READ | FD_CLOSE);
	}
	else
	{
		// Create the listener socket
		bCreate = baseclass::Create(nSocketPort);

		// Receive notifications of a waiting connection, reading data, and disconnections
		AsyncSelect(FD_ACCEPT | FD_READ | FD_CLOSE);

		// Start listening
		if (bCreate)
			m_bListening = baseclass::Listen(SOMAXCONN);
	}

	// Create the transmit packet function pointer
	if (!InitTP())
		bCreate = FALSE;

	return bCreate;
}

// Set the parent window handle for events to be posted back to the parent with WM_APP messags
void CTCPSocket::SetParent(HWND hWndParent)
{
	m_hWndParent = hWndParent;
}

// Make a connection using a outbound socket
BOOL CTCPSocket::Connect(LPCTSTR lpszHostAddress,UINT nHostPort)
{
	if (!m_bListening)
		return baseclass::Connect(lpszHostAddress,nHostPort);
	return FALSE;
}

// Shutdown the socket
BOOL CTCPSocket::ShutDown(int nHow)
{
	return baseclass::ShutDown(nHow);
}

// Notification of a pending inbound connection
void CTCPSocket::OnAccept(int nErrorCode)
{
	ASSERT(m_bListening == TRUE);
	baseclass::OnAccept(nErrorCode);

	// Send the custom WM_ACCEPTCONN message
	if (m_hWndParent != NULL)
	{
		// Send and wait for processing
		SendMessage(m_hWndParent,WM_ACCEPTCONN,(WPARAM)this,0);
	}
}

// Notification that the connection has been successfully made
void CTCPSocket::OnConnect(int nErrorCode)
{
	baseclass::OnConnect(nErrorCode);

	// Send the custom WM_MAKECONN message
	if (m_hWndParent != NULL)
	{
		// Send and wait for processing
		SendMessage(m_hWndParent,WM_MAKECONN,(WPARAM)this,(LPARAM)nErrorCode);
	}
}

// Notification of readiness to receive data
void CTCPSocket::OnReceive(int nErrorCode)
{
	ASSERT(m_bListening == FALSE);
	baseclass::OnReceive(nErrorCode);

	// Send the custom WM_RECEIVEDATA message
	if (m_hWndParent != NULL)
	{
		// Send and wait for processing
		SendMessage(m_hWndParent,WM_RECEIVEDATA,(WPARAM)this,0);
	}
}

// Notification that the connection has been closed
void CTCPSocket::OnClose(int nErrorCode)
{
	baseclass::OnClose(nErrorCode);

	// Shutdown and close the socket
	ShutDown();
	Close();

	// Send the custom WM_CLOSECONN message
	if (m_hWndParent != NULL)
	{
		// Send and wait for processing
		SendMessage(m_hWndParent,WM_CLOSECONN,(WPARAM)this,0);
	}
}

// Helper for sorting, since order really doesn't matter, use the memory address
bool CTCPSocket::operator < (const CTCPSocket & rhs)
{
	return (DWORD_PTR)this < (DWORD_PTR)(&rhs);
}

// Helper to send a packet of data
CTCPSocket & CTCPSocket::operator << (CPacket & rhs)
{
	// Enable blocking mode and prevent events
	DWORD dwArgument = 0;
	AsyncSelect(dwArgument);
	IOCtl(FIONBIO,&dwArgument);

	// Send the packet
	rhs.SerializePacket(this,true);

	// Disable blocking mode and reinstate events
	dwArgument = FD_CONNECT | FD_READ | FD_CLOSE;
	IOCtl(FIONBIO,&dwArgument);
	AsyncSelect(dwArgument);

	// Return a reference to "this" class for chaining
	return *this;
}

// Helper to receive a packet
CTCPSocket & CTCPSocket::operator >> (CPacket & rhs)
{
	// Enable blocking mode and prevent events
	DWORD dwArgument = 0;
	AsyncSelect(dwArgument);
	IOCtl(FIONBIO,&dwArgument);

	// Receive the packet
	rhs.SerializePacket(this,false);

	// Disable blocking mode and reinstate events
	dwArgument = FD_CONNECT | FD_READ | FD_CLOSE;
	IOCtl(FIONBIO,&dwArgument);
	AsyncSelect(dwArgument);

	// Return a reference to "this" class for chaining
	return *this;
}

// Send the buffer of data
int CTCPSocket::Send(const void * lpBuf,int nBufLen,int nFlags)
{
	// Test for a function pointer to the TransmitPackets function
	if (!TransmitPackets)
		return 0;

	// Create the transmit buffer data structure
	_TRANSMIT_PACKETS_ELEMENT TPE;
	TPE.dwElFlags = TP_ELEMENT_MEMORY;
	TPE.cLength = nBufLen;
	TPE.pBuffer = (PVOID)lpBuf;

	// Transmit the packet
	BOOL bTransmit = TransmitPackets(m_hSocket,&TPE,1,nBufLen,NULL,TF_USE_KERNEL_APC);
	return nBufLen;
}

// Receive the buffer of data
int CTCPSocket::Receive(void * lpBuf,int nBufLen,int nFlags)
{
	int nTotalRecv = 0,nRecvAttempt = 0;
	while (nTotalRecv < nBufLen)
	{
		int nRecv = baseclass::Receive((LPSTR)lpBuf + nTotalRecv,nBufLen - nTotalRecv,nFlags);
		if (nRecv == SOCKET_ERROR)
			return nRecv;
		nTotalRecv += nRecv;

		// Test for a dropped connection during a receive
		if (nRecv == 0)
		{
			nRecvAttempt++;
			Sleep(300);
		}
		if (nRecvAttempt == 10)
			return 0;
	}
	return nTotalRecv;
}

/////////////////////////////////////////////////////////////////////////////////

// Handle the socket notifications as well as the basic initialization
BEGIN_MESSAGE_MAP(CSocketWndSink,CWnd)
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_MESSAGE(WM_MAKECONN, &CSocketWndSink::OnMakeConn)
	ON_MESSAGE(WM_RECEIVEDATA, &CSocketWndSink::OnReceiveData)
	ON_MESSAGE(WM_CLOSECONN, &CSocketWndSink::OnCloseConn)
END_MESSAGE_MAP()

// Constructor
CSocketWndSink::CSocketWndSink(HWND hWndParent) : CWnd(), m_hWndParent(hWndParent), m_bConnected(FALSE)
{
	// Create the window
	if (!CreateEx(0,AfxRegisterWndClass(0),_T("RDVSink"),WS_OVERLAPPED,0,0,0,0,NULL,NULL))
		DebugMsg("%s\n",TEXT("Failed to create a RDV window notification sink"));
}

// Destructor
CSocketWndSink::~CSocketWndSink()
{
	if (m_bConnected)
		ShutDown();
}

// Create the window
int CSocketWndSink::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	MoveWindow(0,0,0,0);
	return 0;
}

// Paint the window
void CSocketWndSink::OnPaint()
{
	CPaintDC dc(this);
}

// Return the connected status
BOOL CSocketWndSink::IsConnected()
{
	return m_bConnected;
}

// Shutdown and close the connection
void CSocketWndSink::ShutDown()
{
	m_Socket.ShutDown();
	m_Socket.Close();
	m_bConnected = FALSE;
}

// Connection has been made
LRESULT CSocketWndSink::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);
	}
	else
	{
		// Set the connected state and connection event
		m_bConnected = TRUE;

#if defined(_DEBUG)
		DebugMsg("Connected to the server on thread %d\n",GetCurrentThreadId());
#endif
	}
	return lParam;
}

// Received data event from the desktop server, now read the data
LRESULT CSocketWndSink::OnReceiveData(WPARAM wParam,LPARAM lParam)
{
	// Receive the data
	CPacket Packet;

	// Receive the image
	m_Socket >> Packet;

	// Send the packet and wait for it to be processed
	::SendMessage(m_hWndParent,WM_RECEIVEDATA,(WPARAM)this,(LPARAM)&Packet);

	return 1;
}

// Connection telling us that a connection has been closed
LRESULT CSocketWndSink::OnCloseConn(WPARAM wParam,LPARAM lParam)
{
	return 1;
}

// Make a connection to the server
void CSocketWndSink::Connect(CString csIp,CString csPort)
{
	// Create the connection oriented socket
	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));
}

/////////////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CSocketWndSinkThread, CWinThread)

CSocketWndSinkThread::CSocketWndSinkThread() throw(...)
{
	throw;
}

// Take the update rectangle
CSocketWndSinkThread::CSocketWndSinkThread(HANDLE * phWork,HWND hWndParent,CString csIp,CString csPort) : 
	m_phWork(phWork), m_hWndParent(hWndParent), m_csIp(csIp), m_csPort(csPort), m_pSocketWnd(NULL), m_bPumpMessage(false)
{
	// Create the initialization signaling event
	m_hPumpMessage = CreateEvent(NULL,FALSE,FALSE,NULL);

	// Create the work signaling event
	HANDLE & hWork = *m_phWork;
	hWork = CreateEvent(NULL,FALSE,FALSE,NULL);

	// Initialize the event
	SetEvent(hWork);

	// Create the thread
	CreateThread();
	SetThreadPriority(THREAD_PRIORITY_TIME_CRITICAL);

	// Wait for the thread to initialize and signal its message pump has started
	WaitForSingleObject(m_hPumpMessage,INFINITE);

	// Close the initialization event
	CloseHandle(m_hPumpMessage);
	m_hPumpMessage = NULL;
}

CSocketWndSinkThread::~CSocketWndSinkThread()
{
	// Close the work signaling event
	HANDLE & hWork = *m_phWork;
	CloseHandle(hWork);
}

BOOL CSocketWndSinkThread::InitInstance()
{
	// Initialize sockets for each thread
	if (!AfxSocketInit())
		return FALSE;

	// Create the window for socket event notifications
	m_pSocketWnd = new CSocketWndSink(m_hWndParent);
	ASSERT(m_pSocketWnd != NULL);

#if defined(_DEBUG)
	DebugMsg("Thread %d ready\n",GetCurrentThreadId());
#endif

	return TRUE;
}

int CSocketWndSinkThread::ExitInstance()
{
	// Delete the socket window event notification window
	if (m_pSocketWnd)
	{
		// Shutdown the connection if it is still open
		if (m_pSocketWnd->IsConnected())
			m_pSocketWnd->ShutDown();

		// Destroy the window
		m_pSocketWnd->DestroyWindow();

		// Delete the object
		delete m_pSocketWnd;
		m_pSocketWnd = NULL;
	}

	// Base class handles normal exit sequence
	return CWinThread::ExitInstance();
}

// Use the message pump as a marker to indicate the thread can accept posted messages
BOOL CSocketWndSinkThread::PumpMessage()
{
	// Test for initialization
	if (!m_bPumpMessage)
	{
		// The message pump is active
		m_bPumpMessage = true;

		// Signal completion of this stage
		SetEvent(m_hPumpMessage);
	}

	// Allow the base class to handle the message pump
	return CWinThread::PumpMessage();
}

// Return the connected status
BOOL CSocketWndSinkThread::IsConnected()
{
	return m_pSocketWnd && m_pSocketWnd->IsConnected();
}

// These functions are called by PostThreadMessage
BEGIN_MESSAGE_MAP(CSocketWndSinkThread, CWinThread)
	ON_THREAD_MESSAGE(WM_CONNECTSERVER, &CSocketWndSinkThread::OnConnectServer)
	ON_THREAD_MESSAGE(WM_ENDTHREAD, &CSocketWndSinkThread::OnEndThread)
END_MESSAGE_MAP()

// Connect to the server
void CSocketWndSinkThread::OnConnectServer(WPARAM wParam,LPARAM lParam)
{
	// Create the connection event
	if (m_pSocketWnd)
		m_pSocketWnd->Connect(m_csIp,m_csPort);
}

void CSocketWndSinkThread::OnEndThread(WPARAM wParam,LPARAM lParam)
{
	// Shutdown and close the connection
	if (IsConnected())
		m_pSocketWnd->ShutDown();

	// End the thread
	PostQuitMessage(0);
}

//////////////////////////////////////////////////////////////////////////////////

// Handle the socket notifications as well as the basic initialization
BEGIN_MESSAGE_MAP(CSocketEvent,CWnd)
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_MESSAGE(WM_RECEIVEDATA, &CSocketEvent::OnReceiveData)
	ON_MESSAGE(WM_CLOSECONN, &CSocketEvent::OnCloseConn)
END_MESSAGE_MAP()

// Constructor
CSocketEvent::CSocketEvent(CWinThread * pThread) : CWnd(), m_pThread(pThread), m_bClosed(FALSE)
{
	// Create the window
	if (!CreateEx(0,AfxRegisterWndClass(0),_T("RDSSink"),WS_OVERLAPPED,0,0,0,0,NULL,NULL))
		DebugMsg("%s\n",TEXT("Failed to create a RDS window notification sink"));
}

// Destructor
CSocketEvent::~CSocketEvent()
{
}

// Return the closed status of the socket
BOOL CSocketEvent::IsClosed()
{
	// A valid socket pointer means it received the closed event notification
	return m_bClosed;
}

// Create the window
int CSocketEvent::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	MoveWindow(0,0,0,0);
	return 0;
}

// Paint the window
void CSocketEvent::OnPaint()
{
	CPaintDC dc(this);
}

// The socket has data to be read
LRESULT CSocketEvent::OnReceiveData(WPARAM wParam,LPARAM lParam)
{
	return 1;
}

// The socket has been closed
LRESULT CSocketEvent::OnCloseConn(WPARAM wParam,LPARAM lParam)
{
	// Update the closed status
	m_bClosed = TRUE;

	// Notify the parent
	m_pThread->PostThreadMessage(WM_CLOSECONN,wParam,lParam);

	return 1;
}

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)


Written By
Founder
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions