はじめに (対象読者・この記事でわかること)

この記事は、HTA(HTMLアプリケーション)からHTMLファイルを起動する際に二重起動を防ぐ方法を探求しているデスクトップアプリ開発者や、Windows環境でJavaScriptを利用したアプリケーションを作成している開発者向けです。特に、HTAという比較的マイナーな技術に興味がある方や、Windowsのデスクトップアプリケーション開発でJavaScriptを活用したい方に最適です。

本記事を読むことで、HTAからHTMLを起動する際に発生しがちな二重起動の問題を解決する実践的なJavaScriptコードの書き方が学べます。具体的には、ローカルストレージを活用した起動状態の管理方法や、既存プロセスのチェック手法を理解し、自身のアプリケーションに実装できるようになります。また、HTA特有のセキュリティ制約とそれを回避する方法についても理解を深めることができます。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - HTML/CSS/JavaScriptの基本的な知識 - HTA(HTML Application)の基本的な理解 - Windows環境でのアプリケーション開発の基礎知識

二重起動問題の背景と重要性

HTA(HTMLアプリケーション)は、HTML、CSS、JavaScriptを組み合わせてWindowsのデスクトップアプリケーションを作成できる技術です。Web技術の知識がある開発者にとって、比較的容易にネイティブアプリケーションに近い体験を提供できる魅力的な選択肢です。

しかし、HTAから別のHTMLファイルを起動する際、単純なwindow.open()やShell.Applicationの使用では、ユーザーが誤って複数回クリックしてしまったり、システムのタイミングの問題で意図せず複数プロセスが起動してしまうことがあります。これにより、以下のような問題が発生します。

  1. リソースの無駄遣い:同じアプリケーションが複数起動すると、メモリやCPUリソースを不必要に消費します。
  2. データ競合:同じファイルにアクセスするアプリケーションが複数存在すると、データの整合性が保てなくなるリスクがあります。
  3. ユーザー体験の低下:意図しない複数ウィンドウの表示は、ユーザーを混乱させ、操作を煩雑にします。

特に、HTAから起動されるHTMLファイルがデータベースやファイルシステムにアクセスする場合、二重起動は深刻な問題を引き起こす可能性があります。そのため、二重起動を効果的に抑止する技術は、HTAベースのアプリケーション開発において不可欠となります。

具体的な実装方法とコード例

二重起動を抑止するためには、いくつかのアプローチが考えられます。ここでは、実践性と実装の容易さを考慮し、主に以下の3つの方法を詳しく解説します。

  1. ローカルストレージを利用した起動状態の管理
  2. プロセスの存在チェックによる制御
  3. ミューテックス(Mutex)の利用による排他制御

1. ローカルストレージを利用した起動状態の管理

まず、ブラウザのローカルストレージを利用してアプリケーションの起動状態を管理する方法です。この方法は比較的シンプルで実装が容易ですが、完全な二重起動防止には限界があります。

実装手順

ステップ1:起動フラグの設定

最初に起動するHTMLファイルで、ローカルストレージに起動フラグを設定します。

Javascript
// 起動フラグをローカルストレージに保存 localStorage.setItem('appRunning', 'true'); // ウィンドウが閉じられたときにフラグをクリア window.addEventListener('beforeunload', function() { localStorage.removeItem('appRunning'); });

ステップ2:起動チェックの実装

次に、HTAから起動されるHTMLファイル側で、起動フラグの存在をチェックします。

Javascript
// ページ読み込み時に起動フラグをチェック window.addEventListener('DOMContentLoaded', function() { // ローカルストレージから起動フラグを取得 const isRunning = localStorage.getItem('appRunning'); if (isRunning === 'true') { // 起動フラグが存在する場合、既にアプリケーションが実行中と判断 alert('アプリケーションは既に実行中です。'); window.close(); // ウィンドウを閉じる return; } // 起動フラグを設定 localStorage.setItem('appRunning', 'true'); // ウィンドウが閉じられたときにフラグをクリア window.addEventListener('beforeunload', function() { localStorage.removeItem('appRunning'); }); });

ステップ3:HTA側での起動制御

HTAからHTMLを起動する際に、このチェックを実装します。

Html
<!DOCTYPE html> <html> <head> <title>HTA起動テスト</title> <hta:application id="htaApp" applicationname="HTA起動テスト" icon="app.ico" navigable="no" scroll="no" singleinstance="yes" /> <script type="text/javascript"> function launchApp() { try { // WScript.Shellを使用してHTMLファイルを起動 var shell = new ActiveXObject("WScript.Shell"); var command = 'mshta.exe "file:///C:/path/to/your/app.html"'; shell.Run(command, 1, false); } catch (e) { alert('アプリケーションの起動に失敗しました: ' + e.message); } } </script> </head> <body> <button onclick="launchApp()">アプリケーションを起動</button> </body> </html>

ハマった点やエラー解決

この方法を実装する際に遭遇する問題として、HTAと起動されるHTMLファイル間でのローカルストレージの共有範囲があります。異なるドメインや異なるパスで実行される場合、ローカルストレージは共有されない可能性があります。

解決策

この問題を解決するには、起動されるHTMLファイルとHTAファイルが同じドメイン内に配置されていることを確認するか、Cookieを利用して起動状態を管理する方法が考えられます。また、HTAとHTMLファイルが異なるセキュリティゾーンに属する場合は、セキュリティ設定の調整が必要になる場合があります。

2. プロセスの存在チェックによる制御

次に、Windowsのプロセスを直接チェックして、既にアプリケーションが実行中かどうかを判断する方法です。この方法はより確実な二重起動防止が可能ですが、HTA特有のセキュリティ制約を回避する必要があります。

実装手順

ステップ1:プロセスチェック関数の実装

Javascript
// プロセスが存在するかチェックする関数 function isProcessRunning(processName) { try { var shell = new ActiveXObject("WScript.Shell"); var command = 'tasklist /FI "IMAGENAME eq ' + processName + '" /FO CSV /NH'; var output = shell.Exec(command).StdOut.ReadAll(); // 出力行数が2以上(ヘッダ行+データ行)の場合、プロセスは実行中 return (output.split('\n').length > 1); } catch (e) { console.error('プロセスチェック中にエラーが発生しました: ' + e.message); return false; } }

ステップ2:起動前のチェックを実装

HTAからHTMLを起動する前に、既に同じプロセスが実行中かチェックします。

Javascript
function launchApp() { // mshta.exeプロセスが既に実行中かチェック if (isProcessRunning('mshta.exe')) { alert('アプリケーションは既に実行中です。'); return; } try { var shell = new ActiveXObject("WScript.Shell"); var command = 'mshta.exe "file:///C:/path/to/your/app.html"'; shell.Run(command, 1, false); } catch (e) { alert('アプリケーションの起動に失敗しました: ' + e.message); } }

ステップ3:HTMLファイル側でのプロセス監視

起動されるHTMLファイル側でも、プロセスの状態を監視します。

Javascript
window.addEventListener('DOMContentLoaded', function() { // ウィンドウが閉じられる際に処理を実行 window.addEventListener('beforeunload', function() { // 必要なクリーンアップ処理を実行 }); // 定期的にプロセスをチェック(任意) setInterval(function() { // 必要に応じてプロセス状態をチェック }, 5000); });

ハマった点やエラー解決

この方法では、ActiveXObjectを使用するため、HTAのセキュリティ設定によってはスクリプトが実行されない可能性があります。

解決策

HTAのセキュリティ設定を調整する必要があります。HTAファイルの<head>タグ内に以下のような設定を追加することで、セキュリティ制約を緩和できます。

Html
<hta:application id="htaApp" applicationname="プロセスチェックテスト" navigable="no" scroll="no" singleinstance="yes" windowstate="normal" />

また、singleinstance="yes"属性を追加することで、HTA自体の二重起動を防ぐことも可能です。ただし、これはHTA自体の二重起動防止であり、HTAから起動されるHTMLファイルの二重起動防止にはならない点に注意が必要です。

3. ミューテックス(Mutex)の利用による排他制御

最後に、より確実な二重起動防止を実現するミューテックス(Mutex)を利用する方法です。ミューテックスは、複数のプロセス間でリソースを排他的に制御するための同期オブジェクトです。

実装手順

ステップ1:ミューテックスを作成する関数の実装

Javascript
// ミューテックスを作成し、既に存在するかチェックする関数 function createMutex(mutexName) { try { // Scripting.FileSystemObjectを使用してミューテックスファイルを作成 var fso = new ActiveXObject("Scripting.FileSystemObject"); var mutexFile = fso.CreateTextFile(mutexName + ".mutex", true); mutexFile.Close(); // ミューテックスファイルが正常に作成できた場合、trueを返す return true; } catch (e) { // ファイル作成に失敗した場合(既に存在する)、falseを返す return false; } } // ミューテックスを解放する関数 function releaseMutex(mutexName) { try { var fso = new ActiveXObject("Scripting.FileSystemObject"); if (fso.FileExists(mutexName + ".mutex")) { fso.DeleteFile(mutexName + ".mutex"); } return true; } catch (e) { console.error('ミューテックスの解放に失敗しました: ' + e.message); return false; } }

ステップ2:HTAでのミューテックスチェックと起動

Javascript
function launchApp() { var mutexName = "MyAppMutex"; // ミューテックスを作成できたかチェック if (!createMutex(mutexName)) { alert('アプリケーションは既に実行中です。'); return; } try { var shell = new ActiveXObject("WScript.Shell"); var command = 'mshta.exe "file:///C:/path/to/your/app.html"'; shell.Run(command, 1, false); } catch (e) { alert('アプリケーションの起動に失敗しました: ' + e.message); // エラーの場合はミューテックスを解放 releaseMutex(mutexName); } }

ステップ3:HTMLファイル側でのミューテックス管理

Javascript
window.addEventListener('DOMContentLoaded', function() { var mutexName = "MyAppMutex"; // ページが閉じられる際にミューテックスを解放 window.addEventListener('beforeunload', function() { releaseMutex(mutexName); }); // アプリケーションのメイン処理 initApp(); }); function initApp() { // アプリケーションの初期化処理 console.log('アプリケーションを初期化します'); }

ハマった点やエラー解決

ミューテックスを利用する方法では、ファイルシステムへのアクセスが必要なため、ディレクトリのアクセス権限や一時ディレクトリの使用に関する問題が発生することがあります。

解決策

  1. 一時ディレクトリの使用:システムの一時ディレクトリ(%TEMP%)にミューテックスファイルを作成することで、アクセス権限の問題を回避できます。
Javascript
function getTempPath() { var shell = new ActiveXObject("WScript.Shell"); return shell.ExpandEnvironmentStrings("%TEMP%"); } function createMutex(mutexName) { try { var fso = new ActiveXObject("Scripting.FileSystemObject"); var tempPath = getTempPath(); var mutexFile = fso.CreateTextFile(tempPath + "\\" + mutexName + ".mutex", true); mutexFile.Close(); return true; } catch (e) { return false; } }
  1. ミューテックスファイルのロック:ミューテックスファイルを開いたままにすることで、他のプロセスからファイルが削除されないようにすることも可能です。
Javascript
function createMutex(mutexName) { try { var fso = new ActiveXObject("Scripting.FileSystemObject"); var tempPath = getTempPath(); var mutexFile = fso.OpenTextFile(tempPath + "\\" + mutexName + ".mutex", 8, true); // 8 = ForAppending mutexFile.Write("locked"); mutexFile.Close(); return true; } catch (e) { return false; } }

まとめ

本記事では、HTAからHTMLを起動する際の二重起動を抑止する3つの実用的な方法について解説しました。

  • ローカルストレージを利用した起動状態の管理:比較的シンプルな実装ですが、セキュリティ制約や共有範囲の問題があります。
  • プロセスの存在チェックによる制御:より確実な二重起動防止が可能ですが、HTAのセキュリティ設定の調整が必要です。
  • ミューテックス(Mutex)の利用による排他制御:最も確実な方法ですが、ファイルシステムへのアクセスが必要で、実装が複雑になります。

この記事を通して、HTAベースのデスクトップアプリケーション開発における二重起動問題の解決策を理解し、自身のプロジェクトに適用できるようになったことを願っています。今後は、これらの技術を応用して、より高度なデスクトップアプリケーション開発についても記事にする予定です。

参考資料