Capturing Screenshots with PowerShell and .NET

There are many reasons for taking a screenshot of your local desktop or a remote user’s desktop.

Sometimes we have some task automated but would like to see the end result, but not only as a blob of text. screenshot with powershell

This is especially true to those of us who have worked in a help desk role know the difficulty involved with having an end user describe an error that’s occurred on their machine.

Wouldn’t it be great if they knew how to take a screenshot and send the file to you? Sometimes we just want a picture to look at. Pictures really are worth a thousand words sometimes.

Enter Powershell!

In order to do this, we’re going to leverage .NET for a few things. We’re going to need to create a canvas to save our image data to. We’re going to need to capture our image data. We’re going to need to save that image data to our canvas and then save it to a file.

How exciting!

Taking a screenshot with PowerShell and .NET

This script can be broken down into the following steps:

  • Get screen information to determine screen capture size
  • Create a bitmap object to save our screen capture to
  • Create a graphics object to perform the screen capture
  • Save our screen capture to file

Getting total screen width and height

.NET info – System.Windows.Forms.SystemInformation.VirtualScreen (link)

In this step, we’re using the VirtualScreen property to get width, height and the top-left coordinates. We will use these to configure the area of the screen that we wish to copy.

$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top

Creating a bitmap object

.NET info – System.Drawing.Bitmap (link)

We need to create a bitmap object to store our image. We can do that a number of ways thanks to the many constructors of System.Drawing.Bitmap. One of the simplest ways is to simply specify the width and height (in pixels) for the bitmap that you wish to create:

$bitmap = New-Object System.Drawing.Bitmap $Width, $Height

Creating a graphics object

.NET info – System.Drawing.Graphics (link)
.NET info – System.Drawing.Graphics.FromImage (link)

The System.Drawing.Graphics class is used as a drawing surface to draw on the bitmap object. This is what we’ll be using to perform a screen capture. For our purposes, we will create the graphics object by using the previously-created bitmap object. This will create the graphics object with the same width and height as the bitmap.

$graphic = [System.Drawing.Graphics]::FromImage($bitmap)

Performing a screen capture

.NET info – System.Drawing.Graphics.CopyFromScreen (link)

Using the Graphics object we previously created, we’ll use one of the methods provided, CopyFromScreen, in order to capture an area of the screen. We’ll set the the coordinates to copy to and from (source and destination, respectively). We’ll also set the size of the area that we wish to copy.

$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)

Saving to file

.NET info – System.Drawing.Bitmap.Save (link)

This is the simplest way to save the bitmap to a file.

$bitmap.Save($File)

You can compress and save this bitmap in other formats, but that is out of the scope of this blog post. Here’s a link if you’re interested in an MSDN example for saving as jpeg with several quality levels – link.

Putting it all together

#############################################################################
# Capturing a screenshot
#############################################################################
$File = "\\SomeLocation\SomeDirectory\MyFancyScreenshot.bmp"
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top
# Create bitmap using the top-left and bottom-right bounds
$bitmap = New-Object System.Drawing.Bitmap $Width, $Height
# Create Graphics object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)
# Capture screen
$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
# Save to file
$bitmap.Save($File) 
Write-Output "Screenshot saved to:"
Write-Output $File
#############################################################################

That’s it! Like magic, you now have a screenshot of your local desktop.

But, wait! Didn’t I mention taking a screenshot of remote users?!

Yep. This is where PDQ Deploy comes in.

Remote screenshots with PDQ Deploy

software_deployment
Since PDQ Deploy can deploy just about any software, script or patch to a machine (Yes. It’s that awesome), we’re going to utilize that for taking a screenshot of a remote user desktop.

We can combine the Run As Logged in User feature of PDQ Deploy packages to take a screenshot of a user’s desktop, which is very useful in troubleshooting issues for end users.

This is a Pro license feature, so if you’re using Free Mode, you should definitely sign up for a free trial and test this out for yourself.

Try PDQ Deploy

Here is the general outline of what needs to be done

  • Open/create new package
  • Details tab

    • Install File ? Point to your Powershell script (.ps1 file)
    • Parameters ? -Path “\\Path\ForYour\Screenshots”
  • Conditions tab

    • Logged On State ? Only run if a user is logged on
  • Options tab

    • Run As ? Logged on User

Here’s an animation of all the steps


For this package, I have modified the script slightly. I have added a Path parameter to allow us to change the Path for any given Install Step. I have also modified the FileName to be a combination of the computer name with a time stamp.

#############################################################################
# Capturing a screenshot
#############################################################################
Param(
[Parameter(Mandatory = $true)][string]$Path
)
$FileName = "$env:COMPUTERNAME - $(get-date -f yyyy-MM-dd_HHmmss).bmp"
$File = "$Path\$FileName"
Add-Type -AssemblyName System.Windows.Forms
Add-type -AssemblyName System.Drawing
# Gather Screen resolution information
$Screen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$Width = $Screen.Width
$Height = $Screen.Height
$Left = $Screen.Left
$Top = $Screen.Top
# Create bitmap using the top-left and bottom-right bounds
$bitmap = New-Object System.Drawing.Bitmap $Width, $Height
# Create Graphics object
$graphic = [System.Drawing.Graphics]::FromImage($bitmap)
# Capture screen
$graphic.CopyFromScreen($Left, $Top, 0, 0, $bitmap.Size)
# Save to file
$bitmap.Save($File) 
Write-Output "Screenshot saved to:"
Write-Output $File
#############################################################################

You should be able to deploy that to any machine that has a user currently logged on. It will save the screenshots to the Path that you specify in your package.

It makes troubleshooting a breeze!

Final Notes

There are a few things to keep in mind when using this script with remote users.

The script will fail if somebody isn’t logged into the machine or if the machine is locked.

If you are running this script with PDQ Deploy in the context of a logged on user, the logged on user will need write permissions to the Path that you specify in the Install Step, otherwise they will not be able to save the screenshot to the target Path.

The Path you specify needs to exist for this to work. You can add logic into your script to check for the path or to force its creation. For example, to force the directory to be created you can do something like this:

New-Item -ItemType Directory -Force -Path $Path

Other than that, good luck with your screenshots!

Enjoy!