Skip to content

Instantly share code, notes, and snippets.

@BenjaminArmstrong
Last active June 18, 2024 02:35
Show Gist options
  • Save BenjaminArmstrong/e6e4c771b5553b6c15788a3406feb934 to your computer and use it in GitHub Desktop.
Save BenjaminArmstrong/e6e4c771b5553b6c15788a3406feb934 to your computer and use it in GitHub Desktop.
Sample PowerShell script that shows you how to create a .BMP file of the display of a Hyper-V virtual machine.
$VMName = "VM 1"
$BMPName = "C:\Users\benja\Desktop\test.bmp"
Add-Type -AssemblyName "System.Drawing"
$VMCS = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "ElementName='$($VMName)'"
# Get the resolution of the screen at the moment
$video = $VMCS.GetRelated("Msvm_VideoHead")
$xResolution = $video.CurrentHorizontalResolution[0]
$yResolution = $video.CurrentVerticalResolution[0]
function getVMScreenBMP {
param
(
$VM,
$x,
$y
)
$VMMS = Get-WmiObject -Namespace root\virtualization\v2 -Class Msvm_VirtualSystemManagementService
# Get screenshot
$image = $VMMS.GetVirtualSystemThumbnailImage($VMCS, $x, $y).ImageData
# Transform into bitmap
$BitMap = New-Object System.Drawing.Bitmap -Args $x,$y,Format16bppRgb565
$Rect = New-Object System.Drawing.Rectangle 0,0,$x,$y
$BmpData = $BitMap.LockBits($Rect,"ReadWrite","Format16bppRgb565")
[System.Runtime.InteropServices.Marshal]::Copy($Image, 0, $BmpData.Scan0, $BmpData.Stride*$BmpData.Height)
$BitMap.UnlockBits($BmpData)
return $BitMap
}
(getVMScreenBMP $VMCS $xResolution $yResolution).Save($BMPName)
@jdhitsolutions
Copy link

As a PowerShell best practice you shouldn't really reference variables outside the function scope. Here's my fork of your script, although I'm still testing a few things. https://gist.github.com/jdhitsolutions/78d71ceeefb54675a7c53a13ac861780

@BenjaminArmstrong
Copy link
Author

Good point - I have updated to code to be cleaner on scoping

Copy link

ghost commented Jun 9, 2016

I have an error with a new version of the script:

"A Using variable cannot be retrieved. A Using variable can be used only with Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is used with Invoke-Command, the Using variable is valid only if the script block is invoked on a remote computer."

I have Powershell v5.0.10586.117 on the host (2012 R2) and 2012 R2 guest from Image Factory (https://github.com/BenjaminArmstrong/Hyper-V-PowerShell/tree/master/Image-Factory)

@BenjaminArmstrong
Copy link
Author

Gah! Okay, take three. This one should work.

Copy link

ghost commented Jun 16, 2016

Perfect! Thank you.

@AlexJamesHaines
Copy link

Awesome, works great still on Server 2019 :)

@MakotoE
Copy link

MakotoE commented Mar 12, 2024

This is supposed to be run on the host machine, not inside the guest machine, right?

@citelao
Copy link

citelao commented May 3, 2024

I can confirm this works on Windows PowerShell, but not modern PowerShell (powershell.exe, not pwsh.exe), at least on my machine. This is awesome!

EDIT (18 June 2024): The code as-is only works in Windows PowerShell, but that's because there are newer APIs for using WMI/CIM in modern PowerShell.

For my purposes, I used runspaces to continue using the old APIs, but you can see a modern implementation in strawgate's HyperV-OCR project (a friend found that for me).

Runspace alternative:

$s = New-PSSession -UseWindowsPowerShell
Invoke-Command -Session $s -ScriptBlock {
  # Call above code, basically.
  #
  # If you want to reference external variables:
  # $MyVar becomes $using:MyVar
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment