JScriptからInputBox

昨日からJScriptでInputBoxを実装する方法を探して苦労していたわけだが、これといった決め手が見つからない。


MSScriptControl.ScriptControlを使う方法(JScriptでinputBoxを使う » Jeans & Development)が割とスマートに思えたのだけど、Windows 7で実行してみたところ動作せず。どうやら、MSScriptControlはWindows Vista以降ではオプションでインストールは可能なものの*1、標準でインストールされてないと判明。


function inputBox_SC(prompt, title)
{
var result;
var objScr;

objScr = WScript.createObject("MSScriptControl.ScriptControl");
objScr.language="VBScript";
objScr.addCode(
"Function getInput()" +
" getInput = InputBox(\"" + prompt + "\", \"" + title + "\") " +
"End Function");
result = objScr.eval("getInput");

objScr = null;

return result;
}


それならば、と次はIE経由でInputBox呼び出せばOS依存性ないよね、ということで作ってみた。


function inputBox_IE(prompt, title)
{
var ie;
var result;

ie = WScript.createObject("InternetExplorer.Application");
ie.visible = false;
ie.navigate("about:blank");
while(ie.busy || ie.readyState < 3) WScript.sleep(100);
ie.document.write("<html><body><textarea id='text'></textarea></body></html>");
while(ie.busy || ie.readyState < 3) WScript.sleep(100);
ie.document.parentWindow.execScript(
"document.all.text.value = InputBox('" + prompt + "', '" + title + "')",
"VBScript");
result = ie.document.all.text.value;
ie.quit();
ie = null;

return result;
}

ただし、この方式の欠点は、IEのメインウィンドウを消すために ie.visible = false としているので、InputBoxダイアログがカレントウィンドウの背面に回ってしまうこと。一回Alt+Tabしないと前面に出てこない。
あと、Windows 7+IE8な環境では動作しなかった。
いろいろデバッグした結果、ローカル環境(about:blank)ではInputBox()はクロスサイトスクリプティング対策で弾かれてしまうらしい、ということがわかって、結局この方式も頓挫。どうやらIE8からの機能らしい。

クロスサイト スクリプティング (XSS) フィルター : Internet Explorer 8 のこの新しい機能により "リフレクション (タイプ I) XSS" の脆弱性を悪用することが難しくなります。 スクリプトは、サーバーの応答の生成に HTTP 要求の一部が使用されるときに反映できます。これにより、要求内の悪意のあるスクリプトは、残りのページと同じアクセス レベルで実行できるようになります。 XSS フィルターは、ブラウザー内を移動するすべての要求と応答を監視します。 フィルターは、クロスサイト要求でスクリプトを検出したとき、スクリプトがサーバーの応答で再生された場合に、そのスクリプトを特定して無効化します。 このような動作が発生した場合、メッセージ "Internet Explorer modified this page to prevent a potential cross-site scripting attack" が表示されます。

Internet Explorer 8 の新機能


最終的に、もういっそ、小細工しないでVBSそのまま呼べばいいじゃん!てことに思い至って無理やり作ってみた最終形がこれ。いやもう、そこまでJScriptに拘る必要ってあるのかどうかわからんけど…。


流れとしてはまったく大したことはしておらず、
(1)InputBox()を実行してWScript.EchoするだけのVBSファイルをテンポラリフォルダに作成。
(2)VBSファイルを実行し、終了待ち
(3)標準出力から実行結果を受け取る
(4)VBSファイルを削除
という感じ。ただの力技とも言う。たぶん環境依存せず可搬性ある。



function inputBox(prompt, title)
{
var WshRunning = 0;
var TemporaryFolder = 2;

var fso;
var wsh;
var cmdline;
var oExec;
var vbsFile;
var vbsFilePath;
var result;

fso = WScript.createObject("Scripting.FileSystemObject");
wsh = WScript.createObject("WScript.Shell");

// Creates a temporary VBS script file
vbsFilePath = fso.getSpecialFolder(TemporaryFolder).path + "\\inputbox.vbs";

vbsFile = fso.createTextFile(vbsFilePath, true, false);
vbsFile.writeLine("If WScript.Arguments.Count = 1 Then");
vbsFile.writeLine(" WScript.Echo InputBox(WScript.Arguments.Item(0))");
vbsFile.writeLine("End If");
vbsFile.writeLine("If WScript.Arguments.Count = 2 Then");
vbsFile.writeLine(" WScript.Echo InputBox(WScript.Arguments.Item(0), WScript.Arguments.Item(1))");
vbsFile.writeLine("End If");
vbsFile.close();
vbsFile = null;

// Invokes the VBS script and retrieve the result via StdOut
cmdline = "cscript.exe " + vbsFilePath + " //nologo";
if(prompt){
cmdline += " " + prompt;
if(title){
cmdline += " " + title;
}
}

oExec = wsh.exec(cmdline);
while(oExec.status == WshRunning){
WScript.sleep(100);
}

if(oExec.stdOut.atEndOfStream){
result = false;
} else {
result = oExec.stdOut.readLine();
}

oExec = null;

// Deletes the temporary VBS script file
fso.deleteFile(vbsFilePath, true);

wsh = null;
fso = null;

return result;
}