Click here to Skip to main content
15,892,517 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 897.2K   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.

// RDSDlg.cpp : implementation file
//

#include "stdafx.h"
#include "..\Common.h"
#include "RefreshThread.h"
#include "afxole.h"
#include "resource.h"

IMPLEMENT_DYNCREATE(CRefreshThread, CWinThread)

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

// Take the update rectangle
CRefreshThread::CRefreshThread(HWND hWndParent,std::vector<CRect> vDIBRect,int nBitCount,BOOL bImgDIB,BOOL bUseCompression,BOOL bAC,DWORD dwThread,int nCompThreads) : 
	m_bPumpMessage(false), m_hWndParent(hWndParent),
	m_vDIBRect(vDIBRect), m_nBitCount(nBitCount), 
	m_bImgDIB(bImgDIB), m_bUseCompression(bUseCompression), m_bAC(bAC),
	m_dwThread(dwThread), m_iCurDIB(0), m_nDIB(0),
	m_nCompThreads(nCompThreads)
{
	// Create the event signaling event
	m_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);

	// Create a DC for the display
	m_hDisplayDC = GetDC(GetDesktopWindow());

	// Get the number of iterations for the thread
	m_nDIB = (int)m_vDIBRect.size();

	// Calculate the basic sleep cycle
	m_dwSleep = (DWORD)(50.0 / (double)m_nDIB + .5);

	// Initialize the DIBs for each iteration of the thread
	for (int iDIB = 0;iDIB < m_nDIB;++iDIB)
	{
		// Create a DIB rectangle
		CRect DIBRect = m_vDIBRect[iDIB];
		int nWidth = DIBRect.Width();
		int nHeight = DIBRect.Height();

		// Create a DIB for a refresh rectangle
		CDIBFrame DIB(nWidth,nHeight,nBitCount);
		m_vDIB.push_back(DIB);
		m_vDIBLast.push_back(DIB);
		m_vComp.push_back(FALSE);
		m_vInit.push_back(FALSE);
		m_vDIBPacket.push_back(CPacket());
	}

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

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

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

CRefreshThread::~CRefreshThread()
{
	// Delete the dc's
	ReleaseDC(GetDesktopWindow(),m_hDisplayDC);

	// Signal the ending
	SetEvent(m_hThread);
}

BOOL CRefreshThread::InitInstance()
{
	// Initialize sockets for each thread
	if (!AfxSocketInit())
	{
		AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
		return FALSE;
	}

	return TRUE;
}

int CRefreshThread::ExitInstance()
{
	// Shutdown the connections
	if (!m_setAccept.empty())
	{
		std::set<CTCPSocket *>::iterator itAccept = m_setAccept.begin();
		for (;itAccept != m_setAccept.end();++itAccept)
		{
			// Close the connection
			CTCPSocket * pAccept = *itAccept;
			OnCloseConn((WPARAM)pAccept,0);
		}
	}

	// 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 CRefreshThread::PumpMessage()
{
	// Test for initialization
	if (!m_bPumpMessage)
	{
		// The message pump is active
		m_bPumpMessage = true;

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

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

// These functions are called by PostThreadMessage
BEGIN_MESSAGE_MAP(CRefreshThread, CWinThread)
	ON_THREAD_MESSAGE(WM_ACCEPTCONN, &CRefreshThread::OnAcceptConn)
	ON_THREAD_MESSAGE(WM_IMAGEREFRESH, &CRefreshThread::OnImageRefresh)
	ON_THREAD_MESSAGE(WM_IMAGESEND, &CRefreshThread::OnImageSend)
	ON_THREAD_MESSAGE(WM_ENDTHREAD, &CRefreshThread::OnEndThread)
	ON_THREAD_MESSAGE(WM_CLOSECONN, &CRefreshThread::OnCloseConn)
END_MESSAGE_MAP()

// Integrate an accepted connection for updates
void CRefreshThread::OnAcceptConn(WPARAM wParam,LPARAM lParam)
{
	// Get the accepted socket that has been passed from the main thread
	SOCKET hAccept = (SOCKET)wParam;

	// Initialize the connection
	CTCPSocket * pAccept = new CTCPSocket();
	CTCPSocket & Accept = *pAccept;

	// Create the socket
	Accept.Create();

	// Attach the accepted connection
	Accept.Attach(hAccept,FD_CONNECT | FD_READ | FD_CLOSE);

	// Create a socket event sink for the accepted connection
	CSocketEvent * pAcceptEvent = new CSocketEvent(this);

	// Set the parent for receiving event notifications
	Accept.SetParent(pAcceptEvent->GetSafeHwnd());

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

	// Reset the sequence (comparison and dib initialization) for this connection
	for (int iDIB = 0;iDIB < m_nDIB;++iDIB)
	{
		m_vComp[iDIB] = FALSE;
		m_vInit[iDIB] = FALSE;
	}

	// Tie the accepted connection to the event sink for the accepted connection
	m_mapAcceptEvent[pAccept] = pAcceptEvent;

	#if defined(_DEBUG)
	DebugMsg("Thread %d Accepted Connection\n",m_dwThread);
	#endif
}

// Test for needing to refresh a DIB rectangle
void CRefreshThread::OnImageRefresh(WPARAM wParam,LPARAM lParam)
{
	// Test for connections
	if (m_setAccept.empty())
	{
		// There are no connections so wait for 1/10 a second
		Sleep(100);

		// Notify parent to start the next sequence
		PostMessage(m_hWndParent,WM_IMAGERENEW,(WPARAM)this,(LPARAM)m_dwThread);
		return;
	}

	// Test for a DIB or Cursor
	if (m_nDIB)
	{
		// Get the DIB for this thread
		CDIBFrame & DIB = m_vDIB[m_iCurDIB];
		CDIBFrame & DIBLast = m_vDIBLast[m_iCurDIB];
		CRect & DIBRect = m_vDIBRect[m_iCurDIB];
		BOOL & bSame = m_vComp[m_iCurDIB];
		BOOL & bInit = m_vInit[m_iCurDIB];
		CPacket & DIBPacket = m_vDIBPacket[m_iCurDIB];

		// Capture the DIB
		BitBlt(DIB,0,0,DIBRect.Width(),DIBRect.Height(),m_hDisplayDC,DIBRect.left,DIBRect.top,SRCCOPY);

		// Initialize the input
		char * pInBuffer = (LPSTR)DIB;
		DWORD dwSrcBytes = DIB.m_dwBkgImageBytes;
		HBITMAP hDIB = DIB;

		// Test for having the initial image already
		if (bInit)
		{
			// Get the last image
			char * pLastBuffer = (LPSTR)DIBLast;
			DWORD dwLastBytes = DIBLast.m_dwBkgImageBytes;

			// Compare the bitmaps
			ASSERT(dwSrcBytes == dwLastBytes);
			bSame = memcmp(pInBuffer,pLastBuffer,min(dwSrcBytes,dwLastBytes)) == 0;
			if (!bSame)
			{
				// XOR the current display into the last display
				BitBlt(DIBLast,0,0,DIBRect.Width(),DIBRect.Height(),DIB,0,0,SRCINVERT);

				// Set the buffer as the next XOR'ed DIB
				pInBuffer = (LPSTR)DIBLast;
				hDIB = DIBLast;
				dwSrcBytes = DIBLast.m_dwBkgImageBytes;
			}
		}
		else
		{
			// Initialize the output buffer
			m_OutBuffer = std::auto_ptr<char>(new char[dwSrcBytes]);
		}

		// Test for a change in the current DIB with the last DIB
		if (!bSame)
		{
			// The output buffer and size
			char * pOutBuffer = NULL;
			DWORD dwOutBytes = 0;

			// JPEG support
			COleStreamFile ImageStream;
			IStream * pImageStream = NULL;
			HGLOBAL hImageBuffer = NULL;

			// Check for GDI+ conversion of the DIB
			if (!m_bImgDIB)
			{
				// Attach the DIB to the Image
				m_ImageDIB.Attach(hDIB);

				// Prepare the Image for memory stream serialization
				if (ImageStream.CreateMemoryStream())
				{
					// Serialize the DIB to a memory stream of a GDI+ image format
					pImageStream = ImageStream.GetStream();
					if (pImageStream && SUCCEEDED(m_ImageDIB.Save(pImageStream,Gdiplus::ImageFormatPNG)))
					{
						// Get the new input buffer size and buffer
						dwSrcBytes = (DWORD)ImageStream.GetLength();

						// Get the input buffer
						if (SUCCEEDED(GetHGlobalFromStream(pImageStream,&hImageBuffer)))
							pInBuffer = (char *)GlobalLock(hImageBuffer);
					}
				}

				// Detach from the DIB
				m_ImageDIB.Detach();
			}

			// Test for compression of the DIB
			if (m_bUseCompression)
			{
				// Set the output buffer
				pOutBuffer = m_OutBuffer.get();

				// Initialize the image information for compression
				CompressionInfo Info;
				Info.m_pInBuffer = pInBuffer;
				Info.m_dwSrcBytes = dwSrcBytes;
				Info.m_ppOutBuffer = &pOutBuffer;
				Info.m_pdwOutBytes = &dwOutBytes;

				// Compress the image in the main thread to auto-throttle the system from doing to many compressions at once
				SendMessage(m_hWndParent,WM_IMAGECOMPRESS,(WPARAM)&Info,0);
			}
			else
			{
				// Leave the DIB uncompressed
				pOutBuffer = pInBuffer;

				// Initialize the output bytes
				dwOutBytes = dwSrcBytes;
			}

			// Build an image packet
			DIBPacket = CPacket(DIBRect,pOutBuffer,dwOutBytes,dwSrcBytes,m_nCompThreads,m_bImgDIB,m_bUseCompression,m_bAC,bInit,m_bImgDIB);

			// Notify parent that an image is ready to be sent
			PostMessage(m_hWndParent,WM_IMAGEREFRESH,(WPARAM)this,(LPARAM)&DIBPacket);

			// Update the last DIB
			BitBlt(DIBLast,0,0,DIBRect.Width(),DIBRect.Height(),DIB,0,0,SRCCOPY);

			// Test for GDI+ cleanup
			if (!m_bImgDIB)
			{
				// Clean up the GDI+ image
				if (pInBuffer)
					GlobalUnlock(hImageBuffer);

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

			// The DIB is now a delta of the current and last DIB's
			bInit = TRUE;
		}

		// Update the current DIB
		m_iCurDIB++;
		if (m_iCurDIB == m_nDIB)
		{
			// Reset the count
			m_iCurDIB = 0;
		}

		// If nothing changed then signal the work as completed
		if (bSame)
		{
			// Sleep for a moment
			Sleep(m_dwSleep);

			// Notify parent to start the next sequence
			PostMessage(m_hWndParent,WM_IMAGERENEW,(WPARAM)this,(LPARAM)m_dwThread);
		}
	}
	else
	{
		// Test the cursor for a change
		bool bChange = false;

		// Get the cursor information
		CURSORINFO CursorInfo;
		CursorInfo.cbSize = sizeof(CURSORINFO);
		if (GetCursorInfo(&CursorInfo) && CursorInfo.hCursor)
		{
			// Get the icon information from the cursor handle
			ICONINFO IconInfo;
			if (GetIconInfo(CursorInfo.hCursor,&IconInfo))
			{
				// Get the cursor information flags
				DWORD dwFlags = CursorInfo.flags;

				// Get the hotspot
				DWORD dwXHotSpot = IconInfo.xHotspot;
				DWORD dwYHotSpot = IconInfo.yHotspot;

				// The width and height of the cursor
				int bmWidth = 0;
				int bmHeight = 0;

				// The planes and bits per pixel
				WORD bmMaskPlanes = 0,bmMaskBitsPixel = 0;
				WORD bmColorPlanes = 0,bmColorBitsPixel = 0;

				// Storage for the bitmap bits of the icon
				std::vector<BYTE> MaskBits,ColorBits;

				// Test for a bitmap handle
				bool bMask = false;
				if (IconInfo.hbmMask)
				{
					// Get the bitmap information from the handle
					BITMAP Mask;
					GetObject(IconInfo.hbmMask,sizeof(BITMAP),&Mask);

					// Get the width and height
					bmWidth = Mask.bmWidth;
					bmHeight = Mask.bmHeight;

					// Get the planes and bits per pixel
					bmMaskPlanes = Mask.bmPlanes;
					bmMaskBitsPixel = Mask.bmBitsPixel;

					// Get the bits of the temporary mask bitmap object
					DWORD dwMaskBytes = Mask.bmWidthBytes * Mask.bmHeight;
					MaskBits.resize(dwMaskBytes,0);
					GetBitmapBits(IconInfo.hbmMask,dwMaskBytes,&MaskBits[0]);

					// Free the bitmap
					DeleteObject(IconInfo.hbmMask);
					bMask = true;
				}

				// Test for a bitmap handle
				bool bColor = false;
				if (IconInfo.hbmColor)
				{
					// Get the bitmap information from the handle
					BITMAP Color;
					GetObject(IconInfo.hbmColor,sizeof(BITMAP),&Color);

					// Get the planes and bits per pixel
					bmColorPlanes = Color.bmPlanes;
					bmColorBitsPixel = Color.bmBitsPixel;

					// Get the bits of the temporary color bitmap object
					DWORD dwColorBytes = Color.bmWidthBytes * Color.bmHeight;
					ColorBits.resize(dwColorBytes,0);
					GetBitmapBits(IconInfo.hbmColor,dwColorBytes,&ColorBits[0]);

					// Free the bitmap
					DeleteObject(IconInfo.hbmColor);
					bColor = true;
				}

				// Test for a change in the cursor
				if (bMask && MaskBits != m_Cursor.m_MaskBits)
					bChange = true;
				if (bColor && !bChange && ColorBits != m_Cursor.m_ColorBits)
					bChange = true;

				// Test for sending a cursor
				if (bChange)
				{
					#if defined(_DEBUG)
					DebugMsg("Cursor changed\n");
					#endif

					// Update the last cursor data
					m_Cursor = CPacket(dwFlags,dwXHotSpot,dwYHotSpot,bmWidth,bmHeight,bmMaskPlanes,bmMaskBitsPixel,bmColorPlanes,bmColorBitsPixel,MaskBits,ColorBits);

					// Notify parent that a cursor is ready to be sent
					PostMessage(m_hWndParent,WM_IMAGEREFRESH,(WPARAM)this,(LPARAM)&m_Cursor);
				}
				else
				{
					// Sleep
					Sleep(100);

					// Notify parent to start the next sequence
					PostMessage(m_hWndParent,WM_IMAGERENEW,(WPARAM)this,(LPARAM)m_dwThread);
				}
			}
		}
	}
}

// Send the image to all connected clients
void CRefreshThread::OnImageSend(WPARAM wParam,LPARAM lParam)
{
	// Get the DIB packet to send
	CPacket * pDIBPacket = (CPacket *)wParam;
	CPacket & DIBPacket = *pDIBPacket;

	#if defined(_DEBUG)
	if (m_nDIB)
		DebugMsg("Rect: (%d,%d)-(%d,%d) Uncompressed: %d Compressed: %d\n",DIBPacket.m_Rect.left,DIBPacket.m_Rect.top,DIBPacket.m_Rect.right,DIBPacket.m_Rect.bottom,DIBPacket.m_dwSrcBytes,DIBPacket.m_dwBytes);
	#endif

	// Send the update to all connected clients
	bool bSent = false;
	std::set<CTCPSocket *>::iterator itAccept = m_setAccept.begin();
	for (;itAccept != m_setAccept.end();++itAccept)
	{
		// Get the connection
		CTCPSocket * pAccept = *itAccept;
		CTCPSocket & Accept = *pAccept;

		// Look up the event handler for the socket
		std::map<CTCPSocket *,CSocketEvent *>::iterator itAcceptEvent = m_mapAcceptEvent.find(pAccept);
		if (itAcceptEvent != m_mapAcceptEvent.end())
		{
			// Get the event handler for the socket
			CSocketEvent * pAcceptEvent = itAcceptEvent->second;
			if (!pAcceptEvent->IsClosed())
			{
				// Send the update to the open connection
				Accept << DIBPacket;
				bSent = true;
			}
		}
	}

	// If an image was sent then renew the sequence
	if (bSent)
	{
		// Sleep for a moment
		Sleep(m_dwSleep);

		// Notify parent to start the next sequence
		PostMessage(m_hWndParent,WM_IMAGERENEW,(WPARAM)this,(LPARAM)m_dwThread);
	}
}

// The client closed the connection closed
void CRefreshThread::OnCloseConn(WPARAM wParam,LPARAM lParam)
{
	// Get the socket
	CTCPSocket * pAccept = (CTCPSocket *)wParam;

	// Look up the event handler for the socket
	std::map<CTCPSocket *,CSocketEvent *>::iterator itAcceptEvent;
	itAcceptEvent = m_mapAcceptEvent.find(pAccept);
	if (itAcceptEvent != m_mapAcceptEvent.end())
	{
		// Get the event handler for the socket
		CSocketEvent * pAcceptEvent = itAcceptEvent->second;
		ASSERT(pAcceptEvent->IsClosed());

		// Cleanup event notification
		pAcceptEvent->DestroyWindow();
		delete pAcceptEvent;
		m_mapAcceptEvent.erase(itAcceptEvent);
	}

	// Find the connection for removal
	std::set<CTCPSocket *>::iterator itAccept = m_setAccept.find(pAccept);
	if (itAccept != m_setAccept.end())
	{
		// Shutdown and close the connection
		pAccept->ShutDown();
		pAccept->Close();

		// Delete the connection
		delete pAccept;

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

void CRefreshThread::OnEndThread(WPARAM wParam,LPARAM lParam)
{
	// End the thread
	PostQuitMessage(0);
}

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