Created
March 3, 2022 07:19
-
-
Save DustinVenegas/6c3e818e6e2882784d162246bb996f46 to your computer and use it in GitHub Desktop.
Tweet a Screen Shot with PowerShell Core and Windows 10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -PSEdition Core | |
#Requires -Version 7.0 | |
#Requires -Assembly System.Drawing.Common | |
function Invoke-MainWindowScreenCapture { | |
<# | |
.SYNOPSIS | |
Captures the screen of the main window for a given process on Windows. | |
.DESCRIPTION | |
Takes a screen shot of the main window for a given process on Microsoft | |
Windows. Only Windows is supported. See note about | |
.PARAMETER Process | |
Process to screen capture. Defaults to the current PowerShell process. | |
.NOTES | |
Works well as a low-Dependency screen capture tool. Otherwise, consider a a tool like KSnip. | |
Only works on PowerShell Core with Windows. | |
------------------------------------------- | |
CopyFromScreen is available on Microsoft Windows with .NET Core (or Full) via the System.Drawing.Common | |
library. A partial port of System.Drawing.Common exists for Linux, but the CopyFromScreen function was | |
not ported, like many methods. | |
Incorrectly identified as Windows Defender Threat. | |
-------------------------------------------------- | |
Windows Defender incorrectly identifies this script as a threat named "HackTool:PowerShell/EmptireGetScreenshot.C". | |
This script will be quarantined upon first execution. Either allow the script through Windows Defender or consider | |
using a different tool to take screen shots. | |
#> | |
[CmdletBinding()] | |
[Alias("screenshot")] | |
param( | |
[Diagnostics.Process]$Process = [Diagnostics.Process]::GetCurrentProcess() | |
) | |
begin { | |
function Get-ParentWindowHandle { | |
[CmdletBinding()] | |
param( | |
[Parameter(Mandatory=$True, ValuefromPipeline=$True)] | |
[Diagnostics.Process]$Process | |
) | |
process { | |
$p = $Process | |
$mwh = $p.MainWindowHandle | |
while ($p) { | |
if (0 -lt $p.MainWindowHandle) { | |
Write-Verbose "Found MainWindowHandle on process $p, $($p.ID)" | |
$mwh = $p.MainWindowHandle | |
break | |
} | |
$p = $p.Parent | |
} | |
Write-Verbose -Message "Main window handle: $mwh" | |
if (0 -lt $mwh) { | |
Write-Output $mwh | |
} else { | |
Write-Error "Could not determine parent window handle from process: $p" | |
} | |
} | |
} | |
function Get-WindowRectangle { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory=$True, ValuefromPipeline=$True)] | |
[IntPtr]$MainWindowHandle | |
) | |
begin { | |
# Based on Boe Prox's Window Placment in Windows PowerShell with PInvoke. | |
# https://devblogs.microsoft.com/scripting/weekend-scripter-manage-window-placement-by-using-pinvoke/ | |
Add-Type @" | |
using System; | |
using System.Runtime.InteropServices; | |
public class WindowsAPI { | |
[DllImport("user32.dll")] | |
public static extern int GetWindowRect(IntPtr hwnd, out RECT lpRect); | |
[DllImport("user32.dll")] | |
public static extern bool SetProcessDPIAware(); | |
[DllImport("dwmapi.dll")] | |
public static extern int DwmGetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, out RECT pvAttribute, int cbAttribute); | |
} | |
public struct RECT | |
{ | |
public int Left; // x position of upper-left corner | |
public int Top; // y position of upper-left corner | |
public int Right; // x position of lower-right corner | |
public int Bottom; // y position of lower-right corner | |
} | |
public enum DWMWINDOWATTRIBUTE : uint | |
{ | |
NCRenderingEnabled = 1, | |
NCRenderingPolicy, | |
TransitionsForceDisabled, | |
AllowNCPaint, | |
CaptionButtonBounds, | |
NonClientRtlLayout, | |
ForceIconicRepresentation, | |
Flip3DPolicy, | |
ExtendedFrameBounds, | |
HasIconicBitmap, | |
DisallowPeek, | |
ExcludedFromPeek, | |
Cloak, | |
Cloaked, | |
FreezeRepresentation | |
} | |
"@ | |
} | |
process { | |
$rect = New-Object RECT | |
$frame = New-Object RECT | |
if ([WindowsAPI]::GetWindowRect($MainWindowHandle, [ref] $rect)) { | |
# Take Windows Aero borders into account by obtaining the Window rectangle frame too. | |
# https://stackoverflow.com/questions/34139450/getwindowrect-returns-a-size-including-invisible-borders | |
if ([WindowsAPI]::DwmGetWindowAttribute($MainWindowHandle, [DWMWINDOWATTRIBUTE]::ExtendedFrameBounds, [ref]$frame, [Runtime.InteropServices.Marshal]::SizeOf($rect))) { | |
Write-Error -Message "Call to DwmGetWindowAttribute failed" | |
} | |
$border = [Drawing.Rectangle]::FromLTRB($frame.Left-$rect.Left, $frame.Top-$rect.Top, $frame.Right-$rect.Right, $frame.Left-$rect.Left) | |
Write-Verbose "rect is: $rect, frame is: $frame, border is: $border" | |
return [Drawing.Rectangle]::FromLTRB($rect.Left+$border.Left, $rect.Top+$border.Top, $rect.Right+$border.Right, $rect.Bottom-$border.Bottom) | |
} else { | |
Write-Error "could not determine window rect from handle $MainWindowHandle" | |
} | |
} | |
} | |
[Reflection.Assembly]::LoadWithPartialName("System.Drawing.Common") | Out-Null | |
$graphics = [Drawing.Graphics]$null | |
$bmp = [Drawing.Bitmap]$null | |
} | |
process { | |
$path = "$HOME\Pictures\PowerShell\psscreencapture-$(([DateTimeOffset](Get-Date)).ToUnixTimeSeconds()).png" | |
# create a rectangle sized from the main window of the root process | |
$pwh = Get-ParentWindowHandle -Process $process | |
$bounds = Get-WindowRectangle -MainWindowHandle $pwh | |
Write-Verbose "Parent Window Handle: $pwh, Bounds: $bounds" | |
# Copy from the screen into the rectangle | |
$bmp = New-Object Drawing.Bitmap $bounds.width, $bounds.height | |
$graphics = [Drawing.Graphics]::FromImage($bmp) | |
$graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size) | |
# Write the image | |
$bmp.Save($path) | |
# Emit the path written | |
Write-Output $path | |
} | |
end { | |
if ($graphics) { $graphics.Dispose() | Out-Null } | |
if ($bmp) { $bmp.dispose | Out-Null } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Requires -PSEdition Core | |
#Requires -Version 7.0 | |
#Requires -Module bluebirdps | |
# Take screen capture of the current process. | |
Invoke-MainWindowScreenCapture | Set-Variable mwsc | |
# Now, Tweet it without checking anything. | |
Send-TwitterMedia -Path $mwsc -Category TweetImage | Set-Variable tm | |
Publish-Tweet -TweetText "Oh snap, #PowerShell!" -MediaId $($tm | Select-Object -ExpandProperty media_id) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment