Windows10 のテーマを変更する PowerShell Script

# 作成の背景

壁紙をスライドショー形式で設定しているのだが、時間帯によって参照フォルダーを変えたいというのが目的。

レジストリーにフォルダー・パスが直接記入されているだろうから、cmd.exe をタスク・スケジューラーから叩くだけで簡単に実現できるかと思いきや、どうも複雑な指定がされているみたいで、結局以下のスクリプトでテーマをまるごと切り替える方法を採ることとした。

# コード

```ThemeChanger.ps1
<#  Theme Changer for Windows 10 by Jutha DDA #>

# .theme ファイルのパスを指定
    [String]$pathTheme1 = "C:\Users\johndoe\AppData\Local\Microsoft\Windows\Themes\foo.theme" 
    [String]$pathTheme2 = "C:\Users\johndoe\AppData\Local\Microsoft\Windows\Themes\bar.theme"
    [String]$pathTheme3 = "C:\Users\johndoe\AppData\Local\Microsoft\Windows\Themes\Roamed.theme"
# "HH:mm" 形式でテーマを変更する時刻を指定（ 1<2<3 となるように指定すること）
    [String]$timeStartTheme1 = "00:00" 
    [String]$timeStartTheme2 = "15:00"
    [String]$timeStartTheme3 = "21:30"

# $timeStartThemesX を Get-Date に渡すために時と分に分解
$hour1   = [Int]($timeStartTheme1 -replace ":.*", "")
$minute1 = [Int]($timeStartTheme1 -replace ".*:", "")
$hour2   = [Int]($timeStartTheme2 -replace ":.*", "")
$minute2 = [Int]($timeStartTheme2 -replace ".*:", "")
$hour3   = [Int]($timeStartTheme3 -replace ":.*", "")
$minute3 = [Int]($timeStartTheme3 -replace ".*:", "")

# すでに SystemSetting ウィンドウが開いている場合、ページが遷移するため確認を行う。
$killSystemSettings = $true # SystemSettings が開いていない場合は、実行後ウィンドウを閉じる。
if ( (Get-Process | Where-Object { $_.ProcessName -eq "SystemSettings" }) ) {
    Read-Host "テーマ変更スクリプトを続けるには、Enter を押してください（「設定」ウィンドウのページが「テーマ」に切り替わります）。"
    $killSystemSettings = $false # SystemSettings が開いている場合は、実行後ウィンドウを閉じない
}

# 時刻に応じてテーマを切り替える。
if (
    ( ( Get-Date ) -ge ( Get-Date -Hour $hour1 -Minute $minute1 ) ) -and
    ( ( Get-Date ) -lt ( Get-Date -Hour $hour2 -Minute $minute2 ) )
) {
    Start-Process $pathTheme1 -Wait
} elseif (
    ( ( Get-Date ) -ge ( Get-Date -Hour $hour2 -Minute $minute2 ) ) -and
    ( ( Get-Date ) -lt ( Get-Date -Hour $hour3 -Minute $minute3 ) )
) {
    Start-Process $pathTheme2 -Wait
} else {
    Start-Process $pathTheme3 -Wait
}

# SystemSettings が開くので閉じる（最初から開いていた場合は閉じない）。
if ( $killSystemSettings ) {
    Get-Process | Where-Object { $_.ProcessName -eq "SystemSettings" } | ForEach-Object { $_.Kill() }
    # SystemSetting プロセスが終了する前に、数秒ウィンドウが表示される場合がある。
}
```

※ `Start-Process` は `Invoke-Command` に変えても動く。

# タスクスケジューラの設定

## トリガー

![スクリーンショット、トリガーの編集](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300094/0b156efd-b992-4bfe-cbb0-c7e98ea23191.png)

1. 「スケジュールに従う」>「毎日」を選択。
2. スクリプトの `$timeStartTheme1` と同じ開始時刻を指定。
3. `$timeStartTheme2` と `$timeStartTheme3` についても、同様にトリガーを追加。

## 操作

![スクリーンショット、操作の編集](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300094/264e4159-3b78-0f87-972e-4da591c443f6.png)

1. 「プログラムの開始」を選択。
2. 「プログラム／スクリプト」に PowerShell のパス（`C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`）を入力。
3. 「引数の追加に」`-Command ".\WallpaperChanger.ps1"` と入力。
4. 「開始」にスクリプト・ファイルのパス（例：`C:\User\johndoe\hoge fuga\piyo`）を入力（引用符 `""` も半角スペース等のエスケープ `` ` `` も不要）。

## 設定

![スクリーンショット、設定タブ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/300094/5d269912-4ab4-e436-1824-1c429e78b199.png)

1. 「スケジュールされた時刻にタスクを開始できなかった場合、すぐにタスクを実行する」にチェックを入れる。

# 改善したい点

* 「設定」アプリ（SystemSettings.exe）の起動画面がどうしても表示されてしまうのを解消したい（ 実行ファイルを指定してプロセスを起動していないためか、`-WindowStyle Hidden` は効果がなかった。）
* ~~SystemSettings.exe の起動待ちを `for` 構文で実装しているのが、あまりスマートに思えないので、他の方法で起動を検知したい。~~<ins>使えないと勘違いしていた `Start-Process -Wait` が使えることが判明したため、修正。</ins>
* 「設定」アプリで他のページを開いていた場合、ページ遷移が発生してしまう問題もあるので、そもそも SystemSettings.exe を起動させないで、テーマを切り替えたい（さらにそもそもをいえば、「コントロールパネル」みたいに複数起動できないのをなんとかしてほしいと思うのだが）。

---

以上です。