markdown
はじめに (対象読者・この記事でわかること)
本記事は、Webフロントエンドエンジニアや、ブラウザ上で動く JavaScript を使ってデスクトップ環境と連携したいと考えている方を対象としています。
「JavaScript だけで、同じ PC に表示されている別アプリのウィンドウを検知できるのか?」という疑問に対し、ブラウザのサンドボックス制限や OS のセキュリティモデルを踏まえて結論を示します。さらに、どうしてもウィンドウ情報が必要な場合の代替手段(Electron、ネイティブブリッジ、WebSocket 連携)も具体的に解説します。この記事を読むことで、実装可能かどうかの判断と、実装に向けた選択肢が明確になるでしょう。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。
- HTML/CSS と基本的な JavaScript の知識
- Node.js や npm の基礎的な操作経験
- デスクトップ OS(Windows/macOS)の一般的な概念(ウィンドウマネージャ、プロセス)
JavaScript単体でウィンドウ検知はできない理由
ブラウザは Web コンテンツを安全に実行するため、サンドボックス と呼ばれる強力な制限を設けています。主な制限は以下の通りです。
-
同一オリジンポリシー
JavaScript は自分がロードされたオリジン(例: https://example.com)以外のリソースに直接アクセスできません。デスクトップ上の他アプリは全く別のオリジンに相当し、API が提供されない限り情報取得は不可です。 -
OSレベルの権限がない
ブラウザはユーザーのデスクトップ環境に直接働きかける権限を持ちません。ウィンドウの一覧取得やフォーカスの監視は、OS が提供するシステムコールやネイティブ API(例: Windows のEnumWindows、macOS のCGWindowListCopyWindowInfo)を呼び出さなければ実現できません。 -
プライバシー保護
ユーザーが意図しないアプリ情報の取得はプライバシー侵害につながります。そのため、主要ブラウザはウィンドウ情報へのアクセスを明示的にブロックしています。
以上の理由により、純粋なブラウザ上の JavaScript だけで PC 上の他ウィンドウを検知することは不可能です。
代替手段と実装例:Electron とネイティブブリッジ
ウィンドウ検知がどうしても必要なシナリオ(例: カスタムデスクトップツール、マルチウィンドウ同期アプリ)では、JavaScript の実行環境をブラウザに限定せず、Node.js が統合されたデスクトップランタイムを利用します。代表的な選択肢は Electron です。以下に、Electron で他ウィンドウ情報を取得する基本的な流れを示します。
ステップ1:プロジェクトの作成と依存パッケージのインストール
Bash# プロジェクトディレクトリ作成 mkdir win-detect && cd win-detect # npm 初期化 npm init -y # Electron とウィンドウ情報取得用パッケージをインストール npm install --save electron node-window-manager
electronは Chromium と Node.js を組み合わせたランタイムです。node-window-managerは Windows のウィンドウ列挙 API をラップした Node.js モジュールで、macOS 版は@johannschopplich/macos-window-managerなどを利用します。
ステップ2:メインプロセスでウィンドウ情報を取得
main.js を作成し、以下のコードを書き込みます。
Javascriptconst { app, BrowserWindow, ipcMain } = require('electron'); const { WindowManager } = require('node-window-manager'); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, // レンダラープロセスで Node API を使用可能に contextIsolation: false } }); win.loadFile('index.html'); } // アプリ起動時にウィンドウを生成 app.whenReady().then(createWindow); // IPC ハンドラ:レンダラ側からウィンドウ情報要求を受け取る ipcMain.handle('get-other-windows', async () => { // 全ウィンドウを列挙 const windows = WindowManager.getWindows(); // 現在の Electron アプリ自身のウィンドウは除外 const filtered = windows.filter(w => !w.title.includes('Electron')); // 必要な情報だけ抽出 return filtered.map(w => ({ handle: w.handle, title: w.getTitle(), bounds: w.getBounds(), processId: w.getProcessId() })); });
ポイント解説:
node-window-managerのgetWindows()が OS のウィンドウ一覧を取得します。ipcMain.handleで renderer から非同期にデータを要求でき、セキュリティ的に安全です。title.includes('Electron')で自アプリのウィンドウを除外していますが、用途に合わせてフィルタ条件は変更してください。
ステップ3:レンダラ側で結果を表示
index.html と renderer.js を作成します。
index.html
Html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ウィンドウ検知デモ</title> </head> <body> <h1>PC 上の他ウィンドウ一覧</h1> <button id="detectBtn">検知開始</button> <ul id="winList"></ul> <script src="renderer.js"></script> </body> </html>
renderer.js
Javascriptconst { ipcRenderer } = require('electron'); document.getElementById('detectBtn').addEventListener('click', async () => { try { const windows = await ipcRenderer.invoke('get-other-windows'); const list = document.getElementById('winList'); list.innerHTML = ''; windows.forEach(w => { const li = document.createElement('li'); li.textContent = `タイトル: ${w.title} | プロセスID: ${w.processId}`; list.appendChild(li); }); } catch (err) { console.error('ウィンドウ取得エラー:', err); } });
ステップ4:アプリの実行
Bashnpx electron .
実行するとウィンドウが立ち上がり、「検知開始」 ボタンをクリックすると現在開いている他のアプリケーションウィンドウのタイトルとプロセス ID が一覧表示されます。
ハマった点やエラー解決
| 現象 | 原因 | 解決策 |
|---|---|---|
node-window-manager が Windows 以外で動作しない |
モジュールが Windows の WinAPI に依存している | macOS では @johannschopplich/macos-window-manager に置き換えるか、ffi-napi で自前のブリッジを書く |
Electron のウィンドウが取得対象に含まれてしまう |
フィルタ条件が不十分 | w.getClassName() などで Electron のクラス名を除外 |
contextIsolation が有効だと require が使えない |
セキュリティ設定 | preload.js で contextBridge.exposeInMainWorld を利用し、必要な API のみ公開する |
まとめ
本稿では、ブラウザだけの JavaScript では OS のウィンドウ情報へアクセスできない ことを、サンドボックスとセキュリティの観点から解説しました。その上で、Electron やネイティブブリッジ を用いることで、JavaScript からデスクトップウィンドウを検知する実装例を示しました。ポイントは以下の通りです。
- ブラウザは OS 直接操作権限を持たないため、ウィンドウ検知は不可能。
- Electron のように Node.js が組み込まれた環境なら、ネイティブ API へアクセスできる。
node-window-manager等のライブラリで、プラットフォームごとのウィンドウ列挙が可能。- セキュリティを保つために、IPC と
preloadスクリプトで最小限の権限だけを公開すべき。
これらを踏まえ、必要に応じて デスクトップアプリ向け の開発へ移行すれば、JavaScript でもウィンドウ情報を取得できるようになります。
参考資料
- Electron 公式ドキュメント
- node-window-manager GitHub リポジトリ
- macOS Window Manager (ffi‑napi) 実装例
- Web Security - Same‑origin policy