はじめまして。初投稿ですが、お手柔らかによろしくお願いします。
この記事は MMA ブログリレー の第8回の記事です。
前回の記事はtake37さんでした。
MMA(Microcomputer Making Association)は、東京は調布の中心駅・調布駅(京王線特急も停まって便利)から徒歩7分程度という好立地に鎮座する電気通信大学という大学でコンピュータ関連のいろんなことをやっている部活です。
概要
接続するネットワークのSSIDによって壁紙を変えることができるプログラムを書いてみたので共有します。実際のところ仮想デスクトップを切り替えるだけで実現可能ではあると思うのですが、練習がてらPowerShell 5.11でスクリプトを書いてみました。C++も時間あるときに書いてみたいです。
どうしてこんなプログラムを作ろうと思ったのかはお察しください。
ちなみに、PowerShellのバージョンは以下の通りです。
> $PSVersionTable
Name Value
---- -----
PSVersion 5.1.26100.8115
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.26100.8115
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
できること
- SSIDに応じて壁紙をそれぞれのディレクトリから選択し、設定する(レジストリを直接いじらないので管理者権限不要かつ比較的安全)
- 定期的に呼び出すことによって疑似的にスライドショーができる
できないこと
- 2つ以上のディスプレイの壁紙を別々に設定する
しくみ
とてもシンプル。
- SSIDを判別する
- SSIDに応じたディレクトリから壁紙にセットする画像を選ぶ(画像が入っていない場合の挙動は不定です)
- C#からWin32APIの
SystemParametersInfo関数を呼び出し、壁紙を変える
注意
念のため、「記事内のプログラムを実行して生じたいかなる損害についても責任を負えません」と宣言しておきます。ですが、管理者権限のいる処理はありませんからおそらくOSが壊れるようなことはないと思います。
一応私の環境での動作は確認しましたが、もしかしたら正常に動作しないかもしれません。例えばネットワークアダプタが複数ある場合はSSIDの取得がうまくいかない可能性があります。(未検証)
方法
0. 壁紙を用意
好きな壁紙とTPOに応じた壁紙をそれぞれ別のディレクトリにいれておきます。(拡張子は.png, .jpg, .jpeg, .bmpが指定できるようになっています。.pngが無難だと思われます)
念のため、ディレクトリのパスには日本語やスペースが入らないようにするといいです。
1. コードを用意
こんな感じのコードを書いて、.ps1で保存します。そのままコピペできると思います。
当初はUTF-8で実装していたのですが、文字化けが発生したのでShift-JISにしています。BOM付のUTF-8が推奨と聞いたので、それで問題ないようであればそちらのほうがよいと思います。
# 設定
## パラメータ
## Refreshが指定されればネットワークが変わってなくても壁紙を変えるので、定期的に呼び出せば疑似スライドショーを実現できる
Param(
[switch]$Refresh
)
## Shift_JIS
chcp 932 > $null
## config.jsonを読み込み
$conf=Get-Content "$($PSScriptRoot)\config.json" | ConvertFrom-Json
## SSIDを指定
$white=$conf.white
$black=$conf.black
## それぞれの画像フォルダを指定
$whitepictures=$conf.whitepictures
$blackpictures=$conf.blackpictures
## ログのディレクトリ
$log=$conf.log
# 関数定義
## SSIDを力技で取得
function GetSSID {
$result=netsh wlan show interfaces | Select-String ' SSID'
return $result -replace ' ', '' -replace 'SSID:', ''
}
## フォルダからランダムに画像をピックアップ
function RandomPickup {
param (
$directory
)
$picked=Get-ChildItem -Path $directory -File | Where-Object { $_.Name -match '\.(png|jpg|jpeg|bmp)$' } | Get-Random
$picked=Join-Path $directory $picked.name
return $picked
}
## P/Invokeで壁紙を変更
function ChangeWallpaper {
param (
$picturepath
)
## 参考: https://thinline196.hatenablog.com/entry/2019/05/02/004823
$setwallpapersource = @"
using System.Runtime.InteropServices;
public class wallpaper {
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 ) {
SystemParametersInfo( SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange );
}
}
"@
Add-Type -TypeDefinition $setwallpapersource
[wallpaper]::SetWallpaper($picturepath)
Write-Output `n
}
## 現在の壁紙の絶対パスを返す
function GetCurrentWallpaper {
$current=(Get-ItemProperty 'HKCU:\Control Panel\Desktop').WallPaper
return $current
}
## $fileが$directoryに含まれるか、具体的にはwhite, blackどっちのフォルダにあるかを考えたい
function DirIncludes {
param (
$file, $directory
)
$tmp=Join-Path $directory $([System.IO.Path]::GetFileName($file))
return ((Test-Path ($tmp)) -And ((Split-Path -Parent $file) -eq $directory))
}
## 位置情報の取得をオンにする必要がある
$SSID=GetSSID
Write-Output "検知したSSID: $SSID"
# メイン処理
if ($white -contains $SSID) {
## ホワイトリスト
$current=GetCurrentWallpaper
if (DirIncludes -file $current -directory $whitepictures) {
if ($Refresh) {
Write-Output 'すでに安全なSSIDですが、壁紙を変えます。'
} else {
Write-Output '安全なSSIDのままなので、壁紙は変えないでおきます。'
exit
}
} else {
Write-Output '安全なSSIDを確認しました。壁紙をいつものに戻します。'
}
$picked=RandomPickup -directory $whitepictures
Write-Output "$whitepictures から$picked を選びました。"
"$(Get-Date -F 'yy-MM-dd HH:mm:ss') white >> $($picked)" >> "$($PSScriptRoot)\$($log)"
ChangeWallpaper -picturepath $picked
} elseif ($black -contains $SSID) {
## ブラックリスト
$current=GetCurrentWallpaper
if (DirIncludes -file $current -directory $blackpictures) {
if ($Refresh) {
Write-Output 'まだダメなSSIDですが、壁紙を変えてみます。'
} else {
Write-Output 'まだダメなSSIDなので、壁紙は変えないでおきます。'
exit
}
} else {
Write-Output 'このSSIDはまずいので、壁紙を変更します。'
}
$picked=RandomPickup -directory $blackpictures
Write-Output "$blackpictures から$picked を選びました。"
"$(Get-Date -F 'yy-MM-dd HH:mm:ss') black >> $($picked)" >> "$($PSScriptRoot)\$($log)"
ChangeWallpaper -picturepath $picked
} else {
## 未登録
Write-Output '未知のSSIDです。whiteかblackのどちらかに登録することをおすすめします。'
"$(Get-Date -F 'yy-MM-dd HH:mm:ss') unrecognised SSID: $($SSID)" >> "$($PSScriptRoot)\$($log)"
}
同じディレクトリにconfig.jsonを置きましょう。中身は以下のようにするとよさそうです。
{
"white": ["自宅などの", "Wi-Fiの", "SSID"],
"black": ["職場や学校の", "Wi-Fiの", "SSID"],
"whitepictures": "whiteなSSIDに接続したときの壁紙を格納するディレクトリ",
"blackpictures": "blackなSSIDに接続したときの壁紙を格納するディレクトリ",
"log": "switchwp_log.txt"
}
私はなぜか.ps1を直接実行できなかったので、.ps1を実行するバッチファイルを作りました。
@echo off
powershell -NoProfile -ExecutionPolicy Bypass %~dp0SwitchWallpaper.ps1 %* 2>>%~dp0errorlog.txt
エラーが発生するとerrorlog.txtに内容が送られるようになっています。
ディレクトリ構造の例
こんな感じになっているとよさそうです。
ログファイルはあらかじめ作っておくと安心ですね。
┬ SwitchWallpaper
├ SwitchWallpaper.ps1
├ SwitchWallpaper.bat
├ config.json
├ errorlog.txt
└ switchwp_log.txt
(罫線手打ちで書いたのですが、もっといい方法ありませんかね……)
2. タスクスケジューラを設定
お好きな方法でタスクスケジューラを開き、イベント ビューア タスクというところで新しいタスクを作成します。タスクの作成というウィンドウが出ると思います。
設定内容
全般
名前や説明は適当に書けばOKです。
トリガー
以下のようにします。
| 項目 | 設定内容 |
|---|---|
| タスクの開始(G) | イベント時 |
| ログ(O) | Microsoft-Windows-WLAN-AutoConfig/Operational |
| ソース(S) | WLAN-AutoConfig |
| イベントID |
8000または8001
|
8000(接続開始)と8001(接続完了)は私の環境では0.3秒ほどの差がありました。よほどのことがなければ8000でいいと思います。
各自でお好みのイベントIDをトリガーにすることもできます。
また、同じ要領で定期実行のトリガーも追加すれば定期的に壁紙を変えられますね。
操作
| 項目 | 設定内容 |
|---|---|
| 操作(I) | プログラムの開始 |
| プログラム/スクリプト(P) |
.batのパス |
| 引数の追加 (オプション)(A) | -Refresh |
引数の追加に-Refreshを指定しなければ、ネットワークがホワイトリストからブラックリスト、あるいはその逆に変化したときのみ壁紙を変更します。定期実行をする場合や、同じリスト内でのネットワークの切り替えのときも壁紙を変えたければ-Refreshを指定するといいです。
条件・設定
お好みでどうぞ。
追記:
ノートパソコンだと条件の電源でコンピューターをAC電源で使用している場合のみタスクを開始する(P)のチェックをオフにしておかないと、電源に接続されていないときは壁紙が切り替わりません。(1敗)
3. 準備完了
念のため、設定\プライバシーとセキュリティ\位置情報のデスクトップ アプリに位置情報へのアクセスを許可するがオンになっているか確認してください。オフだと(なぜか)netshができないみたいです。
お疲れ様でした。これでいけたと思います。
試しに接続するWi-Fiを切り替えたりしてみましょう!
改良できそうな点など(メモ)
- ロジックの合理化(特に
DirIncludes) - ウィンドウを出さずにサイレント実行
- 今のままだと文章を書いている最中などに定期実行されるとウィンドウの注目が切れるのでちょっと不便
- エラーが発生したときにメールを飛ばす
- ログファイルの存在確認
-
.ps1を直接実行 - P/Invokeについて理解
- netshのしくみについて理解
- なぜnetshの実行に位置情報が必要なのか?
参考文献
当初reg addで直接レジストリをいじっていましたが、↓の方法のほうが安全かつ確実性が高かったのでこちらで壁紙を変更することにしました。(閲覧日 2026-05-05)
-
7.xでも動くかもしれませんが、未確認です。 ↩