ardarimの日記 このページをアンテナに追加 RSSフィード

2011-12-14

[][] 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;
}

2011-11-23

[][][] ファイルの保存がうまくできない

JScript(WSH)からのOpenOffice.org制御でなんとなくそれっぽく動くものはできてきたのだけれど、現時点でファイルの保存のやり方がうまくいってなくて、ここでつまづいている。

どうやら、COM経由での呼び出しはqueryInterfaceは要らなくて、インタフェースの取得は自動でやってくれるらしいことはわかったのだけど、XStorable.store()だけがどうにもうまくいかない。XStorableインタフェースを取得するにはどうしたらよいのか…。


単純に、

xDoc.store();

だとエラーになってしまう。

[automation bridge] : com.sun.star.task.ErrorCodeIOException:

これの解決方法がわからない…。


問題としてはここ(OpenOffice.org Forum :: How do I save and terminate Calc file (Solved))と同じようなんだけど。ここでsolvedとされている方法(たぶん、マクロ記録から引っ張ってきたやり方だと思う)も試してみたけど、エラーにはならないけどどうもファイルとして保存されていない様子…。

storeProps = new Array();

storeProps[0] = OOo.Bridge_GetStruct("com.sun.star.beans.PropertyValue");

storeProps[0].Name = "URL";

storeProps[0].Value = "file:///" + pathName + "test.ods";

storeProps[1] = OOo.Bridge_GetStruct("com.sun.star.beans.PropertyValue");

storeProps[1].Name = "Overwrite";

storeProps[1].Value = true;

oDocCtrl = xDoc.getCurrentController();

oDocFrame = oDocCtrl.getFrame();

oDispatchHelper = OOo.createInstance("com.sun.star.frame.DispatchHelper");

oDispatchHelper.executeDispatch(oDocFrame, "uno.SaveAs", "", 0, storeProps);


ちゃんとドキュメント読め、という話なのかなあ…。めんどくさい…。