Yabutan 技術ブログ > Tauriアプリをシングルインスタンスで実装する

Tauriアプリをシングルインスタンスで実装する

#Windows#Tauri#Win32#Rust

2022-11-20

この記事では、TauriアプリをWindowsでもシングルインスタンスで実装する方法を紹介します。

Tauriで作成したWindowsアプリは、通常ではexeファイルを起動するたびに新規プロセスが立ち上がります。 起動プロセスを一つに限定したい場合、Tauri Plugin single-instance というものを使うとそれが実現できます。 既にアプリケーションが起動している場合、新規に立ち上げたプロセスはすぐに終了。 すでにあるプロセスにフォーカスするような挙動が可能です。

Electronでいう、requestSingleInstanceLock と同じような挙動です。

Tauriとは

Tauriは、Electronアプリのような、ブラウザベースのデスクトップアプリを作るためのフレームワークです。

Electronと違う点は、メインプロセス側がRust実装になっているのと、 ブラウザはChromiumを使うのではなく、OS側にインストールされているWebViewを利用します。

ビルドすると単一のexeファイルになるので配布しやすいのと、パフォーマンスも優れているのが特徴です。

シングルインスタンスとは

Windowsのアプリケーションは、起動したら起動しただけ、プロセスが新規で立ち上がるのが基本です。 しかし、同じリソースを共有するアプリケーションの場合には、起動できるプロセスをひとつに限定したい場合があります。

アプリケーションインスタンスを一つに限定したい場合、Windowsでは、Win32APIで提供されているプロセス間で共有できるMutexを作成して利用します。 このAPIコールを利用してライブラリ化しているのが、今回紹介するTauri Plugin single-instance です。

プロセス間共有のMutexを作る際には、共有するアプリケーション間で使うユニークな文字列が必要になります。 このプラグインでは、tauri.config.json のidentifier に設定の文字列が利用されます。

Tauri Plugin single-instance

このプラグインは、crate.io には上がっていないので、 Cargo.tomlに、直接 GitHub のURLを記載して利用することになります。

https://github.com/tauri-apps/tauri-plugin-single-instance

MacOSは、最初から単一インスタンスでアプリケーション起動するので必要ないですが、 WindowsとLinux用にシングルインスタンスを実装するのに使えるプラグインです。

既にプロセスがあった場合に、 もとから起動していたプロセスに通知を送って、新しく起動したプロセスは即時に終了します。 先に起動していたプロセスは通知がくるので、その際に挙動を制御できます。

今回の例だと、最小化などを解除して、フォーカスを設定しています。

使用例

src-tauri/Cargo.toml

crate.io にはないので、Cargo.tomlに直接 GitHubのURLを記載して利用します。

[dependencies]

# single instance
tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/tauri-plugin-single-instance" }

src-tauri/main.rs

プラグインのtauri_plugin_single_instance::init関数をコールして登録。 ここで登録したハンドラー内に、別プロセス起動時に通知が来ます。 別プロセスに渡されたコマンド引数の情報も含めてくるので、何かしらに使えそうです。

tauri::Builder::default()
    .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| {

        //  別プロセス起動時に渡されたコマンド引数の情報も含んで通知される。
        info!("{}, {argv:?}, {cwd}", app.package_info().name);

        // メインウィンドウを取得
        if let Some(main_window) = app.get_window("main") {
            
            // 最小化されてる場合は解除。
            if let Err(e) = main_window.unminimize() {
                error!("failed to un-minimize: {}", e);
            }
                            
            // フォーカスを有効にする。
            if let Err(e) = main_window.set_focus() {
                error!("failed to set focus: {}", e);
            }
        }
    }))
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
  • set_focus フォーカスを活性化する
  • unminimize タスク最小化状態からもとに戻す

src-tauri/tauri.conf.json

シングルインスタンス実現のため、 tauri.conf.jsonに設定している、tauri.bundle.identifier をユニーク文字列として利用します。 (こちらは通常、ドメイン名とアプリケーション名に従って命名する)

{
  "package": {
    "productName": "app",
    "version": "0.1.0"
  },
  "build": {
    "distDir": "../public",
    "devPath": "../public"
  },
  "tauri": {
    "bundle": {
      "active": true,
      "targets": "all",
      "identifier": "com.tauri.single-instance",
      .
      .

まとめ

  • シングルインスタンスを実現するため、Tauriでは、Tauri Plugin single-instanceを利用する。
  • Tauri::Builder::pluginに、tauri_plugin_single_instance::init関数で挙動を登録する。(最小化解除とかフォーカスとか)
  • すでに起動しているプロセスに通知が行き、新しく起動したプロセスは直ちに終了する。