You may know the popular Bginfo from Sysinternals and that even Azure uses this utility to tatoo the background of virtual machines.
I wondered if PowerShell (alone) would make it and avoid the dependency on an external binary.
I started to use Google and finally decided to fork the following code available on github: https://github.com/fabriceleal/Imagify/blob/master/imagify.ps1
I also needed to find the way to set a wallpaper under Windows 7 and later…
I decided to extend this PowerTip: http://powershell.com/cs/blogs/tips/archive/2014/01/10/change-desktop-wallpaper.aspx because the rundll32 tricks doesn’t work.
I created two functions, one to create a new background image either from scratch and based on a colored theme (blue, grey and black) or from the existing wallpaper and the second one to set this image as a wallpaper.
Function New-BGinfo {
Param( [Parameter(Mandatory)]
[string] $Text,
[Parameter()]
[string] $OutFile= "$($env:temp)\BGInfo.bmp",
[Parameter()]
[ValidateSet("Left","Center")]
[string]$Align="Center",
[Parameter()]
[ValidateSet("Blue","Grey","Black")]
[string]$Theme="Blue",
[Parameter()]
[string]$FontName="Arial",
[Parameter()]
[ValidateRange(9,45)]
[int32]$FontSize = 12,
[Parameter()]
[switch]$UseCurrentWallpaperAsSource
)
Begin {
Switch ($Theme) {
Blue {
$BG = @(58,110,165)
$FC1 = @(254,253,254)
$FC2 = @(185,190,188)
$FS1 = $FontSize+1
$FS2 = $FontSize-2
break
}
Grey {
$BG = @(77,77,77)
$FC1 = $FC2 = @(255,255,255)
$FS1=$FS2=$FontSize
break
}
Black {
$BG = @(0,0,0)
$FC1 = $FC2 = @(255,255,255)
$FS1=$FS2=$FontSize
}
}
Try {
[system.reflection.assembly]::loadWithPartialName('system.drawing.imaging') | out-null
[system.reflection.assembly]::loadWithPartialName('system.windows.forms') | out-null
# Draw string > alignement
$sFormat = new-object system.drawing.stringformat
Switch ($Align) {
Center {
$sFormat.Alignment = [system.drawing.StringAlignment]::Center
$sFormat.LineAlignment = [system.drawing.StringAlignment]::Center
break
}
Left {
$sFormat.Alignment = [system.drawing.StringAlignment]::Center
$sFormat.LineAlignment = [system.drawing.StringAlignment]::Near
}
}
if ($UseCurrentWallpaperAsSource) {
$wpath = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' -Name WallPaper -ErrorAction Stop).WallPaper
if (Test-Path -Path $wpath -PathType Leaf) {
$bmp = new-object system.drawing.bitmap -ArgumentList $wpath
$image = [System.Drawing.Graphics]::FromImage($bmp)
$SR = $bmp | Select Width,Height
} else {
Write-Warning -Message "Failed cannot find the current wallpaper $($wpath)"
break
}
} else {
$SR = [System.Windows.Forms.Screen]::AllScreens | Where Primary |
Select -ExpandProperty Bounds | Select Width,Height
Write-Verbose -Message "Screen resolution is set to $($SR.Width)x$($SR.Height)" -Verbose
# Create Bitmap
$bmp = new-object system.drawing.bitmap($SR.Width,$SR.Height)
$image = [System.Drawing.Graphics]::FromImage($bmp)
$image.FillRectangle(
(New-Object Drawing.SolidBrush (
[System.Drawing.Color]::FromArgb($BG[0],$BG[1],$BG[2])
)),
(new-object system.drawing.rectanglef(0,0,($SR.Width),($SR.Height)))
)
}
} Catch {
Write-Warning -Message "Failed to $($_.Exception.Message)"
break
}
}
Process {
# Split our string as it can be multiline
$artext = ($text -split "\r\n")
$i = 1
Try {
for ($i ; $i -le $artext.Count ; $i++) {
if ($i -eq 1) {
$font1 = New-Object System.Drawing.Font($FontName,$FS1,[System.Drawing.FontStyle]::Bold)
$Brush1 = New-Object Drawing.SolidBrush (
[System.Drawing.Color]::FromArgb($FC1[0],$FC1[1],$FC1[2])
)
$sz1 = [system.windows.forms.textrenderer]::MeasureText($artext[$i-1], $font1)
$rect1 = New-Object System.Drawing.RectangleF (0,($sz1.Height),$SR.Width,$SR.Height)
$image.DrawString($artext[$i-1], $font1, $brush1, $rect1, $sFormat)
} else {
$font2 = New-Object System.Drawing.Font($FontName,$FS2,[System.Drawing.FontStyle]::Bold)
$Brush2 = New-Object Drawing.SolidBrush (
[System.Drawing.Color]::FromArgb($FC2[0],$FC2[1],$FC2[2])
)
$sz2 = [system.windows.forms.textrenderer]::MeasureText($artext[$i-1], $font2)
$rect2 = New-Object System.Drawing.RectangleF (0,($i*$FontSize*2 + $sz2.Height),$SR.Width,$SR.Height)
$image.DrawString($artext[$i-1], $font2, $brush2, $rect2, $sFormat)
}
}
} Catch {
Write-Warning -Message "Failed to $($_.Exception.Message)"
break
}
}
End {
Try {
# Close Graphics
$image.Dispose();
# Save and close Bitmap
$bmp.Save($OutFile, [system.drawing.imaging.imageformat]::Bmp);
$bmp.Dispose();
# Output our file
Get-Item -Path $OutFile
} Catch {
Write-Warning -Message "Failed to $($_.Exception.Message)"
break
}
}
} # endof function
Function Set-Wallpaper {
Param(
[Parameter(Mandatory=$true)]
$Path,
[ValidateSet('Center','Stretch','Fill','Tile','Fit')]
$Style = 'Stretch'
)
Try {
if (-not ([System.Management.Automation.PSTypeName]'Wallpaper.Setter').Type) {
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Wallpaper {
public enum Style : int {
Center, Stretch, Fill, Fit, Tile
}
public class Setter {
public const int SetDesktopWallpaper = 20;
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo (int uAction, int uParam, string lpvParam, int fuWinIni);
public static void SetWallpaper ( string path, Wallpaper.Style style ) {
SystemParametersInfo( SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange );
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
switch( style ) {
case Style.Tile :
key.SetValue(@"WallpaperStyle", "0") ;
key.SetValue(@"TileWallpaper", "1") ;
break;
case Style.Center :
key.SetValue(@"WallpaperStyle", "0") ;
key.SetValue(@"TileWallpaper", "0") ;
break;
case Style.Stretch :
key.SetValue(@"WallpaperStyle", "2") ;
key.SetValue(@"TileWallpaper", "0") ;
break;
case Style.Fill :
key.SetValue(@"WallpaperStyle", "10") ;
key.SetValue(@"TileWallpaper", "0") ;
break;
case Style.Fit :
key.SetValue(@"WallpaperStyle", "6") ;
key.SetValue(@"TileWallpaper", "0") ;
break;
}
key.Close();
}
}
}
"@ -ErrorAction Stop
} else {
Write-Verbose -Message "Type already loaded" -Verbose
}
# } Catch TYPE_ALREADY_EXISTS
} Catch {
Write-Warning -Message "Failed because $($_.Exception.Message)"
}
[Wallpaper.Setter]::SetWallpaper( $Path, $Style )
}
Let’s see these two functions in action.
First define some multiline text to be written in the image.
$os = Get-CimInstance Win32_OperatingSystem
($o = [pscustomobject]@{
HostName = $env:COMPUTERNAME
UserName = '{0}\{1}' -f $env:USERDOMAIN,$env:USERNAME
'Operating System' = '{0} Service Pack {1} (build {2})' -f $os.Caption,
$os.ServicePackMajorVersion,$os.BuildNumber
}) | ft -AutoSize
$BootTime = (New-TimeSpan -Start $os.LastBootUpTime -End (Get-Date)).ToString()
# $t is the multiline text defined as here-string
$t = @"
$($o.HostName)
Logged on user: $($o.UserName)
$($o.'Operating System')
Uptime: $BootTime
"@
- Exemple 1: ala Backinfo
$WallPaper = New-BGinfo -text $t Set-Wallpaper -Path $WallPaper.FullName -Style Center

$BGHT = @{
Text = $t ;
Theme = "Black" ;
FontName = "Verdana" ;
UseCurrentWallpaperAsSource = $true ;
}
$WallPaper = New-BGinfo @BGHT
Set-Wallpaper -Path $WallPaper.FullName -Style Fill
# Restore the default VM wallpaper
Set-Wallpaper -Path "C:\Windows\Web\Wallpaper\Windows\img0.jpg" -Style Fill
This proof of concept based on just a few hundred lines of PowerShell proves that the dependency on Bginfo could be avoided…
Pingback: Friday Five - September 5, 2014 - The Microsoft MVP Award Program Blog - Site Home - MSDN Blogs
Pingback: Friday Five - September 5th, 2014 - The Microsoft MVP Award Program Blog - Site Home - MSDN Blogs
Pingback: Friday Five – September 5th, 2014 | CmdExec Technology News
Pingback: Friday Five – September 5th, 2014 | Technology News | The Black Gazette
Pingback: Friday Five - September 5th, 2014
I have a similar incomplete project…LOL
using Add-Type -AssemblyName System.Drawing
Pingback: MVP Top Tips—PoC background virtual machines, Real Estate transactions OneDrive OneNote, PowerShell, Windows Server Software defined storage, PCL explained - Canadian IT Manager's Blog - Site Home - TechNet Blogs