バッチ実行中にファイル選択やフォルダ選択を行うGUI


はじめに

これは、Visual Basic Advent Calendar 2021の19日目の記事となります。
先日、Twitterを眺めていたら、下記サイトの紹介ツイートを見つけました。

Windowsならバッチで、ファイル参照やフォルダ参照が出来たら便利かも知れないと、早速ググりました。

PowerShellやC#やWSH(JScript)を使用したものが見られます。
この記事は、Visual Basic Advent Calendarですから、WSH(VBScript)で作り直すことにしました。

ファイル選択

下記サイトを参考にWSH(JScript)から、WSH(VBScript)に書き換えました。

アップロード向けファイル選択

HTMLアプリケーション用のファイル選択を利用しているので、ネットに関連したフィルターが用意されています。

項目 内容
初期値 指定は出来ません。初期はデスクトップですが別フォルダを選択して閉じた場合、次から同じ親プロセス上なら別フォルダが指定されます。
拡張子フィルター すべてのファイル(.)、画像(.gif,.jpg,.jpeg,.png)、HTML(.htm;.html)
タイトル アップロードするファイルの選択
fileRef.bat
@echo off
set dialog="about:<input type=file id=FILE><script language=vbscript>FILE.Click:
set dialog=%dialog%CreateObject("Scripting.FileSystemObject").GetStandardStream(1).WriteLine(FILE.value):
set dialog=%dialog%Close:resizeTo 0,0</script>"

set file=
for /f "tokens=* delims=" %%p in ('mshta.exe %dialog%') do set "file=%%p"
echo selected  file is : "%file%"
pause

結果
c:\Temp>fileRef.bat
selected  file is : "C:\Temp\江戸時代の円周率.txt"
続行するには何かキーを押してください . . .

汎用向けファイル選択

初期フォルダ、フィルタ、タイトルを指定することが出来ます。
拡張子フィルターで指定するファイルが2種類以上の場合、パイプ(|)を使用します。
参照:【C#】オープンファイルダイアログ-ファイル指定(フィルター)

項目 内容
初期値 set "File=(初期値を指定)"
拡張子フィルター set "Filter=(フィルターを指定)"
タイトル set "Title=(タイトル名を指定)"
fileRef2.bat
@echo off
set "File=C:\Temp\*.txt"
set "Filter=テキスト (*.txt)|*.txt|すべてのファイル (*.*)|*.*|"
set "Title=ファイルの選択"

set dialog="about:<object id=HtmlDlgHelper classid=CLSID:3050f4e1-98b5-11cf-bb82-00aa00bdce0b></object>
set dialog=%dialog%<script language=vbscript>resizeTo 0,0:Sub window_onload():
set dialog=%dialog%Set reg=CreateObject("VBScript.RegExp"):reg.Pattern="\0.*":
set dialog=%dialog%Set Env=CreateObject("WScript.Shell").Environment("Process"):
set dialog=%dialog%ret=HtmlDlgHelper.object.openfiledlg(Env("File"),,Env("Filter"),Env("Title")):
set dialog=%dialog%CreateObject("Scripting.FileSystemObject").GetStandardStream(1).Write reg.Replace(ret,""):
set dialog=%dialog%Close:End Sub</script><hta:application caption=no showintaskbar=no />"

set file=
for /f "delims=" %%p in ('MSHTA.EXE %dialog%') do set "file=%%p"
echo selected  file is : "%file%"
pause

注意点として、初回は拡張子フィルターにテキスト (*.txt)を指定しているが、全ての拡張子が表示されてしまう。

一旦、拡張子フィルターをすべてのファイル (*.*)にしてからテキスト (*.txt)を指定すると指定した拡張子のみが表示されるようになる。
次からは同じ親プロセス上なら、テキスト (*.txt)が指定された状態になっている。

結果
c:\Temp>fileRef.bat
selected  file is : "C:\Temp\江戸時代の円周率.txt"
続行するには何かキーを押してください . . .

苦労した点

最初は参考サイトではなく別の方法で検証していたのですが、バッチファイルではパイプ記号(|)のエスケープ処理が難しいかったです。ハット(^)記号をパイプ記号の前に付けてもエラーになり動いてくれない。

参考サイトにて下記のように引数にして渡せば、パイプ記号(|)のエスケープ処理なしで動作できることが分かって、採用しました。

set "Filter=テキスト (*.txt)|*.txt|すべてのファイル (*.*)|*.*|"
Set Env=CreateObject("WScript.Shell").Environment("Process")
ret=HtmlDlgHelper.object.openfiledlg(Env("File"),,Env("Filter"),Env("Title")):

フォルダ選択

項目 内容
ルートフォルダ set "RootFolder=(ルートフォルダを指定)"
メッセージ set "Title=(メッセージ名を指定)"
folderRef.bat
@echo off
set "RootFolder=C:\Temp"
set "Title=読込みフォルダを選択してください"

set dialog="about:<script language=vbscript>resizeTo 0,0:Sub window_onload():
set dialog=%dialog%Set Shell=CreateObject("Shell.Application"):
set dialog=%dialog%Set Env=CreateObject("WScript.Shell").Environment("Process"):
set dialog=%dialog%Set Folder=Shell.BrowseForFolder(0, Env("Title"), 1, Env("RootFolder")):
set dialog=%dialog%If Folder Is Nothing Then ret="" Else ret=Folder.Items.Item.Path End If:
set dialog=%dialog%CreateObject("Scripting.FileSystemObject").GetStandardStream(1).Write ret:
set dialog=%dialog%Close:End Sub</script><hta:application caption=no showintaskbar=no />"

set folder=
for /f "delims=" %%p in ('MSHTA.EXE %dialog%') do  set "folder=%%p"
echo selected  folder is : "%folder%"
pause

結果
c:\Temp>folderRef.bat
selected  file is : "C:\Temp\Test"
続行するには何かキーを押してください . . .

最後に

これまでは、フォルダ固定にしていたりファイルやフォルダをドラッグ&ドロップして引数から処理してました。自分が使用するだけならいいのですが、他の人に操作を依頼するなら、GUIでファイル選択やフォルダ選択できれば、操作性は向上します。

例として、江戸時代の円周率.txtファイルを指定していました。実は江戸時代の寺子屋では、円周率は3.16で教えていたのです。明治になって円周率は3.14に訂正されました。そんな記事をnoteに書きました。