LoginSignup
3
3

More than 3 years have passed since last update.

最強のHTAを求めて。

Posted at

HTAのこれまでと現状については面倒なので略すが、現状HTAはまだ動くには動くが、デフォルトのままではIE7相当の機能でしか動かず、X-UA-Compatibleの指定でIE10以上に設定すると、HTA固有のHTA:APPLICATIONタグの機能が大きくスペックダウンし、起動パラメータが参照できなくなったり、外観や挙動の制御が制限されたりとかになってしまう。

それを回避するために、起動用のHTAを使って、旧バージョンでのフルスペックの機能設定を行った上で、本命のHTAにリダイレクトする、という方法が編み出された。

IE10モード以上のHTAでHTA:APPLICATIONオプションを使う方法 | while(isプログラマ)
https://am-yu.net/2015/04/26/ie10-ie11-htaapplication/

これは大変すばらしい技だが、構成が2つHTAに分かれてしまう、という欠点がある。そこで1つのHTAでこれを実現できないものか?とあれこれ考えて、とりあえず動くものが出来たのでメモ代わりにここに残す。

基本的な考え方は以下の通り。

・バッチファイルにWSH/JScriptとHTAを埋め込む。
・JScriptで起動用のHTAをテンポラリに生成し、元のHTAのHTA:APPLICATIONタグをコピーする。
・起動用HTAから元のHTAをリダイレクト表示してテンポラリファイルを削除する。

そのためのポイントは以下の通り。

・基本的な考えは前述の「IE10モード以上のHTAでHTA:APPLICATIONオプションを使う方法」。
・Chakraエンジンでは<!--がコメント扱いになることを利用して、バッチファイルとして記述したHTAに、自身をChakraエンジン用JScriptとして起動するバッチコマンドをHTAのコメントとして記述する。(上部)
・さらに同様にHTAのコメントとして起動用HTAの生成と実行を行うChakraエンジン用JScriptを記述する。(下部。HTA部分を/* */ でコメントアウトする都合上、下部に */ の追加が必要なので、ついでにコード部分を下に置いた。)

今回はそこまでやってないが、WSH/JScriptからHTAを起動するので、コマンドラインパラメータをオブジェクトにしてJSON化&encodeURIComponentしてgetパラメータで渡せば、コマンドライン・パラメータをHTAに引き渡せる。HTA:APPLICATIONのcommandLineプロパティを解析して取得するよりは全然楽なはず。

で、これがコード。バッチとして起動するのと、MSHTAコマンドから起動するのとでの微妙な違いをご覧いただきたい。

SampleHTA.bat
<!-- : ^
/*
@cscript //nologo //E:{1b7cd997-e5ff-4932-a7a6-2a9e636da385} "%~f0" %*
@exit /B
:↑でバッチ自体は終了するので、ここからはバッチからは無視される。
:↓ここに HTA コードを記述する (/*~*/のコメントは使えないので注意  */
/* /////////////////////////////////////////////////////////////////// -->

<!DOCTYPE html>
<html lang="ja">
<!--
サンプルダミーHTA --- ボタンを押しても何も起きません。
-->
<head>
    <meta http-equiv="content-type" content="text/html; charset=Shift_JIS">
    <meta http-equiv="content-script-type" content="text/javascript">
    <meta http-equiv="content-style-type" content="text/css">
    <meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
    <title>input box</title>
    <hta:application id="oHTA" 
        applicationname="input box" 
        border="thick"
        caption="yes"
        maximizebutton = "no"
        minimizebutton = "no"
        selection = "no"
        showintaskbar="yes"
        singleinstance="yes"
        sysmenu="yes"
        windowstate="nomal">
    <style type="text/css">
        * { margin: 0; padding: 0; }
    </style>
    <link rel="stylesheet" type="text/css"
        href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css" />
    <script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
    <script type="text/javascript"
        src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js"></script>
</head>
<body onresize="window.resizeTo(432,188);" onLoad="window.resizeTo(432,188);" style="background-color: whitesmoke;" >
<div style="position: absolute; left: 3mm; top: 3mm; font-size: 10pt;">
入力してください。
</div>
<input id="text1" type="text" name="text1" size="68" style="position: absolute; left: 3mm; bottom: 3mm; font-size: 10pt; height: 1.5em; ">
<button id="btnOK" type="button" name="btnOK" style="position: absolute; right: 3mm; top: 3mm; font-size: 10pt; height: 2em; width: 7em">OK</button>
<button id="btnCAN" type="button" name="btnCAN" style="position: absolute; right: 3mm; top: 13mm; font-size: 10pt; height: 2em; width: 7em">キャンセル</button>
<script type="text/javascript">
//
</script>
</body>
</html>

<!-- */ /////////////////////////////////////////////////////////////////
// ↑ HTA コードここまで。以下は起動用コード

genHTAkicker( WScript.ScriptFullName )();

// HTA 再起動
function genHTAkicker( file ) {
    var fso = typeof(fso)==='undefined' ? WScript.CreateObject('Scripting.FileSystemObject') : fso;
    var mch, str, xuae, ie=7, app, hta;
    file = fso.GetAbsolutePathName( file );
//  str = _.file2text( file );
    var f = fso.OpenTextFile( file, 1 );    //  iomode 1:ForReading
    if( ! f.AtEndOfStream ) str = f.ReadAll();
    f.close();

    // 雑な正規表現(1) X-UA-Compatible 定義の抽出
    if( mch = str.match(/\<.*"X\x2DUA-Compatible".*\>/) ) {
        xuae = mch[0];
        if( mch = xuae.match(/"IE=([^"]+)"/) ) ie = mch[1];
    }
    // 雑な正規表現(2) HTA:APPLICATION 定義の抽出
    if( mch = str.match(/\<HTA\x3AAPPLICATION[\S\s]*?\>/im) ) app = mch[0];

    if( parseInt(ie) <=9 ) return void 0;

    hta = "\
<!DOCTYPE html>\n\
<html>\n\
<head>\n\
    <meta http-equiv='X-UA-Compatible' content='IE=9' />\n\
    <meta http-equiv='refresh' content='0;URL=" + file + "?kicked'>\n\
    " + app + "\n\
</head>\n\
<body>\n\
    <script>\n\
        var fso = new ActiveXObject('Scripting.FileSystemObject');\n\
        window.addEventListener('unload', function(event) {\n\
            fso.DeleteFile('@@@@@@@@');\n\
        });\n\
    <\x2Fscript>\n\
</body>\n\
</html>";

    // kicker生成
    return function kickHTA(){
        var fso = typeof(fso)==='undefined' ? WScript.CreateObject('Scripting.FileSystemObject') : fso;
        var WshShell = typeof(WshShell)==='undefined' ? WScript.CreateObject('WScript.Shell') : fso;
//      var tempfile = _.genTempPathEasy();
        var tempfile = ( ((new Date()).toLocaleString()).replace( /([^\d])(\d)(?=[月日:]|$)/g, "$10$2" ) ).replace( /[^\d]+/g, "" );
        tempfile = fso.BuildPath( fso.GetSpecialFolder(2), tempfile );
        hta = hta.replace( /@@@@@@@@/, tempfile.replace(/\\/g,"/") );
//      _.text2file( hta, tempfile );
        var f = fso.CreateTextFile( tempfile, true );
        f.Write(hta);
        f.close();
//      WshShell.Run( 'MSHTA ' + tempfile + ' & del ' + tempfile, 1, true );
        WshShell.Run( 'MSHTA ' + tempfile, 1 );
    }
}

// この行は消さないこと。 -->

作るには作った。が、大袈裟すぎる。微妙な違いのためにこれかよ。どうしても1ファイルにしたいとき以外は2ファイル構成にする方がお手軽だし、IE10以降固有の機能まで必要ないなら、IE=9を指定してやればES5で使えるし、commandLineプロパティも含めHTA:APPLICATIONの機能がフルセットで動くので、それでいいじゃないのよ。というのが結論である。やれやれ。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3