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.
Figure 1: Form Design
The Form contains one large PictureBox and one Timer.
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.
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.