Displaying the Computer Screen Using .NET

Introduction

A frequent question that pops up on the forums is about how to display the current computer screen inside a PictureBox on a Windows Form. Today, I will explain this to you. Let’s jump right in!

Practical

Open Visual Studio and create either a C# or a Visual Basic.NET Windows Forms application. Design your form as shown in Figure 1 and set your Timer’s Properties as shown in Figure 2.

Form Design
Figure 1: Form Design

The Form contains one large PictureBox and one Timer.

Timer Properties
Figure 2: Timer Properties

Add a class and name it clsGDI. Add the following Namespaces to your clsGDI class.

C#

using System;
using System.Runtime.InteropServices;

VB.NET

Imports System
Imports System.Runtime.InteropServices

These namespaces allow you to work with the Windows API in a proper manner. Complete the class.

C#

public class clsGDI
{

   public const int SRCCOPY = 13369376;

   [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
   public static extern IntPtr DeleteDC(IntPtr hDc);

   [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
   public static extern IntPtr DeleteObject(IntPtr hDc);

   [DllImport("gdi32.dll", EntryPoint = "BitBlt")]
   public static extern bool BitBlt(IntPtr hdcDest, int xDest,
      int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc,
      int ySrc, int RasterOp);

   [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]
   public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc,
      int nWidth, int nHeight);

   [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")]
   public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

   [DllImport("gdi32.dll", EntryPoint = "SelectObject")]
   public static extern IntPtr SelectObject(IntPtr hdc,
      IntPtr bmp);

}

VB.NET

   Public Const SRCCOPY As Integer = 13369376

   <DllImport("gdi32.dll", EntryPoint:="DeleteDC")>
   Public Shared Function DeleteDC(ByVal hDc As IntPtr) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="DeleteObject")>
   Public Shared Function DeleteObject(ByVal hDc As IntPtr) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="BitBlt")>
   Public Shared Function BitBlt(ByVal hdcDest As IntPtr, _
      ByVal xDest As Integer, ByVal yDest As Integer, ByVal wDest _
      As Integer, ByVal hDest As Integer, ByVal hdcSource As _
      IntPtr, ByVal xSrc As Integer, ByVal ySrc As Integer, ByVal _
      RasterOp As Integer) As Boolean
   End Function

   Public Shared Function CreateCompatibleBitmap(ByVal hdc As _
      IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) _
      As IntPtr
   End Function

   <DllImport("gdi32.dll", EntryPoint:="CreateCompatibleDC")>
   Public Shared Function CreateCompatibleDC(ByVal hdc As IntPtr) _
      As IntPtr
   End Function
   <DllImport("gdi32.dll", EntryPoint:="SelectObject")>
   Public Shared Function SelectObject(ByVal hdc As IntPtr, _
      ByVal bmp As IntPtr) As IntPtr
   End Function

The DeleteDC function deletes the specified device context. The DeleteObject function deletes a pen, brush, font, bitmap, region, or palette; in doing this, it frees all system resources associated with the object. The BitBlt function performs a bit-block transfer of the data from a source device context into a destination device context. The CreateCompatibleBitmap function creates a bitmap compatible with the device. The CreateCompatibleDC function creates a memory device context compatible with the specified device.

Add a class named clsUSER and add the necessary namespaces for the class to interpret the Windows API functions.

C#

using System;
using System.Runtime.InteropServices;

VB.NET

Imports System
Imports System.Runtime.InteropServices

Add the rest of the code for the clsUSER class.

C#

   public class clsUSER
   {

      public const int SM_CXSCREEN = 0;
      public const int SM_CYSCREEN = 1;

      [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
      public static extern IntPtr GetDesktopWindow();

      [DllImport("user32.dll", EntryPoint = "GetDC")]
      public static extern IntPtr GetDC(IntPtr ptr);

      [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
      public static extern int GetSystemMetrics(int abc);

      [DllImport("user32.dll", EntryPoint = "GetWindowDC")]
      public static extern IntPtr GetWindowDC(Int32 ptr);

      [DllImport("user32.dll", EntryPoint = "ReleaseDC")]
      public static extern IntPtr ReleaseDC(IntPtr hWnd,
         IntPtr hDc);

   }

   public struct SIZE
   {
      public int x;
      public int y;
   }

VB.NET

Public Class clsUSER

   Public Const SM_CXSCREEN As Integer = 0
   Public Const SM_CYSCREEN As Integer = 1

   <DllImport("user32.dll", EntryPoint:="GetDesktopWindow")>
   Public Shared Function GetDesktopWindow() As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="GetDC")>
   Public Shared Function GetDC(ByVal ptr As IntPtr) As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="GetSystemMetrics")>
   Public Shared Function GetSystemMetrics(ByVal abc As Integer) _
      As Integer
   End Function
   <DllImport("user32.dll", EntryPoint:="GetWindowDC")>
   Public Shared Function GetWindowDC(ByVal ptr As Int32) As IntPtr
   End Function
   <DllImport("user32.dll", EntryPoint:="ReleaseDC")>
   Public Shared Function ReleaseDC(ByVal hWnd As IntPtr, _
      ByVal hDc As IntPtr) As IntPtr
   End Function

End Class

Public Structure SIZE

   Public x As Integer
   Public y As Integer

End Structure

The GetDesktopWindow function retrieves a handle to the desktop window which covers the entire screen and is the area on top of which other windows are painted. The GetDC function retrieves a handle to a device context for the client area of a certain window or for the entire screen. The GetSystemMetrics function retrieves the specified system metric or system configuration setting. The GetWindowDC function retrieves the device context for the entire window including menus, scrollbars, and the title bar.

SIZE is a public structure containing x and y coordinates.

Add a new class and name it clsCapScreen. Add the following Namespaces into it so that it can make use of Drawing functions inside the Drawing Namespace.

C#

using System;
using System.Drawing;

VB.NET

Imports System
Imports System.Drawing

Add the GetDesktopImage Function to the clsCapScreen class. clsCapScreen contains only this function.

C#

      public static Bitmap GetDesktopImage()
      {

         SIZE ScreenSize;

         IntPtr bitScreenBMP;
         IntPtr iDC = clsUSER.GetDC(clsUSER.GetDesktopWindow());
         IntPtr iMemDC = clsGDI.CreateCompatibleDC(iDC);

         ScreenSize.x = clsUSER.GetSystemMetrics
            (clsUSER.SM_CXSCREEN);
         ScreenSize.y = clsUSER.GetSystemMetrics
            (clsUSER.SM_CYSCREEN);

         bitScreenBMP = clsGDI.CreateCompatibleBitmap(iDC,
            ScreenSize.x, ScreenSize.y);

         if (bitScreenBMP != IntPtr.Zero)
         {

            IntPtr hPrev = (IntPtr)clsGDI.SelectObject(iMemDC,
               bitScreenBMP);

            clsGDI.BitBlt(iMemDC, 0, 0, ScreenSize.x, ScreenSize.y,
               iDC, 0, 0, clsGDI.SRCCOPY);
            clsGDI.SelectObject(iMemDC, hPrev);
            clsGDI.DeleteDC(iMemDC);

            clsUSER.ReleaseDC(clsUSER.GetDesktopWindow(), iDC);

            Bitmap bmpRes = Image.FromHbitmap(bitScreenBMP);

            clsGDI.DeleteObject(bitScreenBMP);

            GC.Collect();

            return bmpRes;

         }

         return null;

      }

VB.NET

   Public Shared Function GetDesktopImage() As Bitmap

      Dim ScreenSize As SIZE

      Dim bitScreenBMP As IntPtr
      Dim iDC As IntPtr = clsUSER.GetDC(clsUSER.GetDesktopWindow())
      Dim iMemDC As IntPtr = clsGDI.CreateCompatibleDC(iDC)

      ScreenSize.x = clsUSER.GetSystemMetrics(clsUSER.SM_CXSCREEN)
      ScreenSize.y = clsUSER.GetSystemMetrics(clsUSER.SM_CYSCREEN)

      bitScreenBMP = clsGDI.CreateCompatibleBitmap(iDC, _
         ScreenSize.x, ScreenSize.y)

      If bitScreenBMP <> IntPtr.Zero Then

         Dim hPrev As IntPtr = CType(clsGDI.SelectObject(iMemDC, _
            bitScreenBMP), IntPtr)

         clsGDI.BitBlt(iMemDC, 0, 0, ScreenSize.x, ScreenSize.y, _
            iDC, 0, 0, clsGDI.SRCCOPY)
         clsGDI.SelectObject(iMemDC, hPrev)
         clsGDI.DeleteDC(iMemDC)

         clsUSER.ReleaseDC(clsUSER.GetDesktopWindow(), iDC)

         Dim bmpRes As Bitmap = Image.FromHbitmap(bitScreenBMP)

         clsGDI.DeleteObject(bitScreenBMP)

         GC.Collect()

         Return bmpRes

      End If

      Return Nothing

   End Function

The beauty of the GetDesktopImage function is that it incorporates the APIs from the clsGDI and clsUSER classes. It first creates a Device Context onto which to draw. The call to GetSystemMetrics in the clsUSER class calculates the screen’s dimensions. If these dimensions are not zero, the function makes use of BitBlt in the clsGDIclass to copy the screen’s contents. After a successful copy, the in-memory graphic objects get deleted.

On your Form, add the following code inside the Timer’s Tick event:

C#

      private void tmrTime_Tick(object sender, EventArgs e)
      {

         picScreen.Image = clsCapScreen.GetDesktopImage();

      }

VB.NET

   Private Sub tmrTime_Tick(sender As Object, e As EventArgs) _
         Handles tmrTime.Tick

      picScreen.Image = clsCapScreen.GetDesktopImage()

   End Sub

When run, your program would look like Figure 3. Remember: This code will run every second, or at what speed you set up the Timer’s Interval, so don’t freak out if your results might look a bit psychedelic.

Running
Figure 3: Running

Conclusion

Working with the Windows API’s Graphic functions is quite easy, as long as you ensure that you keep track of the memory resources being used. You need to clean up unused memory frequently. You do not have to make use of a Timer to copy the screen; this was only an example. Now you know how to show the contents of your current Desktop screen inside a PictureBox. Hopefully, in a future article, I can incorporate the mouse and its events as well.

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read