🐶
PowerShellをタスクトレイに常駐したい
定期的な処理をスクリプトで手早くでっち上げたいなぁというお話
...いや、タスクスケジューラだと管理権限考えるの面倒くさいんですわ
※あとScheduledTaskってPowerShell7では使えなかったよーな気がする
いやまぁ内容的には定期処理ってトコ以外ほぼ以下の記事の二番煎じなんですが
なんか色々魔改造を追加してそこはかとなく別物チックにはなってますええホント
アイコンデータをBase64にしてコード化しつつカラーテーブル弄るのはイカしたアイデアっすね
本題
20250117いくつかこっそり修正
function RunInTaskTray {
param (
[Parameter(Mandatory = $true)] [string] $Name,
[Parameter(Mandatory = $true)] [uint32] $Color,
[Parameter(Mandatory = $true)] [scriptblock] $Conf,
[Parameter(Mandatory = $true)] [scriptblock] $Exec,
[Parameter(Mandatory = $true)] [int] $Interval,
[Parameter(Mandatory = $false)] [string] $MenuNameExit = "終了",
[Parameter(Mandatory = $false)] [string] $MenuNameConf = "設定",
[Parameter(Mandatory = $false)] [string] $MenuNameExec = "実行"
)
begin {}
process {
$mname = "$($Name)Launcher@$Interval)"
$mutex = New-Object System.Threading.Mutex($false, $mname)
try {
# 多重起動回避
if ($mutex.WaitOne(0, $false)) {
try {
# コンテキスト作成
$AppCtxt = New-Object System.Windows.Forms.ApplicationContext
# タスクトレイアイコン作成
$TrayIcon = [System.Windows.Forms.NotifyIcon]@{
Icon = GenTaskTrayIcon($Color)
Text = $Name
BalloonTipIcon = 'Error'
BalloonTipTitle = 'Error'
}
$TrayIcon.ContextMenuStrip = New-Object System.Windows.Forms.ContextMenuStrip
# 設定メニュー
if ($MenuNameConf) {
$ConfMenu = [System.Windows.Forms.ToolStripMenuItem]@{ Text = $MenuNameConf }
$ConfMenu.add_Click({
try {
$null = $Conf.Invoke()
} catch {
$TrayIcon.BalloonTipIcon = "Error"
$TrayIcon.BalloonTipText = $_.ToString()
$TrayIcon.ShowBalloonTip(5000)
}
})
$TrayIcon.ContextMenuStrip.Items.Add($ConfMenu) > $null
}
# 実行メニュー
if ($MenuNameExec) {
$ExecMenu = [System.Windows.Forms.ToolStripMenuItem]@{ Text = $MenuNameExec }
$ExecMenu.add_Click({
$rsl = ""
try {
$rsl = $Exec.Invoke()
} catch {
$TrayIcon.BalloonTipIcon = "Error"
$TrayIcon.BalloonTipText = $_.ToString()
$TrayIcon.ShowBalloonTip(5000)
}
if ($rsl -ne "") {
$TrayIcon.BalloonTipIcon = "Info"
$TrayIcon.BalloonTipText = $rsl
$TrayIcon.ShowBalloonTip(5000)
}
})
$TrayIcon.ContextMenuStrip.Items.Add($ExecMenu) > $null
}
# 終了メニュー
if ("" -eq $ExitMenu -or $null -eq $ExitMenu) {
$ExitMenu = "Exit"
}
$ExitMenu = [System.Windows.Forms.ToolStripMenuItem]@{ Text = $MenuNameExit }
$ExitMenu.add_Click({
$AppCtxt.ExitThread()
})
$TrayIcon.ContextMenuStrip.Items.Add($ExitMenu) > $null
# インターバル
$TrayTimer = New-Object Windows.Forms.Timer
if ($Interval -gt 0){
$TrayTimer.Add_Tick({
$TrayTimer.Stop()
$rsl = ""
try {
$rsl = $Exec.Invoke()
} catch {
$TrayIcon.BalloonTipIcon = "Error"
$TrayIcon.BalloonTipText = $_.ToString()
$TrayIcon.ShowBalloonTip(5000)
}
if ($rsl -ne "") {
$TrayIcon.BalloonTipIcon = "Info"
$TrayIcon.BalloonTipText = $rsl
$TrayIcon.ShowBalloonTip(5000)
}
$TrayTimer.Interval = $Interval
$TrayTimer.Start()
})
$TrayTimer.Interval = 5000 # 固定
$TrayTimer.Enabled = $true
$TrayTimer.Start()
}
# タスクトレイアイコン登録
$TrayIcon.Visible = $true
[System.Windows.Forms.Application]::Run($AppCtxt) > $null
$TrayIcon.Visible = $false
$TrayTimer.Stop()
} finally {
if($TrayTimer){$TrayTimer.Dispose()}
if($TrayIcon ){$TrayIcon.Dispose()}
if($mutex ){$mutex.ReleaseMutex()}
}
}
} finally {
$mutex.Dispose()
}
}
end {}
}
function local:GenTaskTrayIcon([uint32] $ARGB) {
# PowerShell(ぽい)アイコン画像バイナリ
# ・16x16 1bit/pixelインデックスカラー画像
# ・パレット色を書き換えてアイコン背景色を一括変更する
$icon = 'AAABAAEAEBAQAAEABAB4AAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAEDAAAAJT1tIgAAAAZQTFRFAAB/////8DxOgwAAAC1JREFUCNdjYGBgYGFg4GNgYGdgYG5gYGxgYHgARcwHQIJyDAwWNQwGNQxgAACDjAYG7YuK+QAAAABJRU5ErkJggg=='
$strm = New-Object System.IO.MemoryStream(,[System.Convert]::FromBase64String($icon))
$strm.Seek(0x3f, [System.IO.SeekOrigin]::Begin) > $null
$ARGB = $ARGB -band 0x00ffffff
$strm.WriteByte($ARGB -shr 16 -band 0xff)
$strm.WriteByte($ARGB -shr 8 -band 0xff)
$strm.WriteByte($ARGB -shr 0 -band 0xff)
$strm.Seek(0x0, [System.IO.SeekOrigin]::Begin) > $null
return New-Object System.Drawing.Icon($strm)
}
使い方
設定編集と所望処理用のスクリプトブロックを与えて起動します
これで概ね1000ms毎に定期的に処理を実行するハズ
RunInTaskTray "Title" 0x0000ff { Edit } { Exec } 1000
蛇足
実は他の記事についてもまとめるとほぼほぼテンプレコードが出来上がったりします
とりあえずこんだけあれば12時にバックアップ起動とかも簡単に作れるし
やればAutoHotKey的にショートカットキー起動とかもできるよーな気はする
class Conf {
[string]$aaa
[string]$bbb
}
# 設定初期化
function local:InitConfFile([string] $Path) {
if ((Test-Path -LiteralPath $Path) -eq $false) {
$Conf = New-Object Conf -Property @{
aaa = "aaa"
bbb = "bbb"
}
SaveConfFile $Path $Conf
}
}
# 設定書込
function local:SaveConfFile([string] $Path, [Conf] $Conf) {
$null = New-Item ([System.IO.Path]::GetDirectoryName($Path)) -ItemType Directory -ErrorAction SilentlyContinue
$Conf | ConvertTo-Json | Out-File -FilePath $Path
}
# 設定読出
function local:LoadConfFile([string] $Path) {
$json = Get-Content -Path $Path | ConvertFrom-Json
$Conf = ConvertFromPSCO ([Conf]) $json
return $Conf
}
# 設定編集
function local:EditConfFile([string] $Title, [string] $Path) {
$Conf = LoadConfFile $Path
$ret = ShowSettingDialog $Title $Conf
if ($ret -eq "OK") {
SaveConfFile $Path $Conf
}
}
# 設定初期化
InitConfFile $ConfPath
# 処理実行
RunInTaskTray $Title 0x0000ff { EditConfFile $Title $ConfPath } { Exec } 1000
まず設定ロード時のConvertFromPSCO
については以下
設定画面自体(ShowSettingDialog
)については以下
設定画面で使うプロパティグリッドの参照する属性については以下
実はGithubにゴチャゴチャ作ってる最中なんだけど
まだ結構試行錯誤してるんで情報が断片的なのは残念無念
Discussion