The Remote Desktop Viewer Lite is a system to remotely monitor desktop activity. This system is designed to view a remote desktop's activity.
It is composed of three Visual C++ .NET 2003 projects
- Quick Compress - A compression COM object.
Quick Compress is a project that I have previously uploaded. I will briefly sum it up here. It is an ATL/COM component that performs Huffman compression and run length encoding. Both methods are written directly in assembler for optimized performance.
RemoteDesktopService and RemoteDesktop both call on it to compress and uncompress the data.
- RemoteDesktopService - A Win32 console application that creates an installable service. The service, when running, accepts TCP/IP connections and then streams the desktop out to the connection.
If it weren't for MSDN and sample code, I wouldn't have been able to have a rapid turn around on the development of the service. Please refer to the article “Creating a Simple Win32 Service in C++” by Nigel Thompson. It is located in the section DLLs, Processes, and Threads in Technical Articles. I used this program as the foundation for my service.
A Windows registration file fine tunes the behavior of the service. It controls the socket connectivity parameters, the bit depth of the display, the port to allow connections, and an optional password required to make a successful connection. If no registration file is installed, the program uses intelligent defaults, except it will use true color because it looks the best.
The socket connectivity parameters should not be adjusted unless you really know what you are doing. Here is a breakdown of their behavior:
- “MaxBlock” - The number of times to block for “MinWait” microseconds on a socket for data.
- “MinWait” - The number of microseconds to block for data on a socket. Blocking implies a slice of time where the socket is queried for its status. The status can indicate many states including lost connectivity and data present on the port for reading. These last two are uses I make of blocking in the code.
- “MaxPacket” - The maximum attempted send size of a data packet. In most cases the real amount of data sent will be less, but the attempted send size will always be transmitted. The default setting is 65 Kbytes.
- “BitCount” - The bit depth of the captured display.
BitCount can only be the following values; changing it to another value is undefined behavior and usually causes a failure in the GDI system.
- 4 – a 16 color gray scale palette.
- 8 – a 256 color gray scale palette (best economical setting).
- 16 – 2^16 colors.
- 24 – 2^24 colors.
- 32 – 2^32 colors (true color).
- The server port which listens for incoming connections and password.
- 'ServerPort' – Should be in the range of 1025 to 65535. The default is 8370 in honor of my birth date.
- 'Password' – The plain text password that allows clients a connection.
This service runs using the following design:
- The main thread of execution listens on the port for new connections.
- New connections are then handled in a separate thread of execution.
- Each thread is a “desktop” entity composed of GDI components.
- When the thread's connection to the client (the accepted socket) detects a lost connection, it ends.
- In this way, multiple clients can simultaneously view the machine.
- RemoteDesktop – A Win32 MFC SDI application that displays the remote desktop in the SDI document and allows scrolling around the document.
Choosing this type of a project for this application did not come lightly. When I was a brand new Windows 95 programmer using C and the Win32 API solely, I would have balked at such a monstrosity. Back then I wanted to write the boiler plate code because learning Windows programming requires getting your hands dirty. Well, many years have passed since then and my zeal is about sharing the important part of my code with you.
This is a pretty simple SDI application. It derives from
CScrollView because of the wonder that class provides for scrolling around the document. So much for handling
WM_VSCROLL. Oh well, in the name of progress I suppose this is OK.
To connect to the server, use the 'New Connection'. In case of an error in connection, no real message is displayed. However if you are in debug mode you will see an error message in the debug output.
To disconnect from the server use the 'Close Connection'.
The service itself is its own installer and uninstaller.
- To install the service issue the following command:
- To uninstall the service issue the following command:
Data flows from the client to the server and back.
- Client initiates a connection to the server on the server IP or name. DNS or host entries are required for name resolution.
- Client sends the password, or 1 byte null character for no password.
- Server receives the connection and spawns a thread for handling the connection.
- Server sends the bit depth, the width, and the height of its desktop.
- Client receives the bit depth, the width, and the height.
- Server captures and sends the desktop to the client.
- Client receives the desktop and acknowledges receipt.
- Repeat steps 6-7 until the client closes the connection.
- Server ends the thread that handles the client's connection.
We will see how data compression is integrated into the data flow.
If the server simply sent the desktop every time it changed, that wouldn't be very optimal at all. A few techniques have been added to get the size down.
- The server starts with two empty DIB buffers.
- Buffer 1 is the current desktop.
- Buffer 2 is the last desktop.
- The current desktop is captured and XOR'ed with the last desktop.
This step masks out all the common bits of each desktop.
- The XOR'ed desktop is run length encoded to take advantage of high encodeability of the masked out bits.
- The resultant desktop is then Huffman compressed to achieve minimum redundancy of the data.
Please refer to my other article for a breakdown of the compression and encoding techniques.
- The desktop is sent to the client.
- The client Huffman decompresses.
- The client run length decodes.
- The client XOR's with the last desktop.
The key to keeping the data is the initial second buffer, or the “last desktop”. The code uses a DIB that is filled with the color black in both the client and the server applications.
For fun you can fool around with the raster codes of these original buffers. You may get some strange kaleidoscopic effects. Just put on your favorite classic rock and trance with it.
The socket class: there is a custom, home grown socket class that handles all the communication from the client and server. Who doesn't like writing their own socket classes? It's a rite of passage as far as I am concerned. It is a robust enough class that is decently lightweight and does the trick. It has solid error detection and graceful behaviors when connectivity is lost or can't be established. It simply focuses on the Berkeley subset of socket functions available on Win32 API. I am going to leave it to the reader to break down this class.
Debug versus Release modes
Debug mode contains informative output for most of the system. Using the debug viewer from SysInternals is a good way to see what is going on. Another way is to run the program in the debugger and pay attention to the 'Output' tab. In the service you will have to attach to the process to get the same thing. Services can't be run like other programs but they can be debugged when you attach to them. Be prepared for Visual Studio to crash here from time to time. It sometimes has problems enumerating the processes. I've sent 20 error reports to Microsoft when I was deep in the development of this. Lucky for me that the program creates its own report and asked me if I wanted to send it on. I enjoyed spamming Microsoft with these reports.
Building and Running the System
- Extract the RDVL.zip file to a folder location that suits you best.
The zip contains all the source code and projects.
- Load up RDVL.sln into your Visual C++ .NET 2003 Studio.
- Rebuild the entire solution.
- After building the solution for the first time, the service will have to be installed and manually started before it will serve up desktops.
- The Services Control Panel applet of the Administrative Tools can be used to do this.
- The 'net start' DOS command can also be used to do this.
- The ATL/COM object builds and registers itself to the machine.
If you redistribute the application make sure to put the QuickCompress.dll in the application path.
- You won't have to register it on an enduser machine.
- The code does a smart register and unregister of this component when the client or service starts up. As long as the DLL is in the application path you will be OK.
- Run the client and connect to 'localhost', this resolves to 127.0.0.1, and if you see your desktop appearing in the client window then all is working as it should.
The following chart shows some typical compression ratios using a 1280 x 960 resolution display. In debug mode the actual amount of data that is sent for each desktop is sent to the debugger output.
- 32 bpp
- First frame is about 4 MB, compression brings it down to 1 MB.
- Second frame, with no visible change, will drop down to about 8 KB.
- 8 bpp
- First frame is about 100 KB after compression.
- Second frame, with no visible change, will drop down to about 1 KB.
What is next?
(You wondered why I called it the lite version.)
- Integrate the controlling component into the application.
- Not send duplicate screens is a nice and easy optimization.
- Multiple socket connections to the clients.
Each socket represents a grid of the screen and is responsible for sending only its data. This concept helps leverage the power of your networking to handle more than one data stream at a time.
I've contemplated on a screen divided into 4 areas ought to deliver the best performance and compression.
- Mirror Driver
Ahh.. the real deal here. This is where I am ultimately headed. I have the Windows DDK which describes how to build a graphics mirror driver.
- A mirror driver simply echoes all graphical operations to the mirror display.
- Integrating with a mirror driver, the key behind VNC, is what really delivers performance.
Balancing this type of graphical refresh with my method ought to really deliver a punch and make this program a real contender to VNC.