- 更新日: 2018年10月23日
- 公開日: 2018年09月21日
JavaScriptでWindowsアプリ(HTA)を作る
前回まで、WSHでコマンドラインベースの「Windowsで動くJavaScriptプログラム」を作ってみました。今回は、独自のウインドウを持ちHTMLでGUIを作成できるJavaScriptプログラム(HTA)を試してみたいと思います。
HTAは、WHSと同じくWindowsでJavaScript(JScript)ベースのプログラムを実行する仕組みです。WSHはJavaScriptの文を並べた純粋な「スクリプトファイル」の形で作成しましたが、HTAはWeb用のHTMLファイルと同じくHTML+CSS/JavaScriptで作成することになります。
HTAの中身は、Webブラウザ(IE)向けHTMLファイルとほとんど同じです。感覚的にはWindowsの独自機能を利用できるWebアプリ、というイメージに近いかもしれません。
HTAを作ってみる
たとえば、以下のHTMLファイルをtest.htmlなど適当な名前で作成してダブルクリックすると、WebブラウザでWebページとして表示されます。
<!DOCTYPE html>
<html>
<head>
<title>123</title>
</head>
<body>
<p>HTA Test</p>
</body>
</html>
次に、拡張子を「.hta」に変えてダブルクリックしてみましょう。今度は、Webブラウザではなく独自のウインドウが表示されその中にHTMLのレンダリング結果が表示されます。
つまり、Webブラウザが読み込んで表示するWebページとしてではなく、HTAの実行エンジンで実行されるHTAアプリケーションとして動作したわけです。
HTAの実行エンジンはIEベースなので、機能はIEに準じます。通常のHTML/CSS/JavaScriptはもちろん、ActiveXも使うことができます。
WebブラウザとしてのIEではActiveXの実行に制限があり、ActiveXの機能を使ったJScriptコードを実行しようとするとユーザーの同意が必要、あるいは実行自体ができないといった場面も多々ありました。
一方、HTAではローカル資源(ActiveXなどのプログラムやファイル)へのアクセスがより直接的に行えるようになるため、WSHと同じく事実上「Windowsで動くJavaScriptプログラム」として開発を行うことができます。
HTAでは、通常のWebアプリだけでなくWebベースのGUIでローカルのWindowsプログラムやコンポーネント(ActiveX)を利用するGUIツールを作成することもできるわけです。
HTAからコマンドラインを実行してみる
まずは、Windowsプログラムを呼び出す流れを確認するために、HTAでコマンドラインを実行してみましょう。といっても、処理としてはWSHの時と同じくWScript.Shellオブジェクトを作成し、コマンドラインを渡すだけです。
<!DOCTYPE html>
<html>
<head>
<title>notepad test</title>
</head>
<body>
<button onclick="test()">test</button>
<script>
function test() {
var shell = new ActiveXObject("WScript.Shell");
shell.Exec('notepad');
}
</script>
</body>
</html>
testボタンをクリックすると、コマンドライン「notepad」が実行され、メモ帳が起動します。
コマンドラインは文字列ですので、JavaScriptで作成する引数を追加することも可能です。HTAであれば、フォーム部品をはじめとするHTMLのGUIで設定画面を作成して設定を反映した引数でプログラムを起動する、つまり「プログラムをユーザーが指定した設定で起動するGUIランチャー」を作ることもできるでしょう。
実際に、テキスト入力欄を配置して入力内容を引数に渡すHTAを作成してみましょうか。
HTAには、テキスト入力欄(input要素、idはtest)を配置して、入力内容をWindowsのfindコマンドに渡すことにします。
findコマンドは、検索する文字列と検索パス(ファイルの場所やファイル名)を指定すると指定文字列を含むファイルを列挙するコマンドです。
検索文字列に「html」を、検索パスに入力欄の文字列を指定してfindコマンドを呼び出すHTAを作成してみることにします。
<!DOCTYPE html>
<html>
<head>
<title>find test</title>
</head>
<body>
<input id="file_field">
<button onclick="test()">test</button>
<script>
function test() {
var shell = new ActiveXObject("WScript.Shell");
var file_key = document.getElementById('file_field').value;
var e = shell.Exec('find "html" ' + file_key);
alert(e.StdOut.ReadAll());
}
</script>
</body>
</html>
htaを起動したら、入力欄に「*.hta」など適当なファイル指定文字列を入れてtestボタンをクリックしてみてください。
ボタンがクリックされると、入力欄の文字列を元に引数を作成した上でfindコマンドの処理を行い、結果を表示します。HTAファイルには「html」の文字列が含まれるので、実行したhtaファイルが表示されたと思います。
コマンドラインの実行には、WSHの時と同じくShellオブジェクトのExec()を使うので、標準出力からコマンドの出力(処理結果)を得ることができます。今回は、とりあえずコマンドの処理結果をalert()でダイアログ表示してみましたが、実際にHTAを開発する際にはHTML要素を活用して見やすい画面にまとめていきたいところですね。
\Webサイト担当者としてのスキルが身に付く/
HTAにコマンドライン引数を渡す
これでユーザーからの入力をWindowsのプログラムに渡すことができるようになりました。ただ、「他のプログラムから渡された情報」も扱いたい場面もあるかもしれませんね。
たとえば、WSHから文字列を渡してHTAを起動する、といったことができれば、活用の幅も広がりそうです。まずWSHで簡単な前処理やユーザーの環境の確認を行い、その情報を渡してHTA(GUIによる確認や本処理を行うプログラム)を起動する、といったこともできるでしょう。
HTAでは、HTA内にhta:applicationタグを設置することで、コマンドラインの取得やウインドウの表示を変えるといったより「Windowsアプリケーションらしい」機能を追加することができます。
applicationタグは、通常のHTML要素と同じ書き方で追加します。コマンドラインの取得などJScriptからアクセスする場合は、idなど「JScriptからアクセスできる識別子」を入れておくのを忘れないようにしましょう。
applicationタグにはcommandLineというプロパティがあり、そこにコマンドライン文字列が格納されます。コマンドライン文字列にはhta起動時に渡された引数も格納されているので、他のプログラムから引数を追加してやれば、HTA側でその情報を取得することができるわけです。
では、application要素のcommandLineを読み取るHTAを作ってみましょう。
<!DOCTYPE html>
<html>
<head>
<hta:application id="app" />
<title>123</title>
</head>
<body>
<button onclick="test()">test</button>
<script>
function test() {
alert(document.getElementById('app').commandLine);
}
</script>
</body>
</html>
とりあえず、「app」のidでapplication要素を配置して、ボタンがクリックされたらidからapplication要素を取得するようにしてみました。
このHTAをコマンドラインで実行するには、HTAの実行プログラムであるmshtaを呼び出します。mshtaには、URIで実行するHTAを指定できるので、適当な名前で保存したらfile://に続いてファイルの絶対パスを指定してください。
たとえば、HTAがd:\hta\test.htaなら、
mshta file://d:\hta\test.hta test1 test2
のような感じでコマンドを実行します。
最後に適当な引数を指定してコマンドを実行すると、HTAが表示されたと思います。ボタンをクリックすると、起動時に指定したファイルパスと引数がダイアログで表示されたはずです。
引数を含むコマンドラインを取得できたので、実際に渡された引数を利用してみましょう。
先ほどのfindによる文字列検索HTAを修正し、引数として「検索文字列 検索ファイルパス」を受け取るとfindコマンドで検索文字列を含むファイルを検索ファイルパスの中から探して表示するHTAを作成してみました。
引数の処理は、入力ミスなどへの対応も入れていくとかなり複雑化しますが、今回は単純化してコマンドラインをsplit(' ')でスペース一つを区切りとして分割し、最後の2つを引数とみなします。
<!DOCTYPE html>
<html>
<head>
<hta:application id="app" />
<title>find test</title>
</head>
<body onload="init()">
<p><span id="args_view"> </span><br>
<button onclick="test()">test</button></p>
<script>
var arg1;
var arg2;
function init() {
var command = document.getElementById('app').commandLine;
var args = command.split(' ');
arg1 = args[args.length - 2];
arg2 = args[args.length - 1];
document.getElementById('args_view').innerHTML = arg1 + "," + arg2;
}
function test() {
var shell = new ActiveXObject("WScript.Shell");
var e = shell.Exec('find "' + arg1 + '" ' + arg2);
alert(e.StdOut.ReadAll());
}
</script>
</body>
</html>
適当な名前でファイルを作成したら
mshta file://d:\hta\find2.hta html *.hta
といったコマンドラインで実行すると、HTAが起動され渡したコマンド引数(上の例ではhtmlと*.hta)が表示されます。さらに、ボタンをクリックすると、test()が実行され実際にfindコマンドの呼び出しを行い処理結果が表示されたはずです。
さて、先のHTAのコードを見ると「innerHTML」という古臭いプロパティにアクセスしていますね。実は、HTAはデフォルトだとIE7相当の環境で実行されるため、新しい記述や機能を利用できません。
さすがにIE7だと、機能面ではcanvasをはじめいわゆる「HTML5」の機能も使えないものが多く、可能性を狭めてしまいますね。Windows Vistaの時代に逆戻りですから……
HTA(IE向けページ)では、X-UA-Compatibleで実行モードの設定を行えるので、「IE9」相当にしてみましょう。
<meta http-equiv="X-UA-Compatible" content="IE=9" />
この文をhead内に入れると、IE9相当で動作するようになります。
document.getElementById('args_view').innerHTML = arg1 + "," + arg2;
の部分も、
document.getElementById('args_view').textContent = arg1 + "," + arg2;
といった記述が可能になるので、試してみてください。
ただし、IE10以上にするとapplicationタグが利用できなくなる、という罠があります。実用上はIE9で対応できることが多いでしょうから、HTAはIE9相当で作るのが無難かもしれません。
- この記事を書いた人
- 宍戸輝光