Last active
March 17, 2023 08:43
-
-
Save xdhmoore/fa909e1bc7236d36844b86cf9c1a1074 to your computer and use it in GitHub Desktop.
Various POCs for changing windows wallpapers, lockscreen wallpapers, screen orientation, etc. with powershell
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
# These files are code I wrote years ago to change windows 7 wallpaper, etc. with powershell. | |
# It's been several years since I've used most of them and they may not have been tested on windows 10 or powershell core. | |
# In particular the Lockscreen code and PoshWinRT dependency do not work w/o powershell core without modifications. | |
# They were written only for my use and are thus pretty rough. But...it took me a bit of research to get them working and | |
# they did work at some point (for varying values of "work"). A lot of the content is copied and adapted from blog posts | |
# I've found, but which at this point I don't remember the source in order to give any attribution. | |
# Anyway, maybe someone will find them useful. They are here by request, but without any cleanup or polish. | |
# A few functions require pre-existing files to exist at particular locations on the file system. | |
# Use at your own risk! | |
$pinvokeCode = @" | |
using System; | |
using System.Runtime.InteropServices; | |
namespace Resolution | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
public struct DEVMODE | |
{ | |
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)] | |
public string dmDeviceName; | |
public short dmSpecVersion; | |
public short dmDriverVersion; | |
public short dmSize; | |
public short dmDriverExtra; | |
public int dmFields; | |
public int dmPositionX; | |
public int dmPositionY; | |
public int dmDisplayOrientation; | |
public int dmDisplayFixedOutput; | |
public short dmColor; | |
public short dmDuplex; | |
public short dmYResolution; | |
public short dmTTOption; | |
public short dmCollate; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] | |
public string dmFormName; | |
public short dmLogPixels; | |
public short dmBitsPerPel; | |
public int dmPelsWidth; | |
public int dmPelsHeight; | |
public int dmDisplayFlags; | |
public int dmDisplayFrequency; | |
public int dmICMMethod; | |
public int dmICMIntent; | |
public int dmMediaType; | |
public int dmDitherType; | |
public int dmReserved1; | |
public int dmReserved2; | |
public int dmPanningWidth; | |
public int dmPanningHeight; | |
}; | |
class NativeMethods | |
{ | |
[DllImport("user32.dll")] | |
public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode); | |
[DllImport("user32.dll")] | |
public static extern int ChangeDisplaySettings(ref DEVMODE devMode, int flags); | |
public const int ENUM_CURRENT_SETTINGS = -1; | |
public const int CDS_UPDATEREGISTRY = 0x01; | |
public const int CDS_TEST = 0x02; | |
public const int DISP_CHANGE_SUCCESSFUL = 0; | |
public const int DISP_CHANGE_RESTART = 1; | |
public const int DISP_CHANGE_FAILED = -1; | |
public const int DMDO_DEFAULT = 0; | |
public const int DMDO_90 = 1; | |
public const int DMDO_180 = 2; | |
public const int DMDO_270 = 3; | |
} | |
public class PrmaryScreenResolution | |
{ | |
static public int GetOrientation() | |
{ | |
DEVMODE dm = GetDevMode(); | |
if (0 != NativeMethods.EnumDisplaySettings(null, NativeMethods.ENUM_CURRENT_SETTINGS, ref dm)) | |
{ | |
return dm.dmDisplayOrientation; | |
} else { | |
return -1; | |
} | |
} | |
static public string ChangeResolution(int orientation) | |
{ | |
DEVMODE dm = GetDevMode(); | |
if (0 != NativeMethods.EnumDisplaySettings(null, NativeMethods.ENUM_CURRENT_SETTINGS, ref dm)) | |
{ | |
// swap width and height | |
int temp = dm.dmPelsHeight; | |
dm.dmPelsHeight = dm.dmPelsWidth; | |
dm.dmPelsWidth = temp; | |
if(orientation < 0) { | |
// determine new orientation based on the current orientation | |
switch(dm.dmDisplayOrientation) | |
{ | |
case NativeMethods.DMDO_DEFAULT: | |
dm.dmDisplayOrientation = NativeMethods.DMDO_270; | |
break; | |
case NativeMethods.DMDO_270: | |
dm.dmDisplayOrientation = NativeMethods.DMDO_180; | |
break; | |
case NativeMethods.DMDO_180: | |
dm.dmDisplayOrientation = NativeMethods.DMDO_90; | |
break; | |
case NativeMethods.DMDO_90: | |
dm.dmDisplayOrientation = NativeMethods.DMDO_DEFAULT; | |
break; | |
default: | |
// unknown orientation value | |
// add exception handling here | |
break; | |
} | |
} else { | |
dm.dmDisplayOrientation = orientation; | |
} | |
int iRet = NativeMethods.ChangeDisplaySettings(ref dm, NativeMethods.CDS_TEST); | |
if (iRet == NativeMethods.DISP_CHANGE_FAILED) | |
{ | |
return "Unable To Process Your Request. Sorry For This Inconvenience."; | |
} | |
else | |
{ | |
iRet = NativeMethods.ChangeDisplaySettings(ref dm, NativeMethods.CDS_UPDATEREGISTRY); | |
switch (iRet) | |
{ | |
case NativeMethods.DISP_CHANGE_SUCCESSFUL: | |
{ | |
return "Success"; | |
} | |
case NativeMethods.DISP_CHANGE_RESTART: | |
{ | |
return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode."; | |
} | |
default: | |
{ | |
return "Failed To Change The Resolution"; | |
} | |
} | |
} | |
} | |
else | |
{ | |
return "Failed To Change The Resolution."; | |
} | |
} | |
private static DEVMODE GetDevMode() | |
{ | |
DEVMODE dm = new DEVMODE(); | |
dm.dmDeviceName = new String(new char[32]); | |
dm.dmFormName = new String(new char[32]); | |
dm.dmSize = (short)Marshal.SizeOf(dm); | |
return dm; | |
} | |
} | |
} | |
"@ | |
Add-Type $pinvokeCode -ErrorAction SilentlyContinue | |
Function Set-ScreenResolutionAndOrientation($orientation) { | |
<# | |
.Synopsis | |
Sets the Screen Resolution of the primary monitor | |
.Description | |
Uses Pinvoke and ChangeDisplaySettings Win32API to make the change | |
.Example | |
Set-ScreenResolutionAndOrientation | |
#> | |
[Resolution.PrmaryScreenResolution]::ChangeResolution($orientation) | |
} | |
Function Get-Orient() { | |
return [Resolution.PrmaryScreenResolution]::GetOrientation(); | |
} | |
#Set-ScreenResolutionAndOrientation | |
Function Set-LeftOrient() { | |
Set-ScreenResolutionAndOrientation 1 | |
} | |
Function Set-UpsideDownOrient() { | |
Set-ScreenResolutionAndOrientation 2 | |
} | |
Function Set-RightOrient() { | |
Set-ScreenResolutionAndOrientation 3 | |
} | |
Function Reset-Orient() { | |
Set-NormOrient | |
} | |
Function Set-NormOrient() { | |
Set-ScreenResolutionAndOrientation 0 | |
} | |
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
$signature = @" | |
[DllImport("user32.dll")] | |
public static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags ); | |
"@ | |
$systemParamInfo = Add-Type -memberDefinition $signature -Name ScreenSaver -passThru | |
Function Set-ScreenSaver($screensaver) { | |
if (($screensaver ?? "").trim() -ne "") { | |
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name scrnsave.exe -Value $screensaver | |
} else { | |
Remove-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name scrnsave.exe | |
} | |
#"C:\Windows\gphoto~1.scr" | |
} | |
Function Get-ScreenSaverTimeout | |
{ | |
[Int32]$value = 0 | |
$systemParamInfo::SystemParametersInfo(14, 0, [REF]$value, 0) | |
#$($value/60) | |
$value | |
} | |
Function Set-ScreenSaverTimeout | |
{ | |
Param ([Int32]$value) | |
$seconds = $value# * 60 | |
[Int32]$nullVar = 0 | |
if (!($systemParamInfo::SystemParametersInfo(15, $seconds, [REF]$nullVar, 2))) { | |
Write-Error "Failed to set screensaver timeout via SystemParametersInfo()" | |
return; | |
} | |
} | |
#PoshWinRT.dll comes from here: https://github.com/rkeithhill/PoshWinRT | |
function Set-LockscreenWallpaper($path) { | |
$PROJ_DIR="$(Get-Item (Get-item $PSCommandPath).DirectoryName)" | |
Add-Type -Path $($PROJ_DIR + "\PoshWinRT.dll") | |
[Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime] > $null | |
$asyncOp = [Windows.Storage.StorageFile]::GetFileFromPathAsync($path) | |
$typeName = 'PoshWinRT.AsyncOperationWrapper[Windows.Storage.StorageFile]' | |
$wrapper = new-object $typeName -Arg $asyncOp | |
$file = $wrapper.AwaitResult() | |
$null = [Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime] | |
$asyncOp = [Windows.System.UserProfile.LockScreen]::SetImageFileAsync($file) | |
$typeName = 'PoshWinRT.AsyncActionWrapper' | |
$wrapper = new-object $typeName -Arg $asyncOp | |
$wrapper.AwaitResult() | |
} | |
Function Disable-LockscreenSlideshow() { | |
Set-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lock Screen" -name SlideshowEnabled -value 0 | |
rundll32.exe user32.dll, UpdatePerUserSystemParameters | |
} | |
Function Enable-LockscreenSlideshow() { | |
Set-ItemProperty -path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Lock Screen" -name SlideshowEnabled -value 1 | |
rundll32.exe user32.dll, UpdatePerUserSystemParameters | |
} | |
<# | |
Function Set-ScreensaverTimeout($timeout) { | |
Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" -Name "ScreenSaveTimeout" -Value "$timeout" | |
# some internet post said to run this three times and it seems to work | |
for ($i = 0; $i -le 4; $i++) { | |
rundll32.exe user32.dll, UpdatePerUserSystemParameters | |
} | |
#SPI_SETSCREENSAVETIMEOUT | |
} | |
#> |
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
#Taken from http://www.culham.net/powershell/changing-desktop-wallpaper-using-windows-powershell/ | |
Add-Type -ReferencedAssemblies Microsoft.Win32.Registry.dll @" | |
using System; | |
using System.Runtime.InteropServices; | |
using Microsoft.Win32; | |
namespace Wallpaper { | |
public enum Style : int { | |
Tile, Center, Stretch, NoChange, Fit | |
} | |
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 lpvPara, 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); | |
// Copied from https://stackoverflow.com/questions/19989906/how-to-set-wallpaper-style-fill-stretch-according-to-windows-version | |
// Two registry values are set in the Control Panel\Desktop key. | |
// TileWallpaper | |
// 0: The wallpaper picture should not be tiled | |
// 1: The wallpaper picture should be tiled | |
// WallpaperStyle | |
// 0: The image is centered if TileWallpaper=0 or tiled if TileWallpaper=1 | |
// 2: The image is stretched to fill the screen | |
// 6: The image is resized to fit the screen while maintaining the aspect | |
// ratio. (Windows 7 and later) | |
// 10: The image is resized and cropped to fill the screen while | |
// maintaining the aspect ratio. (Windows 7 and later) | |
switch(style) { | |
case Style.Stretch: | |
key.SetValue(@"WallpaperStyle", "2"); | |
key.SetValue(@"TileWallpaper", "0"); | |
break; | |
case Style.Center: | |
key.SetValue(@"WallpaperStyle", "1"); | |
key.SetValue(@"TileWallpaper", "0"); | |
break; | |
case Style.Tile: | |
key.SetValue(@"WallpaperStyle", "1"); | |
key.SetValue(@"TileWallpaper", "1"); | |
break; | |
//TODO test this | |
case Style.Fit: | |
key.SetValue(@"WallpaperStyle", "6"); | |
key.SetValue(@"TileWallpaper", "0"); | |
break; | |
case Style.NoChange: | |
break; | |
} | |
key.Close(); | |
} | |
} | |
} | |
"@ | |
Function Set-WallColor() { | |
Set-Wallpaper "Color" $null | |
} | |
Function Set-WallPic($Path) { | |
Set-Wallpaper "Single" "$Path" | |
} | |
Function Set-WallSlides($IniPath) { | |
Set-Wallpaper "SlideShow" "$IniPath" | |
} | |
Function Set-Wallpaper([string]$Type, [string]$Path) { | |
#TODO figure out how to create slideshow.ini | |
#There's a mention of the data struct on your superuser post: | |
#https://superuser.com/questions/1007379/how-do-i-change-the-wallpaper-slideshow-album-programmatically | |
$themeFolder="~\AppData\Roaming\Microsoft\Windows\Themes" | |
$configPath="$themeFolder\slideshow.ini" | |
$createdImg="$themeFolder\TranscodedWallpaper.jpg" | |
$imgFitStyle=4 | |
$wallpaperStyle=$imgFitStyle | |
if ($Type -eq "Single") { | |
if ("$Path" -and !(Test-Path "$Path")) { | |
Write-Error "Can't find image file '$Path'" | |
return | |
} | |
if (Test-Path "$createdImg") { | |
rm -Force "$createdImg" | |
} | |
if (Test-Path "$configPath") { | |
#Remove slideshow.ini | |
rm -Force $configPath | |
} | |
[Wallpaper.Setter]::SetWallpaper("$Path", $wallpaperStyle) | |
} elseif ($Type -eq "Slideshow") { | |
if (!(Test-Path $Path)) { | |
Write-Error "Can't find slideshow.ini config file '$Path'" | |
return | |
} | |
[Wallpaper.Setter]::SetWallpaper("", $wallpaperStyle) | |
#Copy new slideshow.ini | |
cp -Force "$Path" "$configPath" | |
} elseif ($Type -eq "Color") { | |
#Obviously, this isn't changing the color. Could probably change it above if we really want | |
#Also, this means changing to color resets style to "Fit". Could | |
#add a 3rd parameter: style | |
Set-Wallpaper -Type Single -Path "" | |
} else { | |
Write-Error "Unknown Type: '$Type'" | |
return | |
} | |
Start-Job -ScriptBlock { | |
#TODO there has to be a better way to refresh the desktop | |
#TODO look at https://superuser.com/questions/398605/how-to-force-windows-desktop-background-to-update-or-refresh | |
Stop-Process -name explorer | |
#TODO could do this last part async in a job | |
sleep 1; | |
if (!(Get-Process -name explorer)) { | |
#TODO is there a way to get back the focus after this? | |
Start-Process explorer | |
} | |
} | Out-Null | |
} | |
Function Next-Slide() { | |
$shellApp = New-Object -ComObject Shell.Application | |
$wScript = New-Object -ComObject WScript.Shell | |
# stack.push(...) | |
# I guess this is assuming the desktop isn't activated. | |
$shellApp.ToggleDesktop(); | |
# This doesn't seem to be needed, but just in case: | |
#$desktopLoc = $wScript.SpecialFolders('Desktop'); | |
#$wScript.AppActivate($desktopLoc); | |
#Give time to get to the desktop | |
sleep 1; | |
# Hack to make sure something is selected on the desktop | |
# if there is something | |
$wScript.SendKeys('{RIGHT}'); | |
$wScript.SendKeys('{UP}'); | |
$wScript.SendKeys('{LEFT}'); | |
$wScript.SendKeys('{DOWN}'); | |
# Toggles selection on desktop. Selected items have a | |
# different context window, so we want to have nothing | |
# selected | |
$wScript.SendKeys("^ "); | |
# Open a context menu and select the option to see the next slide | |
$wScript.SendKeys("+{F10}"); | |
$wScript.SendKeys("n"); | |
$wScript.SendKeys("~"); #I don't usually use the enter key, but when I do I use "~" | |
# Give the key commands time to be read | |
sleep 1; | |
# stack.pop() | |
$shellApp.ToggleDesktop(); | |
} | |
Function Set-LockScreen($path) { | |
Set-ItemProperty -path 'HKCU:\Control Panel\Desktop\' -name wallpaper -value "$path" | |
rundll32.exe user32.dll, UpdatePerUserSystemParameters | |
} | |
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
Function Set-Theme ($theme) { | |
cmd /C ('rundll32.exe %SystemRoot%\system32\shell32.dll,Control_RunDLL %SystemRoot%\system32\desk.cpl desk,@Themes /Action:OpenTheme /file:"C:\Users\admin\AppData\Local\Microsoft\Windows\Themes\' + $theme + '.theme"') | |
sleep 2; | |
# This is neat, bro | |
#$wScript = New-Object -ComObject WScript.Shell; $wScript.AppActivate('Personalization'); $wScript.SendKeys('%{F4}') | |
#$wScript = New-Object -ComObject WScript.Shell; | |
#$wScript.AppActivate('Personalization'); | |
## This is kindof dicey | |
#$wScript.SendKeys('%{F4}') | |
# By default, it always (?) uses the alphabetically first picture | |
Next-Slide | |
$handle = $(Get-WindowHandle 'Personalization' 'CabinetWClass') | |
while ($handle.ToInt64() -le 0) { | |
sleep 0.5; | |
$handle = Get-WindowHandle 'Personalization' 'CabinetWClass' | |
} | |
Close-WindowHandle $handle | |
} | |
Function Flip-Screen() { | |
if ($(Get-Orient) -eq 0) { | |
Set-LeftOrient | |
sleep 1 | |
Set-Theme vslidebg | |
} else { | |
Set-NormOrient | |
sleep 1 | |
Set-Theme slidebg | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment