Windows10 のデスクトップ背景画像を切り替える PowerShell スクリプト

久々に PowerShell を書いた。

夜パソコンをしている時に、ディスプレイが明るいと鬱陶しいので、Windows10 のデスクトップ背景画像をなくして黒一色の背景にし、ディスプレイの発光を押さえたいなと思った。

「PC 設定」から背景画像を取り消したり、元に戻したり、といったことを都度やるのは面倒なので、Windows バッチや PowerShell でスクリプト化してみようと思った次第。

目次

レジストリ変更・Windows バッチでの実装では反映されないことが多く断念。

「背景画像の設定」みたいな感じでググると、Windows バッチでレジストリを変更し、rundll32.exe を使って反映する、という手法が多く紹介されている。

しかし、どの文献でも「1回だけ実行しても上手く反映されないので、何度か同じコード行を実行して反映させる」なんていう、乱暴な方法が紹介されていた。

色んな文献を総合して、以下のように実装することで Windows バッチでも背景画像を設定したり、取り消して一色塗りにしたりが出来たが、その設定変更が反映されないことがあってイマイチなので、自分はコレを使わないことにした。

Rem Windows Batch で実装する・背景画像設定スクリプト

Rem 現在の背景画像の設定を確認する
for /f "tokens=3" %%a in ('reg query "HKEY_CURRENT_USER\Control Panel\Desktop" /v WallPaper ^| findstr /ri "REG_SZ"') do (
  set CURRENT_WALLPAPER_PATH=%%a
)

Rem 「set WALLPAPER_PATH=」と空文字で定義すれば、画像を指定しない設定になる
set WALLPAPER_PATH=C:\PATH\TO\WALL-PAPER.jpg

Rem 背景を固定で黒色にする : 「0 0 0」部分が RGB で 0 ~ 255 を指定できる
reg add "HKEY_CURRENT_USER\Control Panel\Colors" /v Background /t REG_SZ /d "0 0 0" /f
Rem 背景画像を設定する (画像を使用しない場合は空文字を渡す)
reg add "HKEY_CURRENT_USER\Control Panel\Desktop" /v WallPaper /t REG_SZ /d "%WALLPAPER_PATH%" /f
Rem 画像の配置方法を選ぶ (10 : 余白ができないように拡大配置する)
reg add "HKEY_CURRENT_USER\Control Panel\Desktop" /v WallPaperStyle /t REG_SZ /d 10 /f

Rem 設定を反映する : 10回ほど叩くと上手く反映されるのでループしているが、このバッチ自体を連続で実行したりすると上手く反映されないので断念
for /l %%i in (1,1,10) do (
  rundll32.exe user32.dll, UpdatePerUserSystemParameters
  Rem 以下のように書いても動きは同じだった
  Rem rundll32.exe user32.dll, UpdatePerUserSystemParameters 1, True
)

↑ 全く動作しないワケじゃないが、反映されないタイミングが多いので、微妙だなーという。ココにだけ書き残して捨てることにする。

PowerShell で書き直す

もう少し調べてみると、SystemParametersInfo() という Win32 API を使った設定方法が見つかった。C# や .NET は全然書けないが、見様見真似で書いてみた。どうも「PC 設定」など GUI でやる時は自動的に行われているようだが、SystemParametersInfo() で設定変更すると同時に、同様の内容をレジストリにも反映しておかないと、設定がレジストリと一致しなくなってしまうようだ。

以下に、自分が作った PowerShell スクリプト全量を載せておく。ユーザ指定するべきは $wallPaperPath 変数の値のみ。デスクトップ背景にしたい画像へのフルパスを指定しておくだけ。

# デスクトップ背景設定スクリプト : 実行する度に「指定の画像を背景に設定する」「黒一色の背景に変更する」を切り替える

# 変更したい壁紙のパスを指定する : 黒背景に変更する場合はこの変数を空文字に変更して流用・続行する
$wallPaperPath = 'C:\PATH\TO\WALL-PAPER.jpg';

# 背景を変更する C# 関数
$code = @'
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;

public class WallPaper {
  [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
  private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
  
  [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  private static extern int SetSysColors(int cElements, int[] lpaElements, int[] lpRgbValues);
  
  public const int SetDesktopWallpaper = 0x0014;
  public const int UpdateIniFile = 0x01;
  public const int SendWinIniChange = 0x02;
  
  public const int colourDesktop = 1;
  public int[] first = {colourDesktop};
  
  // 背景画像を変更する : 空文字を指定すれば背景画像なしになる
  // レジストリの WallPaper 値をセットすることで壁紙を設定できるが、SystemParametersInfo() を呼び出さないと即座に反映されない
  public static void SetWallPaper(string wallPaperPath) {
    SystemParametersInfo(SetDesktopWallpaper, 0, wallPaperPath, UpdateIniFile | SendWinIniChange);
    
    RegistryKey regKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
    regKey.SetValue("WallPaper", wallPaperPath);
    // アスペクト比を維持して画面全体の大きさにあわせて拡大・画面に収まらない部分ははみ出すよう表示方法を設定する
    regKey.SetValue("WallpaperStyle", "10");
    regKey.SetValue("TileWallpaper", "0");
    regKey.Close();
  }
  
  // 背景色を設定する
  public static void SetBackgroundColour(byte r, byte g, byte b) {
    int[] elements = {colourDesktop};
    System.Drawing.Color colours = System.Drawing.Color.FromArgb(r,g,b);
    int[] colors = {System.Drawing.ColorTranslator.ToWin32(colours)};
    SetSysColors(elements.Length, elements, colors);
    
    RegistryKey regKey = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
    regKey.SetValue("Background", string.Format("{0} {1} {2}", colours.R, colours.G, colours.B));
    regKey.Close();
  }
}
'@;
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing.dll;

# 現在の値を取得する
$currentWallPaper = (Get-Item -Path 'Registry::HKEY_CURRENT_USER\Control Panel\Desktop').GetValue('WallPaper');

$message = '';
if([string]::IsNullOrEmpty("$currentWallPaper")) {
  $message = '現在黒背景なので、背景画像を設定します。';
} else {
  $message = '現在背景画像が設定されているので、黒背景にします。';
  $wallPaperPath = '';
}

Write-Host "$message";
#Read-Host "${message}Enter で開始します。";

# 背景を固定で黒色にする
[WallPaper]::SetBackgroundColour(0, 0, 0);
# 背景画像を設定する
[WallPaper]::SetWallPaper($wallPaperPath);

Write-Host '完了・終了します。';
#Read-Host '完了・Enter で終了します。';
exit;

このスクリプトを .ps1 ファイルとして保存し実行すると、実行する度に

という処理が切り替わる。動作前に確認がしたい場合は、コメントアウトしてある Read-Host をアンコメントしてやれば、Enter を押すことでスクリプトを続行できるようになる。

GitHub リポジトリにもこのスクリプトを置いておいた。

以下、参考文献。

C# や .NET や、Windows API なんかは全然コーディングしたことがなかったので、新鮮だった。今回の、C# のコードを PowerShell 内に埋め込んだ部分の実装は NotePad++ で愚直に実装していて、IDE による支援とかを全然受けずに書いていた。全然経験のない分野だし、.NET あたり調べてやっていこうかな。