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
Really like this proof of concept…
Have made a couple of tweaks to support better automation around detecting whether the wallpaper is actually set, or if the current user has a solid colour instead…
So, have added “Current” as a $Theme (& validation), which goes and grabs the current background colour, and falls back to the default if it needs to:
Switch ($Theme) {
Current {
$RGB = (Get-ItemProperty ‘HKCU:\Control Panel\Colors’ -ErrorAction Stop).BackGround
if ($RGB.Length -eq 0) {
$Theme = “Black”
} else {
$BG = $RGB -split ” ”
$FC1 = $FC2 = @(255,255,255)
$FS1=$FS2=$FontSize
break
}
}
Blue { ….
And have moved the $wpath item to the very top of Begin:
# Enumerate current wallpaper now, so we can decide whether it’s a solid colour or not
try {
$wpath = (Get-ItemProperty ‘HKCU:\Control Panel\Desktop’ -Name WallPaper -ErrorAction Stop).WallPaper
if ($wpath.Length -eq 0) {
# Solid colour used
$UseCurrentWallpaperAsSource = $false
$Theme = “Current”
}
} catch {
$UseCurrentWallpaperAsSource = $false
$Theme = “Current”
}
Code: http://pastebin.com/Fva47UKT
I know I’m late to the party, and on top of that my PS-skills are very rudimentary, but I came here since BGInfo does stupid things at high resolutions. Could you please describe how I can use this instead of BGInfo?
To be able to test the code:
1. Copy/paste the 2 functions’ code into the script pane of the Windows PowerShell ISE running as Administrator.
2. Copy/paste the code of 1 of the examples, and press F5 in the ISE to run the script.
If it prompts to save the file (answer yes) and it will then execute.
looking for a script to deploy Dekstopinfo with mdt/sccm and autorun the app for all users at startup
This is kinda weak but this is what I do.
Function DeskInfo {
$Install_Deskinfo = @” Robocopy.exe D:\install\desktopinfo\ C:\’Program Files (x86)’\DesktopInfo /CopyALL
REG IMPORT C:\’Program Files (x86)’\desktopinfo\addtorunlocalmachine.REG
start-process -FilePath C:\’Program Files (x86)’\desktopinfo\desktopinfo.exe
Write-Host ‘Added DesktopInfo, Your Welcome’
‘@
Invoke-VMScript -VM $VM -ScriptText $Install_Deskinfo -GuestCredential $cred -ScriptType Powershell
}
I would like to pull WMI and CIM objects in. Just havent gotten around to it.
Hopefully this helps.
-Mike
Pingback: Friday fun with Intune: Remove dependency to BGInfo – DeployWindows
Pingback: A Modern bginfo solution – just a simple powershellscript – Client Management / pontuswendt.blog
Hey, first thank you for the script its amazing! Works alot better than bginfo.exe file. Also made a post of this, and linking it to you of course.
https://pontuswendt.blog/2019/05/31/a-modern-bginfo-solution-just-a-simple-powershellscript/
Is it possible to change the fontcolor? For example, if diskspace is going below 10 MB i would like that this line on the desktop that show diskspace available will be drawed in RED. The rest of the text can still be in white
Yes, it’s probably possible, you’ll need to implement this in the process block of the New-BGinfo function.
ok, sounds good, but do you have some code how this should look like?