gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

ブラウザからアプリ(EXE)を呼び出す方法調査(プロトコルハンドラー、カスタムURLスキーム、URIハンドラー)

2023/06/24 追記 どんな方法があるの? を思いついたので追記

2021/12/13 追記 「URIハンドラー」という用語や参考リンクを追記。

2021/11/20 追記 GitHubのサンプルへのリンクを追加。

2021/11/13 追記 拡張子の関連付けの方法を追記、全体構成を加筆修正。

何がしたいの

ブラウザからクライアントアプリ(EXE)を呼び出したい。

身近な例としては メールアドレスをクリック→メーラーが起動

ってのが一番身近だと思う。

どんな方法あるの?

ActiveX経由

  • 基本的にIEしか動かない。
  • Chromeだと謎のアプリをインストールする必要がある

IEは今後サポート切れになるので没

NPAPI

→没

JavaApplet

  • Java11から廃止されている
  • 今時JavaAppletは・・・

→没

ファイルを開く

事前にファイルの関連付けを設定しておき※

ファイルをダウンロードする際に「開く」をすれば、任意のプログラムを起動できる。

コマンドプロンプト assoc - [ファイルと拡張子の関連を表示・設定する] とか

edgeの場合↓

この方法なら、ファイルにパラメータを埋め込んでおけば、 ほぼ無制限にパラメータを渡すことができる。

ただし、

  • 毎回「開く」をクリックしないといけない。
  • ダウンロードフォルダにtempファイルが一時的に溜まる
  • 操作をミスるとファイルがDLされてファイルが溜まってしまう

というデメリットが存在する

→ 実現はできるが、ベストではない感じはする。

ローカルにWEBサーバーを立てて、そのサーバー経由でアプリを呼び出す

2023/6/24追記

ローカルにWEBサーバーを立てて、

http://localhost:8080/hogehoge/fugafuga

みたいなURLをクリックすると、WEBサーバーにリクエストが飛ぶので、 それをトリガーとしてアプリを呼ぶ。

後述する方法は大量データを受け渡しができない。 この方法ならデータのやり取りはある程度自由にできるし、WEBの仕組みを全部使える。

ただし、デメリットとして、ローカルにWEBサーバーを立てる手間が発生する。 実務的に言うなら、ローカルサーバーを起動するアプリのインストーラーを配って面倒をみないといけなくなる。 あと、ポートの競合問題なども地味に面倒そう。

レジストリに追記する

レジストリを弄った後に、

<a href="mailto:hogehoge@example.com?subject=件名&amp;body=本文">メールを送信する</a>

のようなURLをクリックすると呼べるようになる。

以下参考文献

windows - ActiveXを使わずにwebブラウザ上でクライアントのexeを実行する方法 - スタック・オーバーフロー

Registering an Application to a URI Scheme (Windows) | Microsoft Learn

→ 今回の本題。以下に詳細を記載

レジストリに追記する」の具体例

サンプルサイトは下記。

クライアント呼び出し

ソース → gologius.github.io/test/blowser_to_call_exe/test1.html at master · gologius/gologius.github.io · GitHub

1)自分のPCのレジストリに下記のようなものを登録する

xyz_regist.reg

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\xyz]
"URL Protocol"=""

[HKEY_CLASSES_ROOT\xyz\shell]

[HKEY_CLASSES_ROOT\xyz\shell\open]

[HKEY_CLASSES_ROOT\xyz\shell\open\command]
@="\"C:\\Windows\\System32\\notepad.exe\" \"%1\""

2)html内に <a href="xyz:">ここをクリックするとメモ帳が開きます</a> などと記載しておく

3)リンクをクリックする

するとメモ帳が開く

内部で何してるの?

これはプロトコルハンドラーと呼ばれる技術を使用している。 カスタムURLスキームや、URIハンドラーとも呼ばれるらしい。呼び方を統一してほしい・・・

Edgeの設定だと「アプリケーション リンク」、chromeだと「プロトコル ハンドラ」の設定が該当する。

先のメールアドレスの例だと、下記のような感じのHTMLが埋め込まれているのが一般的になる

<a href="mailto:hogehoge@example.com?subject=件名&amp;body=本文">メールを送信する</a>

コロン ':' の後に文字列をセットすると、その文字列を別アプリに渡すことができる。

別アプリに値を渡す

レジストリキーに %1 が登録されていた場合、パラメータをアプリに引き渡す

(Default) = "C:\Windows\notepad.exe" "%1"

エンコードされた文字列が連携されるため、クライアント側でデコードして処理する必要がある。 よって、文字コードなどにも注意する必要がある。

%1 以外の意味 → Windows レジストリ 解剖記: HKEY_CLASSES_ROOT - Programming Field

<a href="xyz:hogehoge">ここをクリックするとメモ帳が開きます</a>

の場合だと

C:\レジストリに登録した.exe xyz:hogehoge

のような感じでOS側でコールされる模様

サンプルサイト

下記サイトで伝送できるバイト値を変えて遊べるようにした。

クライアント呼び出し

ソース → gologius.github.io/test/blowser_to_call_exe/test2.html at master · gologius/gologius.github.io · GitHub

サンプルレジストリバッチを起動しているなら

[HKEY_CLASSES_ROOT\xyz\shell\open\command]
@="\"C:\\Windows\\System32\\notepad.exe\" \"%1\""

になってるはずなので、メモ帳が開く先のファイルパスにランダム文字列を指定することになる。

使い方は単純で、バイト数入力→反映ボタンを押すとそのバイト数に応じたURLを生成→リンククリックでメモ帳が開く

※ただしファイルパスですらないのでエラーは出る。

こんな感じのリンクが裏では作られている↓

<a id="exetarget_ex" href="xyz:5nBGDkabx9tz4eCrl4xsEjMZbzle9v">サンプルアプリxyz呼び出し バイト数指定</a>

下記はChromeの例

判明している不具合

大量のデータを伝送できない模様。 特に日本語をbase64エンコードしようものなら、バイト量が増えてアプリが開かなくなるパターンを見つけた。

ちなみに、ブラウザによって限界値が違う模様。

  • chrome → 1000バイトまではメモ帳起動する。3000バイトは起動すらしない
  • firefox → 10000バイトまではメモ帳起動する。100000バイトは起動すらしない

※厳密に何バイトまでいけるかという実験ではないのでご了承下さい。

原因考察

二つある気がしている

  • ブラウザ側の伝送制限(そうでないと、ChromeFirefoxとの挙動差の説明がつかない)
  • OS側の制限 ※1

※ 文字数制限がある。8191文字が最大っぽい。が上記の結果とつじつまが合わない・・・

コマンド プロンプトの行文字列の制限 - Windows Client | Microsoft Learn

余談

C#でつくったWindowsFormアプリに値を飛ばしてみた。 ここでもブラウザの違いが見られた。 パラメータに全角文字をそのままぶちこんで飛ばすと、

された。

Chrome

Firefox

作ったもの github.com

所感

まぁ便利なんですが、下記のようにセキュリティ的なデメリットもあります。

カスタムURLスキームの乗っ取りとその対策 - Akaki I/O

パラメータにOSコマンド入れたりすれば、OSへの攻撃もできそうですよね。

レジストリを弄ってないと開かないとはいえ、注意したほうがいいと思いました。

というか、ブラウザからOSの機能にアクセスできるってのが個人的には怖いですね。