BATファイルとEXEファイルとで引数の扱いが異なる件
Thunderbirdから既定のブラウザであるFirefoxが起動できないことがある問題に対処するために、起動できるように特殊処理をした後にFirefoxを起動するEXEファイルを作ったのだが、Thunderbirdから起動する時に引数が正常に引き渡されないトラブルがあった。BATファイルを【Batch To Exe Converter】で変換して作ったEXEファイルであるが、原因は引数に含まれる = や & などの特殊文字にあるらしかった。それらを含む引数はダブルクォート "" で囲むのが正しい方法だが、Thunderbirdから渡される引数がダブルクォートで囲まれているのかどうか分からなかった。BATファイル内で囲んでも手遅れのようだった。しかし、既存のFirefoxやIEでは正常に引数が渡されている。私が作ったEXEファイルと、その元になったBATファイルに問題があるとしか思えなかった。
BATファイルがダブルクォートで囲まれた引数を正常に処理できていないのではないか、だからコンパイルして作ったEXEファイルもダブルクォートで囲まれた引数を正常に処理できないのではないか、そもそもcmd.exeの限界なのではないか。それを確認してブログに記録しておくために簡単に確認できるコマンドプロンプトでダブルクォート付きの引数でBATファイルを起動してみたら、正常に動作した。想定外だったので、そのBATファイルをコンパイルして作ったEXEファイルで同じように試してみた。こちらは正常に動作しなかった。どうやら、BATファィルと、それをコンパイルして作ったEXEファイルとでは引数の扱いが異なるようである。
とりあえず、BATファイルをコンパイルしたEXEファイルではダメなことが分かったので、別の方法で対処した後に、混乱した頭を整理するために、BATファイルの記述と引数の渡し方(どちらもダブルクォートの有無)で考えられる全ての組み合わせを試してみることにした。幸い、【Batch To Exe Converter】以外にBATファィルをEXEファイルにコンパイルできる別のソフト【nandemoExe】も入手できたので一緒に確認してみた。
EXEファイルにコンパイルする前のBATファイル(echo0.bat)の記述は次の通り。
cd d:\20140317
echo %1
echo "%1"
echo %*
echo "%*"
pause
この echo0.bat を【Batch To Exe Converter】と【nandemoExe】でコンパイルして、それぞれ echo1.exe、echo2.exe を作った。
それぞれに対して、次の二つを引数にしてコマンドプロンプト上で実行したらどうなるか確認した。引数の全てが表示されれば正常であり、一部しか表示されないようなら異常である。
search?q=AKB48&hl=ja
echo0.bat "search?q=AKB48&hl=ja" の結果(ケース1)は次の通り。
D:\20140317>echo "search?q=AKB48&hl=ja"
"search?q=AKB48&hl=ja"
D:\20140317>echo ""search?q=AKB48 & hl=ja""
""search?q=AKB48
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\20140317>echo "search?q=AKB48&hl=ja"
"search?q=AKB48&hl=ja"
D:\20140317>echo ""search?q=AKB48 & hl=ja""
""search?q=AKB48
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\20140317>pause
続行するには何かキーを押してください . . .
echo0.bat search?q=AKB48&hl=ja の結果(ケース2)は次の通り。
D:\20140317>echo search?q
search?q
D:\20140317>echo "search?q"
"search?q"
D:\20140317>echo search?q=AKB48
search?q=AKB48
D:\20140317>echo "search?q=AKB48"
"search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
echo1.exe "search?q=AKB48&hl=ja" の結果(ケース3)は次の通り。
D:\20140317>echo search?q
search?q
D:\20140317>echo "search?q"
"search?q"
D:\20140317>echo search?q=AKB48
search?q=AKB48
D:\20140317>echo "search?q=AKB48"
"search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
echo1.exe search?q=AKB48&hl=ja の結果(ケース4)は次の通り。
D:\20140317>echo search?q
search?q
D:\20140317>echo "search?q"
"search?q"
D:\20140317>echo search?q=AKB48
search?q=AKB48
D:\20140317>echo "search?q=AKB48"
"search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
echo2.exe "search?q=AKB48&hl=ja" の結果(ケース5)は次の通り。ウインドウがもう一つ開いて、そちらに表示された。
D:\20140317>echo "search?q=AKB48&hl=ja"
"search?q=AKB48&hl=ja"
D:\20140317>echo ""search?q=AKB48 & hl=ja""
""search?q=AKB48
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\20140317>echo "search?q=AKB48&hl=ja"
"search?q=AKB48&hl=ja"
D:\20140317>echo ""search?q=AKB48 & hl=ja""
""search?q=AKB48
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
D:\20140317>pause
続行するには何かキーを押してください . . .
echo2.exe search?q=AKB48&hl=ja の結果(ケース6)は次の通り。ウインドウがもう一つ開いて、そちらに表示された。
D:\20140317>echo search?q
search?q
D:\20140317>echo "search?q"
"search?q"
D:\20140317>echo search?q=AKB48
search?q=AKB48
D:\20140317>echo "search?q=AKB48"
"search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
(続行後にメインウインドウに次のエラーが追加された。)
'hl' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
echo1.exe と echo2.exe については、Thunderbirdから http://test.jp/search?q=AKB48&hl=ja というリンクをクリックした時にどんな結果になるかも実験してみた。
echo1.exe が開いた時の結果(ケース7)は次の通り。
D:\20140317>echo http://test.jp/search?q
http://test.jp/search?q
D:\20140317>echo "http://test.jp/search?q"
"http://test.jp/search?q"
D:\20140317>echo http://test.jp/search?q=AKB48
http://test.jp/search?q=AKB48
D:\20140317>echo "http://test.jp/search?q=AKB48"
"http://test.jp/search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
(続行後はウインドウがすぐに閉じてしまったので未確認)
echo2.exe が開いた時の結果(ケース8)は次の通り。
D:\20140317>echo http://test.jp/search?q
http://test.jp/search?q
D:\20140317>echo "http://test.jp/search?q"
"http://test.jp/search?q"
D:\20140317>echo http://test.jp/search?q=AKB48
http://test.jp/search?q=AKB48
D:\20140317>echo "http://test.jp/search?q=AKB48"
"http://test.jp/search?q=AKB48"
D:\20140317>pause
続行するには何かキーを押してください . . .
(続行後はウインドウがすぐに閉じてしまったので未確認)
Thunderbirdから開いた場合、【Batch To Exe Converter】で変換したEXEファイル(echo1.exe)も【nandemoExe】で変換したEXEファイル(echo2.exe)も、結果は同じだった。
ケース1からケース8までを眺めて見ると、ケース1とケース5が同じで、その他は全て同じようだ。
ケース1とケース5は引数が全て引き渡されていて、それ以外は = や & の前で切れている。 = の前で切れたのは引数として %1 を利用した場合で、引数で %* を利用した場合は = では切れずに & までは引き渡されたようである。しかし、それは今回の実験ではあまり重要ではなかったらしい。実際、ケース1とケース5は引数に %1 を使っても %* を使っても全て引き渡されている。
ケース1とケース5にも、引数の扱いで失敗しているケースがある。それはBATファイル(echo0.bat)で %1 や %* をダブルクォート "" で囲んで "%1" や "%*" とした場合である。これはダブルクォート付きで渡された引数を内部でさらにダブルクォートで囲んだため、引数として扱ってほしい文字列の前でダブルクォートは閉じられ、その後はダブルクォート無しで引き渡された形になり、 & の前で切れたようである。ただ、%1 を使った場合も = の前で切れることは無かった。これはいったんBATファイルやEXEファイルが受け取れば内部で切られることは無いのかもしれない。ケース1とケース5以外で切れたのは、BATファイルやEXEファイルが受け取る時の処理なのだろう。
これらの結果から、BATファイルと【nandemoExe】で変換したEXEファイルは引数をダブルクォートで囲んだ状態で受け取り、内部では引数を表す %1 をダブルクォートで囲まない方が良さそうである。引数をダブルクォートで囲まずに受け取っても内部でダブルクォートで囲めば結果として同じだと勘違いしてしまいそうだが、受け取る段階で = や & 以降を切り捨ててしまうので良くない。
さて、【nandemoExe】で変換した echo2.exe でも、Thunderbirdから引数を受け取った時には失敗している。これはケース6の状態になったものと思われる。すなわち、Thunderbirdがダブルクォート付きで引数を渡してくれなかったのである。
しかし、JScript の WScript.Arguments を使えば大丈夫らしい。「引数のコレクション」という扱いらしく、そのコレクションから最初の引数を利用する形でスクリプトを書けば良いらしい。JScript でEXEファイルを起動することもできるが、BATファイルでしか処理できないことがあった場合、JScript でBATファイルを起動する必要がある。その際に、すなわちBATファイルに引数を渡す際に、ダブルクォートで囲んで渡せば、ダブルクォート無しで引数を渡すソフトが相手でも対処できそうである。
長くなったが、Thunderbirdのリンクをクリックした時にFirefoxを起動させるEXEファイルを作ったので書いておく。Firefoxを直接起動するだけではダメな理由は、かなり前から指摘されているThunderbirdやFirefoxのバグ(仕様?)、「MOZ_NO_REMOTE」や「-no-remote」の問題(参考:【ThunderbirdからFirefoxを起動できないし、FirefoxからThunderbirdを起動できない。:正己の異論・反論(雑感):So-netブログ を見て】)があるからである。
さて、Thunderbirdから引数を受け取ってBATファイル(openFx.bat)を起動する JScript ファイル(openFx.js)のスクリプトは次の通り。
var openFx = "\"C:\\(openFx.batの入ってるディレクトリのパスで\を\\にしたもの)\\openFx.bat\"";
var geturl = WScript.Arguments;
var openurl = geturl(0);
openurl = "\"" + openurl + "\"";
// shell.Popup(openurl); // openFx.batの引数を確認したければ有効に
var command = openFx + "\ " + openurl;
// shell.Popup(command); // 実行されるコマンドを確認したければ有効に
shell.run(command)
この openFx.js から起動する openFx.bat の記述は次の通り。
Set MOZ_NO_REMOTE=
start "" "C:\Program Files\Mozilla Firefox\firefox.exe" %*
exit
この Set MOZ_NO_REMOTE= が「MOZ_NO_REMOTE」や「-no-remote」の問題を解決するためにどうしても必要で、JScript では実現できそうにないので、BATファイルを起動する必要があった。ThunderbirdからBATファイルを起動する方法が分からなかったので、BATファィルをEXEファイルに変換して添付ファイル扱いにしたThunderbirdのリンクを開くソフトとして指定していた(参考:【ThunderbirdからFirefoxを起動できないし、FirefoxからThunderbirdを起動できない。:正己の異論・反論(雑感):So-netブログ を見て】)。そのEXEファイルが正常に動作しなかったのである。
openFx.js を作ってもThunderbirdから起動できなければ意味が無いので、JScriptファイルをEXEファイルに変換できる【MakeExe】というフリーウェアを使ってEXEファイル(openFx.exe)に変換した。【nandemoExe】を使っても変換できる。【MakeExe】はコンパイラではないようだが、【nandemoExe】はコンパイルしているかもしれない。ただし、上記程度のスクリプトなら、コンパイルしようがしまいが、速度の差を感じられないかもしれない。【MakeExe】と【nandemoExe】で差は感じられなかった。
BATファイルとそれをコンパイルしたEXEファイルとで引数の扱いが異なることに気付くまで3週間以上(2014/2/21~3/15)かかった。BATファイルを作って異常があったらコマンドプロンプトで確認した方が良い。今回は別の目的でコマンドプロンプトから実行した時にBATファイルに異常が見られないことでEXEファイルの異常だと気付けた。無駄な時間を費やしたかもしれないが、良い勉強になった。
追記(2014/3/18):
上のスクリプトを少し変えて、私以外の人が試せるようにEXEファイルをアップロードした。→
FxfromTB.zip
解凍して現れた FxfromTB フォルダの中の FxfromTB.exe を firefox.exe と同じフォルダに入れて、Thunderbirdのリンクをクリックした時に起動するプログラムとして FxfromTB.exe を指定すれば良い。 readme.txt もフォルダに入れてあるので、使用方法などを確認してほしい。Thunderbirdのリンクをクリックしても起動しないケースも書いてある。
追記(2014/3/23):
FxfromTB.exeの元であるFxfromTB.jsのスクリプトはver.1.02で次のように変更されている。
var openFx = "\"openFx.bat\"";
var geturl = WScript.Arguments;
if (geturl.length < 1) {var openurl = ""} else {
var openurl = geturl(0);
openurl = "\"" + openurl + "\"";
}
// shell.Popup(openurl); // openFx.batの引数を確認したければ有効に
var command = openFx + "\ " + openurl;
// shell.Popup(command); // 実行されるコマンドを確認したければ有効に
shell.run(command,0)
FxfromTB.exe では当初 openFx.bat ではなく openFx.exe を実行していたのだが、openFx.bat に戻した。shell.run(command,0) と",0"を追加したことでBATファイルを実行した時でもコマンドプロンプトの黒い画面が表示されないことが分かった。また、FxfromTB.exe は当初、引数が無ければ実行できず、ダブルクリックするとエラーが表示されていたが、if 行を追加することで、引数が無い場合に、引数が無い状態でFirefoxを起動するようにした。
また、 FxfromTB.exe を firefox.exe と同じフォルダに入れる理由は、openFx.bat が次のように、Firefox の存在するディレクトリを記述せず、同じディレクトリに存在するものとして実行するようになっているからである。
Set MOZ_NO_REMOTE=
start "" "firefox.exe" %*
exit
追記(2015/2/3):
FxfromTB.exe にバグが見つかった。
FxfromTB.exe は FxfromTB.js を実行することで openFx.bat が実行されるようになっているので、拡張子がjsのファイルを開く既定のプログラムがスクリプトを実行するものでなければならないらしい。頻繁に編集するからとテキストエディタを既定のプログラムにしちゃうと、テキストエディタで FxfromTB.js が開く。また、既定のプログラムをDreamweaverにしておくとDreamweaverが開いてしまうらしい。
拡張子がjsのファイルの既定プログラムがテキストエディタでもスクリプトが実行されるようにする方法は、今のところ、分からない。
追記(2015/4/30):
Firefoxの古いバージョンを追加で別のフォルダーにインストールしたら、古いバージョンのFirefoxが起動してしまうバグが生じた。また、追加でインストールした古いバージョンのFirefoxを削除して、コントロールパネルの「プログラムと機能」にFirefoxが無くなり、そこから先にインストールしたFirefoxを削除できなくなった状態で使用すると、次のようなエラーでFirefoxは起動できないバグが生じた。
(クリックすれば拡大)
追記(2016/9/18):
記事中の『この Set MOZ_NO_REMOTE= が「MOZ_NO_REMOTE」や「-no-remote」の問題を解決するためにどうしても必要で、JScript では実現できそうにない』についてTMさんからコメントを頂いたので、リンク先を参考にしてvbsファイルを作って、フリーウェア【MakeExe】でexeファイルに変えてThunderbirdで起動したみたら、「MOZ_NO_REMOTE」の問題が生じずに起動できた。とりあえず作ってみたスクリプトは次の通り。正しいかどうかは不明。しかも、引数の問題は省略しているので、下のスクリプトではリンク先が開くのではなく、Firefoxが起動するだけ。リンク先が開くするようにするには、それなりの記述が必要。私の能力では無理(&時間がない)なので、私はJScript を使った上記のFxfromTB.exeを使い続ける。
On Error Resume Next
Dim objWshShell ' WshShell オブジェクト
Dim objUsrEnv ' 環境変数情報
Dim strEnvName ' 環境変数名
Set objWshShell = WScript.CreateObject("WScript.Shell")
If Err.Number = 0 Then
Set objUsrEnv = objWshShell.Environment("Process")
If Err.Number = 0 Then
strEnvName = "MOZ_NO_REMOTE"
objUsrEnv.Remove(strEnvName)
Else
WScript.Echo "エラー: " & Err.Description
End If
Else
WScript.Echo "エラー: " & Err.Description
End If
Set objUsrEnv = Nothing
Set objWshShell = Nothing
Dim objWShell
Set objWShell = CreateObject("WScript.Shell")
objWShell.Run "firefox.exe", 4, False
Set objWShell = Nothing
追記(2016/9/19):
試しにJScriptだけで「MOZ_NO_REMOTE」を削除してThunderbirdからFirefoxを起動するスクリプトを作ってみた。フリーウェア【MakeExe】でexeファイルに変えてThunderbirdで起動したみたら、問題なく起動した(一度しか試してない)。スクリプトは次の通り。「MOZ_NO_REMOTE」を削除する部分以外は上記とほとんど同じ(openFx.bat→Firefox.exe)。
var openFx = "\"Firefox.exe\"";
var geturl = WScript.Arguments;
if (geturl.length < 1) {var openurl = ""} else {
var openurl = geturl(0);
openurl = "\"" + openurl + "\"";
}
// shell.Popup(openurl); // Firefox.exeの引数を確認したければ有効に
var WshShell = WScript.CreateObject("WScript.Shell");
var WshSysEnv = WshShell.Environment("Process");
WshSysEnv.Remove("MOZ_NO_REMOTE");
var command = openFx + "\ " + openurl;
// shell.Popup(command); // 実行されるコマンドを確認したければ有効に
shell.run(command);
参考ページ:Environment プロパティ
追記(2016/9/21):
JScriptだけで「MOZ_NO_REMOTE」を削除してThunderbirdからFirefoxを起動するソフトを作るまでの経緯を別記事にまとめた。
【MOZ_NO_REMOTE を削除してThunderbirdからFirefoxを起動するソフトを作り直した】
私も-no-remoteを指定したサンダーバード内でURLをクリックし、firefoxを起動した際に「firefoxを閉じろ」がでて悩んでました。
>この Set MOZ_NO_REMOTE= が「MOZ_NO_REMOTE」や「-no-remote」の問題を解決するためにどうしても必要で、JScript では実現できそうにない
確かにこの部分が必要なようで、私はJScriptは触らないのでVBSで書きましたが、この部分は実現できました。
「VBS 環境変数 削除」 で検索して削除の仕方を調べた上で「MOZ_NO_REMOTE」を削除しました。JScriptでも出来るんじゃないですかね。
参考になったサイト
環境変数を削除する
http://www.whitire.com/vbs/tips0117.html
↑UserになってるところをProcessに変更しました
http://www.atmarkit.co.jp/ait/articles/0409/17/news086_2.html
>Environmentプロパティ
by TM (2016-09-18 15:23)
TMさん、コメントありがとうございます。
恥ずかしながら、自分で書いた文章(この記事のこと)ですらなかなか理解できませんでした。(^^;
当時のツイートなどを確認したところ、JScriptでも「MOZ_NO_REMOTE」を削除できるようです。
たぶん、次のURLのようにすれば良いのだと思います。
http://chicagrafo.blogspot.jp/2007/10/moznoremote.html
ただ、このURLを参考にしてexeファイルを作ってThunderbirdから起動したところ、うまくいかなかったようです。
2014/3/15の私のツイート。
https://twitter.com/self7777/status/444740575699283968
2014/3/16の私のツイート。
https://twitter.com/self7777/status/444989881647775744
それで「JScript では実現できそうにない」と書いたのかもしれません。
私はJScriptの知識がないので、それ以上考えるのはやめて、以前に作っておいた「MOZ_NO_REMOTE」を削除するbatファイルから作ったexeファイルのバグを修正するためだけにJScriptを使ったようです。
http://masami7777.blog.so-net.ne.jp/2014-03-16
その記事を読み直してみたら、「JScriptよりもVBScriptで作った方が動作が早いならVBScriptで作りたいが、能力不足で断念」と書いてありました。ちなみに、JScriptのプログラミング能力もありません。(^^;
ググってコピペするくらいしかできません。(^^;
exeファイルを作るのに使ったMakeExeはVBScriptもexeファイルにできるようですから、TMさんのコメントを参考にしてexeファイルを作ってThunderbirdから起動した方が良いかもしれませんね。
それにしても、私の読みにくい文章を読んでくださり、ありがとうございました。m(_ _)m
by 正己 (2016-09-18 17:38)