Skip to content

Instantly share code, notes, and snippets.

@IMJLA
Created March 25, 2022 17:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IMJLA/7cf2af9843de5e1eada36107487fdbdb to your computer and use it in GitHub Desktop.
Save IMJLA/7cf2af9843de5e1eada36107487fdbdb to your computer and use it in GitHub Desktop.
# Requires -Module virtualmachinemanager
using namespace Windows.Storage
using namespace Windows.Graphics.Imaging
using namespace System.Runtime.WindowsRuntime
param (
[string]$VMMServer,
[string]$OutputDir = 'C:\ProgramData\Get-SCVMConsoleOCR'
)
function Get-SCVMConsoleJPG {
param (
[Parameter(ValueFromPipeline)]
[string[]]$SCVirtualMachine,
[string]$OutputDir
)
begin {
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") > null
}
process {
foreach ($machine in $SCVirtualMachine) {
$ComputerName = ($machine.Name.ToUpper() -split '\.')[0]
$HyperVHost = $machine.HostName
$HyperVGuest = $ComputerName
$xRes = 1024
$yRes = 768
$VMManagementService = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\virtualization\v2" -ComputerName $HyperVHost
$Vm = Get-WmiObject -Namespace "root\virtualization\v2" -ComputerName $HyperVHost -Query "Select * From Msvm_ComputerSystem Where ElementName='$HyperVGuest'"
$VMSettingData = Get-WmiObject -Namespace "root\virtualization\v2" -Query "Associators of {$Vm} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState" -ComputerName $HyperVHost
$RawImageData = $VMManagementService.GetVirtualSystemThumbnailImage(
$VMSettingData,
"$xRes",
"$yRes"
)
$VMThumbnail = [System.Drawing.Bitmap]::new($xRes, $yRes, [System.Drawing.Imaging.PixelFormat]::Format16bppRgb565)
$rectangle = [System.Drawing.Rectangle]::new(0,0,$xRes,$yRes)
[System.Drawing.Imaging.BitmapData]$VMThumbnailBitmapData = $VMThumbnail.LockBits(
$rectangle,
[System.Drawing.Imaging.ImageLockMode]::WriteOnly,
[System.Drawing.Imaging.PixelFormat]::Format16bppRgb565
)
[System.Runtime.InteropServices.marshal]::Copy(
$RawImageData.ImageData,
0,
$VMThumbnailBitmapData.Scan0,
$xRes*$yRes*2
)
$VMThumbnail.UnlockBits($VMThumbnailBitmapData)
$OutputJpgFile = "$OutputDir\$ComputerName.jpg"
Remove-Item $OutputJpgFile -Force -ErrorAction SilentlyContinue
$VMThumbnail.Save($OutputJpgFile)
Write-Output $OutputJpgFile
}
}
}
function Get-JpgOcrText {
param (
[Parameter(ValueFromPipeline)]
[string[]]$File,
[string]$OutputDir
)
begin {
# Add the WinRT assembly, and load the appropriate WinRT types
#Add-Type -AssemblyName System.Runtime.WindowsRuntime
$null = [Windows.Storage.StorageFile, Windows.Storage, ContentType = WindowsRuntime]
$null = [Windows.Media.Ocr.OcrEngine, Windows.Foundation, ContentType = WindowsRuntime]
$null = [Windows.Foundation.IAsyncOperation`1, Windows.Foundation, ContentType = WindowsRuntime]
$null = [Windows.Graphics.Imaging.SoftwareBitmap, Windows.Foundation, ContentType = WindowsRuntime]
$null = [Windows.Storage.Streams.RandomAccessStream, Windows.Storage.Streams, ContentType = WindowsRuntime]
# [Windows.Media.Ocr.OcrEngine]::AvailableRecognizerLanguages
$ocrEngine = [Windows.Media.Ocr.OcrEngine]::TryCreateFromUserProfileLanguages()
# PowerShell doesn't have built-in support for Async operations,
# but all the WinRT methods are Async.
# This function wraps a way to call those methods, and wait for their results.
$getAwaiterBaseMethod = [WindowsRuntimeSystemExtensions].GetMember('GetAwaiter').Where(
{
$PSItem.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'
},
'First'
)[0]
Function Wait-AsyncTask {
param($AsyncTask, $ResultType)
$getAwaiterBaseMethod.
MakeGenericMethod($ResultType).
Invoke($null, @($AsyncTask)).
GetResult()
}
}
process {
foreach ($jpgFile in $File)
{
# From MSDN, the necessary steps to load an image are:
# Call the OpenAsync method of the StorageFile object to get a random access stream containing the image data.
# Call the static method BitmapDecoder.CreateAsync to get an instance of the BitmapDecoder class for the specified stream.
# Call GetSoftwareBitmapAsync to get a SoftwareBitmap object containing the image.
#
# https://docs.microsoft.com/en-us/windows/uwp/audio-video-camera/imaging#save-a-softwarebitmap-to-a-file-with-bitmapencoder
# .Net method needs a full path, or at least might not have the same relative path root as PowerShell
$jpgFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($jpgFile)
$params = @{
AsyncTask = [StorageFile]::GetFileFromPathAsync($jpgFile)
ResultType = [StorageFile]
}
$storageFile = Wait-AsyncTask @params
$params = @{
AsyncTask = $storageFile.OpenAsync([FileAccessMode]::Read)
ResultType = [Streams.IRandomAccessStream]
}
$fileStream = Wait-AsyncTask @params
$params = @{
AsyncTask = [BitmapDecoder]::CreateAsync($fileStream)
ResultType = [BitmapDecoder]
}
$bitmapDecoder = Wait-AsyncTask @params
$params = @{
AsyncTask = $bitmapDecoder.GetSoftwareBitmapAsync()
ResultType = [SoftwareBitmap]
}
$softwareBitmap = Wait-AsyncTask @params
# Run the OCR
$results = Wait-AsyncTask $ocrEngine.RecognizeAsync($softwareBitmap) ([Windows.Media.Ocr.OcrResult])
$ImageFileNameWithoutExtension = ($jpgFile | Split-Path -Leaf) -split '\.' | Select-Object -First 1
$OutputFile = "$OutputDir\$ImageFileNameWithoutExtension.txt"
$results.Text | Out-File $OutputFile -Force
Write-Output $OutputFile
}
}
}
$TextDir = "$OutputDir\Text"
$ImageDir = "$OutputDir\Images"
$null = New-Item -ItemType Directory -Path $TextDir -ErrorAction SilentlyContinue
$null = New-Item -ItemType Directory -Path $ImageDir -ErrorAction SilentlyContinue
Get-SCVirtualMachine -VMMServer $VMMServer |
Get-SCVMConsoleJPG -OutputDir $ImageDir |
Get-JpgOcrText -OutputDir $TextDir
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment