Create a Remote Desktop Viewer using C# and WCF
I have wanted to create some code that utilizes a Windows Communication Foundation service for quite some time. This blog will introduce a “remote desktop” service. The purpose is to create something that may have a use later and at the same time keep it simple enough to be a learning platform. In no way do I envision this being a viable remote desktop competitor against Microsoft Remote Desktop, VNC, or the many other mature technologies. The goal of the project is to be able to use a client (WinForm in this case) to remotely view the desktop (and maybe later control) of a computer hosting the WCF service.
Capturing Desktop Activity
Capturing the activity on the desktop is fairly straight-forward. However, if you want to optimize the performance then you must use some tricks. A key performance metric for remote desktop applications is remote screen refresh rate. Two areas that I will concentrate on to increase refresh rates are:
- The algorithm used to generate the screen capture must be fast.
- The amount of data shuttled across the network must be minimized.
The following class diagram shows my screen capture class:
This class exposes two public methods: Cursor and Screen. I want to be able to capture both the desktop surface and the mouse as it moves over the desktop. I have separated the methods to capture this data. It seems reasonable that I would be able to get a better user experience by refreshing the cursor faster and the screen a bit slower. So this class allows me to run different refresh rates for those two.
Here is the code for capturing the cursor and the screen:
public Bitmap Screen(ref Rectangle bounds) { // Capture a new screenshot. // _newBitmap = CaptureScreen.CaptureDesktop(); // If we have a previous screenshot, only send back // a subset that is the minimum rectangular area // that encompasses all the changed pixels. // if (_prevBitmap != null) { // Get the bounding box. // bounds = GetBoundingBoxForChanges(); if (bounds == Rectangle.Empty) { // Nothing has changed. // return null; } // Get the minimum rectangular area // Bitmap diff = new Bitmap(bounds.Width, bounds.Height); Graphics g = Graphics.FromImage(diff); g.DrawImage(_newBitmap, 0, 0, bounds, GraphicsUnit.Pixel); g.Dispose(); // Set the current bitmap as the previous to prepare // for the next screen capture. // _prevBitmap = _newBitmap; return diff; } // We don't have a previous screen capture. Therefore // we need to send back the whole screen this time. // else { // Set the previous bitmap to the current to prepare // for the next screen capture. // _prevBitmap = _newBitmap; // Create a bounding rectangle. // bounds = new Rectangle(0, 0, _newBitmap.Width, _newBitmap.Height); return _newBitmap; } } public Bitmap Cursor(ref int cursorX, ref int cursorY) { if (_newBitmap == null) { return null; } else { Bitmap img = CaptureScreen.CaptureCursor(ref cursorX, ref cursorY); if (img!=null && cursorX < _newBitmap.Width && cursorY < _newBitmap.Height) { return img; } else { return null; } } }
First I must tell you that I am using Rashid Mahmood’s code to capture the actual bitmaps of the screen and cursor. Rashid’s code can be found here. He provides a wrappers around a number of WIN32 API. For performance, I want to be as close to the Operating System as possible on these calls to minimize the number of wrappers. All the static methods in the CaptureScreen class are provided by Rashid. Please check out the link for more information.
To minimize the amount of network traffic, I only want to return data when pixels have changed. To accomplish this the Screen method keeps the previously captured screenshot for comparison. It then utilizes a “GetBoundingBoxForChanges” method to determine the minimal rectangle that encompasses all the changed pixels. This rectangle is then used to generate a smaller bitmap that is a portion of the full screen for transfer over the wire. I searched for WIN32 API to get this bounding box, but did not find one.
The “GetBoundingBoxForChanges” routine is shown below. It is fairly well commented. The algorithm uses a few tricks to increase performance.
private Rectangle GetBoundingBoxForChanges() { // The search algorithm starts by looking // for the top and left bounds. The search // starts in the upper-left corner and scans // left to right and then top to bottom. It uses // an adaptive approach on the pixels it // searches. Another pass is looks for the // lower and right bounds. The search starts // in the lower-right corner and scans right // to left and then bottom to top. Again, an // adaptive approach on the search area is used. // // Note: The GetPixel member of the Bitmap class // is too slow for this purpose. This is a good // case of using unsafe code to access pointers // to increase the speed. // // Validate the images are the same shape and type. // if (_prevBitmap.Width != _newBitmap.Width || _prevBitmap.Height != _newBitmap.Height || _prevBitmap.PixelFormat != _newBitmap.PixelFormat) { // Not the same shape...can't do the search. // return Rectangle.Empty; } // Init the search parameters. // int width = _newBitmap.Width; int height = _newBitmap.Height; int left = width; int right = 0; int top = height; int bottom = 0; BitmapData bmNewData = null; BitmapData bmPrevData = null; try { // Lock the bits into memory. // bmNewData = _newBitmap.LockBits( new Rectangle(0, 0, _newBitmap.Width, _newBitmap.Height), ImageLockMode.ReadOnly, _newBitmap.PixelFormat); bmPrevData = _prevBitmap.LockBits( new Rectangle(0, 0, _prevBitmap.Width, _prevBitmap.Height), ImageLockMode.ReadOnly, _prevBitmap.PixelFormat); // The images are ARGB (4 bytes) // int numBytesPerPixel = 4; // Get the number of integers (4 bytes) in each row // of the image. // int strideNew = bmNewData.Stride / numBytesPerPixel; int stridePrev = bmPrevData.Stride / numBytesPerPixel; // Get a pointer to the first pixel. // // Note: Another speed up implemented is that I don't // need the ARGB elements. I am only trying to detect // change. So this algorithm reads the 4 bytes as an // integer and compares the two numbers. // System.IntPtr scanNew0 = bmNewData.Scan0; System.IntPtr scanPrev0 = bmPrevData.Scan0; // Enter the unsafe code. // unsafe { // Cast the safe pointers into unsafe pointers. // int* pNew = (int*)(void*)scanNew0; int* pPrev = (int*)(void*)scanPrev0; // First Pass - Find the left and top bounds // of the minimum bounding rectangle. Adapt the // number of pixels scanned from left to right so // we only scan up to the current bound. We also // initialize the bottom & right. This helps optimize // the second pass. // // For all rows of pixels (top to bottom) // for (int y = 0; y < _newBitmap.Height; ++y) { // For pixels up to the current bound (left to right) // for (int x = 0; x < left; ++x) { // Use pointer arithmetic to index the // next pixel in this row. // if ((pNew + x)[0] != (pPrev + x)[0]) { // Found a change. // if (x < left) { left = x; } if (x > right) { right = x; } if (y < top) { top = y; } if (y > bottom) { bottom = y; } } } // Move the pointers to the next row. // pNew += strideNew; pPrev += stridePrev; } // If we did not find any changed pixels // then no need to do a second pass. // if (left != width) { // Second Pass - The first pass found at // least one different pixel and has set // the left & top bounds. In addition, the // right & bottom bounds have been initialized. // Adapt the number of pixels scanned from right // to left so we only scan up to the current bound. // In addition, there is no need to scan past // the top bound. // // Set the pointers to the first element of the // bottom row. // pNew = (int*)(void*)scanNew0; pPrev = (int*)(void*)scanPrev0; pNew += (_newBitmap.Height - 1) * strideNew; pPrev += (_prevBitmap.Height - 1) * stridePrev; // For each row (bottom to top) // for (int y = _newBitmap.Height - 1; y > top; y--) { // For each column (right to left) // for (int x = _newBitmap.Width - 1; x > right; x--) { // Use pointer arithmetic to index the // next pixel in this row. // if ((pNew + x)[0] != (pPrev + x)[0]) { // Found a change. // if (x > right) { right = x; } if (y > bottom) { bottom = y; } } } // Move up one row. // pNew -= strideNew; pPrev -= stridePrev; } } } } catch (Exception ex) { int xxx = 0; } finally { // Unlock the bits of the image. // if (bmNewData != null) { _newBitmap.UnlockBits(bmNewData); } if (bmPrevData != null) { _prevBitmap.UnlockBits(bmPrevData); } } // Validate we found a bounding box. If not // return an empty rectangle. // int diffImgWidth = right - left + 1; int diffImgHeight = bottom - top + 1; if (diffImgHeight < 0 || diffImgWidth < 0) { // Nothing changed return Rectangle.Empty; } // Return the bounding box. // return new Rectangle(left, top, diffImgWidth, diffImgHeight); }
The first trick is to not use the “GetPixel” method that is available on the Bitmap class. This method is too slow for our purposes. Instead we access the bitmap data using pointers. This requires a bit of “unsafe” code. Each pixel has 4 bytes that provide the red, green, blue, and alpha channel values (RGBA). The algorithm reads all 4 bytes at once by using int pointers.
The algorithm uses a two pass approach for determining the bounding box. The first pass searches from the top to the bottom while scanning from left to right for changed pixels. The number of pixels scanned is adapted as we go to optimize the search time. The result of the first pass is that we now know the top & left boundaries and have initialized the bottom and right boundaries. The second pass searches from bottom to the top while scanning from right to left for changed pixels. Again we adapt the scan width as we go. After this pass we know the bottom and right boundaries.
I am sure there are better ways to achieve a faster algorithm and minimize the amount of data. If you have suggestions, please leave a comment.
WCF Server & Host
To create the WCF server, I first created the service contract. The following is a simple contract that provides update of desktop activity. Again, I separated the screen and cursor updates with the intent on having different refresh rates.
[ServiceContract(SessionMode=SessionMode.Required)] public interface IRemoteDesktop { [OperationContract] byte[] UpdateScreenImage(); [OperationContract] byte[] UpdateCursorImage(); }
The implementation of this service is shown by the following code:
public class RemoteDesktopService : IRemoteDesktop { // An instance of the screen capture class. // private ScreenCapture capture = new ScreenCapture(); /// <summary> /// Capture the screen image and return bytes. /// </summary> /// <returns>4 ints [top,bot,left,right] (16 bytes) + image data bytes</returns> public byte[] UpdateScreenImage() { // Capture minimally sized image that encompasses // all the changed pixels. // Rectangle bounds = new Rectangle(); Bitmap img = capture.Screen(ref bounds); if (img != null) { // Something changed. // byte[] result = Utils.PackScreenCaptureData(img, bounds); // Log to the console. // Console.WriteLine(DateTime.Now.ToString() + " Screen Capture - {0} bytes", result.Length); return result; } else { // Nothing changed. // // Log to the console. Console.WriteLine(DateTime.Now.ToString() + " Screen Capture - {0} bytes", 0); return null; } } /// <summary> /// Capture the cursor data. /// </summary> /// <returns>2 ints [x,y] (8 bytes) + image bytes</returns> public byte[] UpdateCursorImage() { // Get the cursor bitmap. // int cursorX = 0; int cursorY = 0; Bitmap img = capture.Cursor(ref cursorX, ref cursorY); if (img != null) { // Something changed. // byte[] result = Utils.PackCursorCaptureData(img, cursorX, cursorY); // Log to the console. // Console.WriteLine(DateTime.Now.ToString() + " Cursor Capture - {0} bytes", result.Length); return result; } else { // Nothing changed. // // Log to the console. // Console.WriteLine(DateTime.Now.ToString() + " Cursor Capture - {0} bytes", 0); return null; } } }
Notice that the service methods are returning a byte array. I selected this format because I wanted to pack data into a structure before sending and then unpack on the other side. This packing is using the static Utils class methods. This class needs to be cleaned up a bit. For now it serves as a holder for methods that I know will be re-used. The following code shows one of the packing methods.
public static byte[] PackScreenCaptureData(Image image, Rectangle bounds) { // Pack the image data into a byte stream to // be transferred over the wire. // // Get the bytes of the image data. // Note: We are using JPEG compression. // byte[] imgData; using (MemoryStream ms = new MemoryStream()) { image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); imgData = ms.ToArray(); } // Get the bytes that describe the bounding // rectangle. // byte[] topData = BitConverter.GetBytes(bounds.Top); byte[] botData = BitConverter.GetBytes(bounds.Bottom); byte[] leftData = BitConverter.GetBytes(bounds.Left); byte[] rightData = BitConverter.GetBytes(bounds.Right); // Create the final byte stream. // Note: We are streaming back both the bounding // rectangle and the image data. // int sizeOfInt = topData.Length; byte[] result = new byte[imgData.Length + 4 * sizeOfInt]; Array.Copy(topData, 0, result, 0, topData.Length); Array.Copy(botData, 0, result, sizeOfInt, botData.Length); Array.Copy(leftData, 0, result, 2 * sizeOfInt, leftData.Length); Array.Copy(rightData, 0, result, 3 * sizeOfInt, rightData.Length); Array.Copy(imgData, 0, result, 4 * sizeOfInt, imgData.Length); return result; }
Pretty straightforward. Notice that I am using JPEG compression on what is left of the image before packing it into the byte array. There are similar routines that unpack the data on the other side. There may be a better way to do this marshalling. If you have ideas, please let me know.
For now I am hosting the WCF service in a console application. The code for the host is simple enough:
[STAThread()] static void Main(string[] args) { string myHost = System.Net.Dns.GetHostName(); string myIp = System.Net.Dns.GetHostEntry(myHost).AddressList[1].ToString(); Uri baseAddress = new Uri("http://" + myIp + ":8080/Rlc/RemoteDesktop"); Console.WriteLine("WCF Remote Desktop Server"); Console.WriteLine("========================="); Console.WriteLine(); Console.WriteLine("Initializing server endpoint..."); Console.WriteLine("Listening on: " + baseAddress.ToString()); Console.WriteLine(); ServiceHost myServiceHost = new ServiceHost(typeof(RemoteDesktopService), baseAddress); myServiceHost.Open(); Console.ReadLine(); if (myServiceHost.State != CommunicationState.Closed) { myServiceHost.Close(); } }
When you start the host you see the following:
WinForm Client
For the client, I created a simple WinForms application. In the Visual Studio designer it looks like the following:
The dashed rectangle is a picture box. The two text boxes at the bottom are the refresh rate in milliseconds for the screen and cursor. The apply button allows the refresh rates to be updated after the application has been started. The two elements that you cannot see are two timers that are used to trigger the updates for the screen and the cursor.
The first step in connecting to the WCF service is to create a service reference. You just need to fire up the service and let the WSDL to the work. You will then have a proxy for accessing the service methods we defined earlier.
The following code is the implementation for the triggers for the two timers:
private void timer1_Tick(object sender, EventArgs e) { byte[] data = svc.UpdateScreenImage(); if (data != null) { // Update the current screen. // Utils.UpdateScreen(ref _screen, data); // Update the UI. // ShowImage(); } else { // screen has not changed } } private void cursorTimer_Tick(object sender, EventArgs e) { byte[] data = svc.UpdateCursorImage(); if (data != null) { // Unpack the data. // Utils.UnpackCursorCaptureData(data, out _cursor, out _cursorX, out _cursorY); } else { _cursor = null; } // Update the UI. // ShowImage(); }
Notice each method uses the WCF service proxy (svc) to remotely call the service method. Then the returned data is unpacked using another method in the “Utils” class. Finally the UI is updated to show the new data. During this update, the screen image and the cursor image are merged together.
The following shows a screenshot of the client running. Notice that it is capturing itself over and over.
Results
The results so far are decent. When there is not activity on the remote machine, the server does not transfer any data to the client. This is shown below in the server console window.
During activity the number of bytes transferred adapts to the number of pixels changed.
As can be seen in the screens above, the refresh rates range from 2-10 frames per second depending on the number of pixels changed. Take these numbers with some skepticism, since these test were run on my internal network (100MB/s).
I have some more road to travel with this concept before I post the project. I will also try to post a video of the desktop viewer at some point. If you have any suggestions on improvements, please leave a comment.
Thanks for the article! I’m looking to do something similar between window and an iPad. Was wondering if their have been any updates to this project since you posted this article?
Thanks again.
John
please give a link to download this the project’s code “Create a Remote Desktop Viewer using C# and WCF”
Thanks for stopping by. The WCF implementation can be downloaded at the top of this page: http://blog.bobcravens.com/2009/05/multiple-remote-desktop-viewer-c-wcf/
Bob
Hi Bob!
This application looks amazing! I have a project at work and I need to show like 6 or 8 computer screens in one main screen. I am new to all this implementation and I was wondering if you can show me how to create the main website and connect to the other remote computers by using your code. It would be very helpful for me
Thanks in advance!!
The code that I developed for this blog post is a prototype and would need a bit of work for a production situation. I also did a ‘firewall friendly’ variant using ASP.NET MVC that you can find here: http://blog.bobcravens.com/2009/10/monitor-multiple-computers-remotely-using-asp-net-mvc/.
There is a link to download the code for that version in the comments. Take a look and see if that helps get you started. There is not a lot remaining to do to get to a viewer of multiple computers. There is quite a bit more work if you want to remotely control those computers. Please feel free to contact me if you have questions.
Bob
Hi rcravens
Can’t we do this without an application for the client PC.
I am trying to capture remote PC screenshots in my LAN by given the remote PC’s IP address to my screen capture admin application which is run in admin PC.
thanks
Dileepa
Hi Dileepa,
Interesting thought. Obviously you would still need something to capture the screen shot and make it available for transfer to the server. I suspect that writing a script to use the built in screen capture facility (print screen) is feasible. So maybe then you could pipe the image (in local clipboard memory) back to the server. Even if you could get all that working, you will be sending a full sized bitmap back over the pipe. My sense tells me that payload may be okay for some applications. So although you *may* be able to get it working, at this point I don’t know if it is a good long term approach. The moment you want to hook into the system (for compression or to piggy-back other meta such as mouse position) you will be writing code. I don’t like to be a ‘nay-sayer’ and encourage you to run with your concept. Let me know how it goes.
Bob
Hi Bob
Yes, I am fully agree to your explanation. I know it is not a suitable long term solution.
Now I am thinking to change the idea again 🙁
I’ll update you if any good news with me.
thanks for you and your blog.
Dileepa
I was looking for such a software, hope it will help me….
Thanks.
Definition of few methods are missing here can u please write the method definition in this page.
Utils.UpdateScreen(ref _screen, data);
ShowImage();
PackCursorCaptureData
UnPackCursorCaptureData
Where I can find these method definition
Thank you for posting this article. This is very useful for my project .But i don’t know exactly how to split the code in to client machine for get the server packets and display into the client system .please any one send the concept of this article
Thanking you
it has a little flaw to your algorithm of GetBoundingBoxForChanges Function,I use ‘0’ to instead of same pixels for newBitmap and preBitmap,and use ‘1’ to instead of diff pixels,
0000000000000000
0000000000000000
s000S1111111111111111
11111111111111111111111
11111111111111111111111
111111111111E000000e
S-E is your bound,but s-e is ture bound.
Hi,
Thanks for the post.
My question is: did you try this when the screen is off?, mean if you create other RDP session, is it possible that you will get black image?
Lets say I will run this im my server, and create a RDP session via win service via code (different session that the one you logged in), I hope i’m wrong, but i think then you will get black image.
Pls correct me if i’m wrong!
Tnx
Joe
hey bob I am unable to run your c# windows application code in LAN please send me whole code using it I am able to run over LAN.
I a able to run that code on i own localhost machine but that code is not run over LAN like a separate client and separate server over LAN.
please send me code as early as possible .
Thanking You
hola: vi tu trabajo y esta genial. pero mucho de como lograr hacer algo asi, no creo que pueda. necesitaria si me ayudaria. un proyecto sencillo, necesito poder conectar a otra pc que tenga la misma aplicacion que otra pc. y poder de una pc monitorear lo que se esta aciendo en la otra pc dentro de la aplicacion, osea ver que esta acciendo en tiempo real en un formulario determinado. simplemente es eso.
me podrias orientar o darme alguna ayuda. programo en C#, visual estudio 2010
Hi Bob, have you thought of using DirectX to capture screens instead? I’ve been messing around with SlimDX and it appears to capture screens very quick with only a few lines of code?
will you please tell me what logic you use to send the screen shot of one machine to send over to another machine over the network. every time are you sending a complete image of desktop to another machine over network or just only difference between prev and current image.
if you send only difference then tell me what logic you use to apply the diif on current image at the other end where screen changes are displaying. please discuss my two points in details.
can i know what searching algorithm was used in the pixel searching?
Hi rcravens,
I have a problem at this line: _viewerService.Open();
probably problem is this line: Uri baseAddress = new Uri(“http://localhost:5974/Rlc/Viewer”);
that error: HTTP could not register URL http://localhost:5974/Rlc/Viewer
Thanks very much,
The basic code and demo is a great starting point for an app I wish to develop.
King
Hi Bob,
Many thanks to you to share this great article.
Really great work and it is really very helpful.
Can i use this logic in my software. Is there any condition on using the logic.
Thanks and Regards
Manoj
Manoj
Manoj,
The blog’s license is at the bottom. I tried to make it very permissive.
Hi Bob,
Thank you very much, hats off to your work and the clean way you implemented it.
Thanks a ton.
Thanks and Regards
Manoj
Can it work on WAN ??
how to make this in windows phone 7? plzz help me this is my final year project.
Hi Bob,
Amazing article. Major speed improvements over CopyFromScreen for my project. One thing I would really like to do is narrow the capture area from the whole desktop to a specific application window. I already can get the rectangle of interest by hWnd but I’m not exactly sure how I can limit this to that specific area. Off course I can just crop off the bits off the final bitmap I don’t need, but I have a feeling it could be faster if I did this farther “upstream”.
Thanks,
Josh
Dear Bob,
I am doing similar kind of the project , i downloaded yours also but the problem is i am not able to connect over internet , and not able to control it, i am having one sys in my home with lan and another is some where else with lan in this case its not working , can u give some idea to break it..
Nice Post.
Hi Sir,
I want to transfer a data from one pc to remote pc which is outside india, how can i do this please give me some idea.
Thanks Sir.
Hi Bob,
Is there any possible that we can sent the event to the server , for eg. mouse move & click and keyboard events ..
hi , could you please tell me why i’m getting an error on ” if ((pNew + x)[0] != (pPrev + x)[0])” regarding protected / corrupted memory ? I would appreciate it
Hi,
We are developing a remote application using Remote desktop services, In this we have a functionality of opening a word file from the app, it is opening the winword from the server not from the client, is there any redirection for file type to client in RDS
Thanks
Hi, Bob. I want to thank the post that you published.
Analyzed their codes of this and his other project (multiple remote desktop…) and implemented in my own WCF project, however the cliente captures all screenshots (i can see by trafic of data), but only shows the first. What will?
Hey man..
I’m trying to set up a system for security on my pc,
I’m just wandering how this exactly works meaning how does it connect from the host to the client? is it possible without being on the same network?
Thanks
Utils.PackCursorCaptureData()
doesn’t found please upload the defination for this also
The capture method here should not be assumed to be fast, nor efficient. it uses BitBlt which has possibly the WORST performance of all the screen capture methods. DirectX CreateOffscreenPlainSurface is most likely twice as fast as this.
Don’t get me started on your implementation of change regions…
You don’t have any means to reduce the bandwidth requirements when the same content is rendered over and over again, e.g. no CopyRect, not to mention that your change region is greedy as hell and most likely has a crap ton of redundant data from the previous frame.
I would strongly encourage you to look into Mirror Drivers, Desktop Duplication APIs, or look into some of the Open implementations of VNC, things like that, don’t use this for any REAL remote desktop application, because it will be both a CPU and bandwidth HOG.
sorry if this seems rude, but it’s all true, This may have been one of the better methods to do this ten years ago, but times have changed.
Hi!
I am not skilled on WCF, but i am developing an application that capture de desktop, as in your exemple, but i have no ideia how to begin doing this.
Would you please, post the step-by-step how to implement this service.
I do not know how to begin implementing WCF on WinForms Application.
Thanks
Hi I want to remove the partial Image because if we are on same screen it repeats the same screen image in the same box and it will continue display the image inside image. could you please tell me how we resolve it.
I need this terribly please help
Hi;
Im this code running but ı giving code error:***
“Parameter is not valid”
code:
Rectangle bounds = new Rectangle();
Bitmap img = capture.Screen(ref bounds);
if (img != null)
{
//img = cozunurlukAzalt(img);
byte[] result = Utils.PackScreenCaptureData(img, bounds);
MemoryStream ms = new MemoryStream(result);
listBox1.Items.Add(ms.Length.ToString());
Image gelenEkran = Bitmap.FromStream(ms); // THIS LINE ERROR
pictureBox1.Image = img;
}
(Im sorry, bad my speak english)
Hello, i want to know how to get remote system IP address and connecting to remote system.
please if anybody have a suggestion please reply..
Very cool stuff. Do you know if the sever program will still be able to capture the screen if it is running in a windows service?
Hi Bob,
Am a final year student of computer science Gombe State University Nigeria. i have interest in desktop screen sharing app. As a matter of urgency, i would like to hear from you if you can help put me through the development of such system in other to hel[p solve problem of learning programming in this defective educational system of ours. deedatb(at)gmail.com, thank you in advance
Hi!
I have written a remote desktop viewer application with the client and monitoring program written in .NET and the server in NodeJS.
It used to capture the full screen every second and send it to the server which forwarded it to the monitoring program (all using sockets), trying to optimize it, I had it send only slices of the screen every couple of hundred milliseconds (I think the slices were an 1/8 of the screen).
The monitoring program could display multiple clients with a window/form for each client, if a client has more than one monitor it still shows an image of all monitors.
I’ve been searching for months now for some simple code that will tap in to the display driver and get only the updated segments of the screen so only that is pushed to the monitoring program, that would save a lot of bandwidth, packet sizes and will allow me to decrease the refresh rate.
I found your articles a couple of month ago when I was building the application, I tried to download and build it locally but I couldn’t get it to work, I wasn’t using wcf, I didn’t know much about wcf as well as my server wasn’t in .NET, I guess the only thing I needed was the function that returns only the portion of the screen that changed.
I see that the function (GetBoundingBoxForChanges) returns a single rectangle, so I’m wondering, what happens if there are changes on many different locations on the screens, shouldn’t it return an array of rectangles?
Also seeing that I want to use only this function, I assume I can just copy-paste the function into my client code, add 2 parameters for the previous bitmap and new bitmap, and the function will return the rectangle coordinates for the part of the bmp that changes, I’ll create a new bmp from the new bmp only selecting the rectangle returned from the function and send it to the server together with the rectangle coordinates so it knows which part of the image to replace, would that work?
Thanks
Hello Bob,
Hope you are doing good. this was a nice article about RDP.
my requirement is to do the RDP via ASP.Net, in the above example can I use ASP.Net webform instead of the client application ??
please do the needful.
one more thing, if possible please let me know the procedure to connect windows machine, linux machine as well as unix shell via web forms.
With Thanks
Amjath
i am new in .net i want to make a feature in my project to take screenshots remotely but unable to integrate this code kindly plz help me i copy past this code in windows form
kind send me this class diagram code
plz any body help me in creating this class diagram plzzzzz
Very well explained 🙂
I want to share also this one open source project if can be useful to someone:
https://github.com/miroslavpejic85/p2p
Best regards.