お仕事とかでテスト用batとか作ってしょっちゅうたたくけどいちいちコマンドプロンプトに打つのも面倒なので画面のはじっこにでもbat実行用のLauncherでも置いとけばいいかなと思って作ってみた。
最初は適当にexe作ろうと思ったけどたまにはPowerShellでForm作るの復習がてらやってみようってことで昼休みに作ってたらまんまと罠にはまって一旦諦め。
解決はしたものの悔しさと絶対覚えてられない自身があったので記事に残す。
最初のおまじないさえ知っておけばそんなに難しくない
こういうの(アセンブリ)使うよって教えてあげて
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Form(外側)作ってあげて
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "BatToolLauncher"
$Form.Width = 180
$Form.Height = $BatList.Count*30 + 50
$Form.TopMost = $true
ボタンを配置する
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Point(0,0)
$Button.Top = $Button.Top + $i * 30
$Button.Size = New-Object System.Drawing.Size(160,30)
$Button.Text = $x.BaseName
$Form.Controls.Add($Button)
※$BatListはbatの一覧(Get-ChildItemの結果)
※$xは$BatListをforeachで回した時の一時変数
ボタンクリック時の挙動を書くことだけは簡単。
「Add_イベント」の形で定義してあげればいい。クリックイベントなら下の{}の中に書けばいい。
$Button.Add_Click({})
簡単。なはずだった。
とにもかくにも失敗例↓
$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName})
これは$x.FullNameでbatのフルパスが入るからそいつをStart-Processするだけ簡単だよね、と思って頭を抱えたもの。
何が起こるかというと、実際のクリック時の$xを参照してくれる。foreachでぐるぐる回していたので、$xが指すのは最後のbat。つまり、どのボタンを押しても最後のbatファイルが実行される。
というわけで半日かけて試行錯誤・調査して解決策↓
$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure())
GetNewClosure()
を書くことでこの{}で囲まれた部分はスクリプトブロックじゃなくてクロージャだよ、となるらしい。今回の話に沿わせるとこの行を書いた時点の変数の内容でFixしてくれるらしい。
ちょっと説明の言葉が怪しい自覚はある。
結果的にこんな感じ。
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
if($args.Length -gt 0)
{
$TargetDir = $args[0]
}
else
{
$MyPath = $MyInvocation.MyCommand.Path
$TargetDir = Split-Path -Parent $MyPath
}
# batファイルのリスト作成
$BatList = Get-ChildItem $TargetDir -Filter "*.bat"
if($BatList -eq $null)
{
[System.Windows.Forms.MessageBox]::Show("no bat files", "Error")
return
}
# Form作成
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "BatToolLauncher"
$Form.Width = 180
$Form.Height = $BatList.Count*30 + 50
$Form.TopMost = $true
# ボタン作成
$i = 0
foreach($x in $BatList)
{
$Button = New-Object System.Windows.Forms.Button
$Button.Location = New-Object System.Drawing.Point(0,0)
$Button.Top = $Button.Top + $i * 30
$Button.Size = New-Object System.Drawing.Size(160,30)
$Button.Text = $x.BaseName
# GetNewClosure様をたたえる!
$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure())
$Form.Controls.Add($Button)
$i++
}
# 表示
$Form.ShowDialog()
ps1ファイルってそのままたたけないのでいつも汎用性持たせて作ってるbat
powershell -ExecutionPolicy RemoteSigned %~dpn0.ps1 %*
同じ場所にある同名ps1を実行するbat
いつもの使いまわしファイルがどこかにいったので久しぶりに手書きしたけどだいたいこんなだったはず。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント