Public/Functions/Screenshot.ps1
|
function Get-ScreenPNG { <# .SYNOPSIS Gets ScreenPNG information. .DESCRIPTION Returns ScreenPNG data for the current system or OSD session context. .PARAMETER Env Specifies the Env to use when running Get-ScreenPNG. .PARAMETER Directory Specifies the Directory to use when running Get-ScreenPNG. .PARAMETER Prefix Specifies the Prefix to use when running Get-ScreenPNG. .PARAMETER Delay Specifies the Delay to use when running Get-ScreenPNG. .PARAMETER Count Specifies the Count to use when running Get-ScreenPNG. .PARAMETER Clipboard Specifies the Clipboard to use when running Get-ScreenPNG. .PARAMETER Primary Specifies the Primary to use when running Get-ScreenPNG. .EXAMPLE Get-ScreenPNG -Env <value> Demonstrates a common way to run Get-ScreenPNG. .LINK https://github.com/OSDeploy/OSD/tree/master/docs .NOTES Author: David Segura - Recast Software 2026-07-13 - Initial help block created 2026-07-13 - Refined generated help text #> [CmdletBinding()] param ( #Directory where the Screenshots will be saved #Default = $Env:TEMP\Screenshots [string]$Directory = $null, #Saved files will have a Screenshot prefix in the filename [string]$Prefix = $null, #Delay before taking a Screenshot in seconds #Default: 0 (1 Count) #Default: 1 (>1 Count) [uint32]$Delay = 0, #Total number of Screenshots to capture #Default = 1 [uint32]$Count = 1, #Additionally copies the Screenshot to the Clipboard [System.Management.Automation.SwitchParameter]$Clipboard = $false, #Screenshot of the Primary Display only [System.Management.Automation.SwitchParameter]$Primary = $false ) begin { #================================================= # Gather #================================================= $GetCommandNoun = Get-Command -Name Get-ScreenPNG | Select-Object -ExpandProperty Noun $GetCommandVersion = Get-Command -Name Get-ScreenPNG | Select-Object -ExpandProperty Version $GetCommandHelpUri = Get-Command -Name Get-ScreenPNG | Select-Object -ExpandProperty HelpUri $GetCommandModule = Get-Command -Name Get-ScreenPNG | Select-Object -ExpandProperty Module $GetModuleDescription = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty Description $GetModuleProjectUri = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty ProjectUri $GetModulePath = Get-Module -Name $GetCommandModule | Select-Object -ExpandProperty Path $MyPictures = (New-Object -ComObject Shell.Application).NameSpace('shell:My Pictures').Self.Path #================================================= # Adjust Delay #================================================= if ($Count -gt '1') {if ($Delay -eq 0) {$Delay = 1}} #================================================= # Determine Task Sequence #================================================= $LogPath = '' $SMSTSLogPath = '' try { $TSEnv = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction SilentlyContinue $IsTaskSequence = $true $LogPath = $TSEnv.Value('LogPath') $SMSTSLogPath = $TSEnv.Value('_SMSTSLogPath') } catch [System.Exception] { $IsTaskSequence = $false $LogPath = '' $SMSTSLogPath = '' } #================================================= # Set AutoPath #================================================= if ($Directory -eq '') { if ($IsTaskSequence -and (Test-Path $LogPath)) { $AutoPath = Join-Path -Path $LogPath -ChildPath "Screenshots" } elseif ($IsTaskSequence -and (Test-Path $SMSTSLogPath)) { $AutoPath = Join-Path -Path $SMSTSLogPath -ChildPath "Screenshots" } elseif ($env:SystemDrive -eq 'X:') { $AutoPath = 'X:\Screenshots' } elseif (Test-Path $MyPictures) { $AutoPath = Join-Path -Path $MyPictures -ChildPath "Screenshots" } else { $AutoPath = "$Env:TEMP\Screenshots" } } else { $AutoPath = $Directory } #================================================= # Usage #================================================= Write-Verbose '======================================================================================================' Write-Verbose "$GetCommandNoun $GetCommandVersion $GetCommandHelpUri" Write-Verbose $GetModuleDescription Write-Verbose "Module Path: $GetModulePath" Write-Verbose '======================================================================================================' Write-Verbose 'Get-ScreenPNG [[-Directory] <String>] [[-Prefix] <String>] [[-Delay] <UInt32>] [[-Count] <UInt32>] [-Clipboard] [-Primary]' Write-Verbose '' Write-Verbose '-Directory Directory where the Screenshots will be saved' Write-Verbose ' If this value is not set, Path will be automatically set between the following:' Write-Verbose ' Defaults = [LogPath\Screenshots] [_SMSTSLogPath\Screenshots] [My Pictures\Screenshots] [$Env:TEMP\Screenshots]' Write-Verbose " Value = $AutoPath" Write-Verbose '' $DateString = (Get-Date).ToString('yyyyMMdd_HHmmss') Write-Verbose "-Prefix Pattern in the file name $($Prefix)_$($DateString).png" Write-Verbose " Default = Screenshot" Write-Verbose " Value = $Prefix" Write-Verbose '' Write-Verbose '-Count Total number of Screenshots to capture' Write-Verbose ' Default = 1' Write-Verbose " Value = $Count" Write-Verbose '' Write-Verbose '-Delay Delay before capturing the Screenshots in seconds' Write-Verbose ' Default = 0 (Count = 1) | Default = 1 (Count > 1)' Write-Verbose " Value = $Delay" Write-Verbose '' Write-Verbose '-Clipboard Additionally copies the Screenshot to the Clipboard' Write-Verbose " Value = $Clipboard" Write-Verbose '' Write-Verbose '-Primary Captures Screenshot from the Primary Display only for Multiple Displays' Write-Verbose " Value = $Primary" Write-Verbose '======================================================================================================' #================================================= # Load Assemblies #================================================= Add-Type -Assembly System.Drawing Add-Type -Assembly System.Windows.Forms #================================================= } process { foreach ($i in 1..$Count) { #================================================= # Determine Task Sequence (Process Block) #================================================= $LogPath = '' $SMSTSLogPath = '' try { $TSEnv = New-Object -ComObject Microsoft.SMS.TSEnvironment -ErrorAction SilentlyContinue $IsTaskSequence = $true $LogPath = $TSEnv.Value('LogPath') $SMSTSLogPath = $TSEnv.Value('_SMSTSLogPath') } catch [System.Exception] { $IsTaskSequence = $false $LogPath = '' $SMSTSLogPath = '' } #================================================= # Set AutoPath (Process Block) #================================================= $AutoPathBackup = $AutoPath if ($Directory -eq '') { if ($IsTaskSequence -and (Test-Path $LogPath)) { $AutoPath = Join-Path -Path $LogPath -ChildPath "Screenshots" } elseif ($IsTaskSequence -and (Test-Path $SMSTSLogPath)) { $AutoPath = Join-Path -Path $SMSTSLogPath -ChildPath "Screenshots" } elseif ($env:SystemDrive -eq 'X:') { $AutoPath = 'X:\Screenshots' } elseif (Test-Path $MyPictures) { $AutoPath = Join-Path -Path $MyPictures -ChildPath "Screenshots" } else { $AutoPath = "$Env:TEMP\Screenshots" } } else { $AutoPath = $Directory } Write-Verbose "AutoPath is set to $AutoPath" #================================================= # AutoPathBackup #================================================= if ($AutoPathBackup -ne $AutoPath) { #Path changed, so need to move the content from the previous AutoPath } #================================================= # Determine AutoPath #================================================= if (!(Test-Path "$AutoPath")) { Write-Verbose "Creating snaScreenshot directory at $AutoPath" New-Item -Path "$AutoPath" -ItemType Directory -Force -ErrorAction Stop | Out-Null } #================================================= # Delay #================================================= Write-Verbose "Delay $Delay Seconds" Start-Sleep -Seconds $Delay #================================================= # Display Information #================================================= $GetDisplayAllScreens = @(Get-DisplayAllScreens) $GetDisplayVirtualScreen = Get-DisplayVirtualScreen #================================================= # Display Number #================================================= foreach ($Device in $GetDisplayAllScreens) { #DateString $DateString = (Get-Date).ToString('yyyyMMdd_HHmmss') #DisplayNumber $DisplayNumber = $Device.DeviceName -Replace "[^0-9]" Write-Verbose "DisplayNumber: $DisplayNumber" #FileName if ($Prefix) { $FileName = "$($Prefix)_$($DateString)" } else { $FileName = "$($DateString)" } if ($GetDisplayAllScreens.Count -eq 1) { $FileName = "$($FileName).png" } else { $FileName = "$($FileName)_$($DisplayNumber).png" } if ($Device.Primary -eq $true) { $GetDisplayPrimaryBitmapSize = Get-DisplayPrimaryBitmapSize #Write-Verbose "Width: $($GetDisplayPrimaryBitmapSize.Width)" -Verbose #Write-Verbose "Height: $($GetDisplayPrimaryBitmapSize.Height)" -Verbose $ScreenshotBitmap = New-Object System.Drawing.Bitmap $GetDisplayPrimaryBitmapSize.Width, $GetDisplayPrimaryBitmapSize.Height $ScreenshotGraphics = [System.Drawing.Graphics]::FromImage($ScreenShotBitmap) #Write-Verbose "X: $($GetDisplayVirtualScreen.X)" -Verbose #Write-Verbose "Y: $($GetDisplayVirtualScreen.Y)" -Verbose #Write-Verbose "Size: $($GetDisplayVirtualScreen.Size)" -Verbose $ScreenshotGraphics.CopyFromScreen($GetDisplayVirtualScreen.X, $GetDisplayVirtualScreen.Y, $GetDisplayVirtualScreen.X, $GetDisplayVirtualScreen.Y, $GetDisplayVirtualScreen.Size) Write-Verbose "Saving Primary Screenshot $i of $Count to to $AutoPath\$FileName" } if ($Device.Primary -eq $false) { if ($Primary -eq $true) {Continue} Write-Verbose "Width: $($Device.Bounds.Width)" -Verbose Write-Verbose "Height: $($Device.Bounds.Height)" -Verbose $ScreenshotBitmap = New-Object System.Drawing.Bitmap $Device.Bounds.Width, $Device.Bounds.Height $ScreenshotGraphics = [System.Drawing.Graphics]::FromImage($ScreenShotBitmap) Write-Verbose "X: $($Device.Bounds.X)" -Verbose Write-Verbose "Y: $($Device.Bounds.Y)" -Verbose Write-Verbose "Size: $($GetDisplayVirtualScreen.Size)" -Verbose $ScreenshotGraphics.CopyFromScreen($Device.Bounds.X, $Device.Bounds.Y, 0, 0, $GetDisplayVirtualScreen.Size) Write-Verbose "Saving Secondary Screenshot $i of $Count to to $AutoPath\$FileName" } #================================================= # Save the Screenshot to File # https://docs.microsoft.com/en-us/dotnet/api/system.drawing.image.tag?view=dotnet-plat-ext-5.0 #================================================= $ScreenshotBitmap.Save("$AutoPath\$FileName") #================================================= # Copy the Screenshot to the Clipboard # https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setimage?view=net-5.0 #================================================= if ($Device.Primary -eq $true) { if ($Clipboard) { Write-Verbose "Copying Screenshot to the Clipboard" #Add-Type -Assembly System.Drawing #Add-Type -Assembly System.Windows.Forms [System.Windows.Forms.Clipboard]::SetImage($ScreenshotBitmap) } } } #================================================= # Close #================================================= $ScreenshotGraphics.Dispose() $ScreenshotBitmap.Dispose() #================================================= # Return Get-Item #================================================= Get-Item "$AutoPath\$FileName" #================================================= } } end {} } function Set-ClipboardScreenshot { <# .SYNOPSIS Captures a screenshot and copies it to the clipboard .DESCRIPTION Takes a screenshot of the current display(s) and copies the image to the clipboard using Windows Forms bitmap functionality. .EXAMPLE Set-ClipboardScreenshot Captures the current screen and copies to clipboard .NOTES Author: David Segura - Recast Software 2026-07-10 - Added comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () #================================================= # Load Assemblies #================================================= Add-Type -Assembly System.Drawing Add-Type -Assembly System.Windows.Forms #================================================= # Display Information #================================================= $GetDisplayVirtualScreen = Get-DisplayVirtualScreen #================================================= # Display Number #================================================= $GetDisplayPrimaryBitmapSize = Get-DisplayPrimaryBitmapSize #Write-Verbose "Width: $($GetDisplayPrimaryBitmapSize.Width)" -Verbose #Write-Verbose "Height: $($GetDisplayPrimaryBitmapSize.Height)" -Verbose $ScreenShotBitmap = New-Object System.Drawing.Bitmap $GetDisplayPrimaryBitmapSize.Width, $GetDisplayPrimaryBitmapSize.Height $ScreenShotGraphics = [System.Drawing.Graphics]::FromImage($ScreenShotBitmap) #Write-Verbose "X: $($GetDisplayVirtualScreen.X)" -Verbose #Write-Verbose "Y: $($GetDisplayVirtualScreen.Y)" -Verbose #Write-Verbose "Size: $($GetDisplayVirtualScreen.Size)" -Verbose $ScreenShotGraphics.CopyFromScreen($GetDisplayVirtualScreen.X, $GetDisplayVirtualScreen.Y, $GetDisplayVirtualScreen.X, $GetDisplayVirtualScreen.Y, $GetDisplayVirtualScreen.Size) #================================================= # Copy the ScreenShot to the Clipboard # https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setimage?view=net-5.0 #================================================= [System.Windows.Forms.Clipboard]::SetImage($ScreenShotBitmap) Return Get-Clipboard -Format Image } function Start-ScreenPNGProcess { <# .SYNOPSIS Starts a background process to capture screenshots .DESCRIPTION Launches a hidden PowerShell process that periodically captures screenshots and saves them to the specified directory. .PARAMETER Directory Directory where screenshots will be saved. This parameter is mandatory. .PARAMETER Count Total number of screenshots to capture. Default is 9999 .PARAMETER Delay Delay in seconds between screenshots. Default is 2 seconds .EXAMPLE Start-ScreenPNGProcess -Directory 'C:\Screenshots' Starts capturing screenshots with default delay and count .EXAMPLE Start-ScreenPNGProcess -Directory 'C:\Screenshots' -Count 5 -Delay 3 Starts capturing 5 screenshots with 3-second intervals .NOTES Author: David Segura - Recast Software 2026-07-10 - Added comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Directory, [uint32]$Delay = 2, [uint32]$Count = 9999 ) $StartInfo = new-object System.Diagnostics.ProcessStartInfo $StartInfo.FileName = 'powershell.exe' $StartInfo.Arguments = "-NoExit -WindowStyle Hidden -Command Get-ScreenPNG -Directory $Directory -Count $Count -Delay $Delay" $Global:ScreenPNGPath = $Directory $Global:ScreenPNGProcess = ([System.Diagnostics.Process]::Start($StartInfo)).Id } function Stop-ScreenPNGProcess { <# .SYNOPSIS Stops the background screenshot capture process .DESCRIPTION Terminates the background PowerShell process that is capturing screenshots and clears related global variables. .EXAMPLE Stop-ScreenPNGProcess Stops the background screenshot process .NOTES Author: David Segura - Recast Software 2026-07-10 - Added comment-based help .LINK https://github.com/OSDeploy/OSD/tree/master/docs #> [CmdletBinding()] param () Stop-Process -Id $Global:ScreenPNGProcess -Force -ErrorAction SilentlyContinue $Global:ScreenPNGPath = $null $Global:ScreenPNGProcess = $null } |