デスクトップのショートカットは、アプリのコードで作るよりインストーラーに任せるのが確実です。Tauri が Windows 向けに作る NSIS と MSI のインストーラーは、設定を書かなくてもデスクトップとスタートメニューにショートカットを作り、アンインストール時に消します。インストーラーを通らないポータブル版(exe を zip で配る形)や、ユーザーが消したショートカットを設定画面から作り直したい場合は、Rust から OS 標準の仕組みを呼んで作ります。どちらも追加のクレートは要りません。
前提条件
インストーラーで作るだけなら、プラグインも権限も不要です。2 章の例では作る前に確認ダイアログを出すので、Dialog プラグインを追加します。確認に使う ask() と結果表示の message() は dialog:default で使えます。
npm run tauri add dialog
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"windows": ["main"],
"permissions": ["core:default", "dialog:default"]
}
1. インストーラーに作らせる (tauri.conf.json)
形式ごとの動きは次のとおりです。ショートカットの名前はどれも productName になります。
| 形式 | デスクトップ | スタートメニュー | アンインストール時 |
|---|---|---|---|
NSIS(-setup.exe) | 完了画面のチェックボックス(既定でオン) | 常に作る | どちらも消す |
| MSI | 常に作る(選ぶ画面は無い) | 常に作る | どちらも消す |
| deb / rpm | 作らない(アプリ一覧に登録) | ― | 一覧から消える |
| AppImage / .app / DMG | 作らない | ― | ― |
NSIS の languages に Japanese を入れておくと、日本語の Windows では完了画面のチェックボックスが「デスクトップショートカットを作成する」と表示されます。スタートメニューの中でフォルダーにまとめたいときは startMenuFolder を指定します。
{
"productName": "MyNotes",
"bundle": {
"active": true,
"targets": ["nsis", "msi"],
"windows": {
"nsis": {
"installMode": "currentUser",
"languages": ["Japanese", "English"],
"startMenuFolder": "My Company"
}
}
}
}
ネットで見かける createDesktopShortcut や createStartMenuShortcut は、Tauri v2 の nsis にはありません。書くと設定の検証でエラーになります。チェックボックスを既定でオフにする、項目自体を消すといった変更は template に独自の .nsi を指定するしかなく、Tauri の更新に合わせて保守する手間がかかります。
サイレント(/S)とパッシブ(/P)のインストールでは完了画面が出ないため、デスクトップのショートカットも自動で作られます。社内への一括配布などで作らせたくないときは /NS を付けると、スタートメニューとデスクトップのどちらも作られません。
.\MyNotes_1.0.0_x64-setup.exe /P /NS
2. バックエンドから実装する (Rust)
実行中に作るのは、ポータブル版や「ショートカットを作り直す」ボタンを用意する場合です。Windows のショートカット(.lnk)はテキストファイルではないので、OS に付属する PowerShell の標準機能で作ります。パスは文字列に埋め込まず環境変数で渡すと、空白や ' を含むパスでも壊れません。置き場所は desktop_dir() で取得します。ユーザーがデスクトップの場所を移していることがあるので、C:\Users\<名前>\Desktop と決め打ちしません。
use std::path::Path;
use tauri::Manager;
#[derive(serde::Serialize)]
struct ShortcutResult {
path: String,
created: bool, // false なら既にあったので何もしていない
}
/// デスクトップに productName のショートカットを作る(既にあれば触らない)
#[tauri::command]
fn create_desktop_shortcut(app: tauri::AppHandle) -> Result<ShortcutResult, String> {
let name = app.package_info().name.clone(); // productName
let desktop = app.path().desktop_dir().map_err(|e| e.to_string())?;
let exe = std::env::current_exe().map_err(|e| e.to_string())?;
let ext = if cfg!(windows) { "lnk" } else { "desktop" };
let path = desktop.join(format!("{name}.{ext}"));
if path.exists() {
return Ok(ShortcutResult { path: path.display().to_string(), created: false });
}
write_shortcut(&path, &exe, &name)?;
Ok(ShortcutResult { path: path.display().to_string(), created: true })
}
#[cfg(windows)]
fn write_shortcut(lnk: &Path, exe: &Path, _name: &str) -> Result<(), String> {
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x0800_0000; // PowerShell の黒い画面を出さない
let script = "$s = (New-Object -ComObject WScript.Shell).CreateShortcut($env:LNK_PATH); \
$s.TargetPath = $env:LNK_TARGET; \
$s.WorkingDirectory = $env:LNK_DIR; \
$s.Save()";
let status = std::process::Command::new("powershell")
.args(["-NoProfile", "-NonInteractive", "-Command", script])
.env("LNK_PATH", lnk)
.env("LNK_TARGET", exe)
.env("LNK_DIR", exe.parent().unwrap_or(exe))
.creation_flags(CREATE_NO_WINDOW)
.status()
.map_err(|e| format!("PowerShell を起動できません: {e}"))?;
if status.success() && lnk.exists() {
Ok(())
} else {
Err(format!("ショートカットを作れませんでした ({status})"))
}
}
#[cfg(target_os = "linux")]
fn write_shortcut(file: &Path, exe: &Path, name: &str) -> Result<(), String> {
use std::os::unix::fs::PermissionsExt;
// AppImage で動いているときは AppImage ファイル自体を起動させる
let target = std::env::var_os("APPIMAGE")
.map(std::path::PathBuf::from)
.unwrap_or_else(|| exe.to_path_buf());
let entry = format!(
"[Desktop Entry]\nType=Application\nName={name}\nExec=\"{}\"\nTerminal=false\n",
target.display()
);
std::fs::write(file, entry).map_err(|e| e.to_string())?;
std::fs::set_permissions(file, std::fs::Permissions::from_mode(0o755)).map_err(|e| e.to_string())
}
#[cfg(not(any(windows, target_os = "linux")))]
fn write_shortcut(_: &Path, _: &Path, _: &str) -> Result<(), String> {
Err("この OS ではデスクトップにショートカットを作りません".into())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.invoke_handler(tauri::generate_handler![create_desktop_shortcut])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
ショートカットの行き先は current_exe()、つまり今動いている実行ファイルです。ユーザーが消したショートカットを起動のたびに作り直すと嫌がられるので、設定画面のボタンから、確認を取ってから呼びます(確認ダイアログ)。
import { invoke } from '@tauri-apps/api/core';
import { ask, message } from '@tauri-apps/plugin-dialog';
type ShortcutResult = { path: string; created: boolean };
// 設定画面の「デスクトップにショートカットを作成」ボタンから呼ぶ
export async function offerDesktopShortcut() {
const yes = await ask('デスクトップにショートカットを作成しますか?', { title: 'ショートカット' });
if (!yes) return;
try {
const r = await invoke<ShortcutResult>('create_desktop_shortcut');
await message(r.created ? `作成しました: ${r.path}` : `既にあります: ${r.path}`);
} catch (e) {
await message(`作成できませんでした: ${e}`, { kind: 'error' });
}
}
動作確認
インストーラーは npm run tauri build -- --bundles nsis で作り、src-tauri/target/release/bundle/nsis/ の -setup.exe を実行します。完了画面のチェックを付けたまま閉じるとデスクトップに「MyNotes」ができ、「アプリと機能」からアンインストールすると消えます。
実行中に作る方は、npm run tauri dev で起動して offerDesktopShortcut() を呼び、「はい」を選ぶと次のように表示されます。もう一度呼ぶと「既にあります」になります。
作成しました: C:\Users\taro\Desktop\MyNotes.lnk
tauri dev 中に作ったショートカットは開発用の実行ファイル(src-tauri/target/debug の中)を指すので、確かめたら削除します。
よくあるエラーと対処法
tauri buildなどで「is not valid under any of the schemas listed in the 'anyOf' keyword」で終わるエラー: 場所がbundle > windows > nsisと示されていれば、nsisに無いキー(createDesktopShortcutなど)を書いています。消せば、ショートカットは既定の動きで作られます。- サイレントインストールでショートカットができない:
/NSが付いていないか確かめます。 - 「PowerShell を起動できません」「ショートカットを作れませんでした」: 会社の PC などで PowerShell の実行が制限されていると失敗します。インストーラーでの作成に切り替えるか、手で作る手順を案内します。
- ショートカットを開くと「項目が見つからない」趣旨の表示になる: ポータブル版の exe を移動したか、開発中に作ったものです。削除して作り直します。
- 作ったのにデスクトップに見当たらない: パスを自分で組み立てていないか確かめます。
desktop_dir()(JS はdesktopDir())の場所に作ります。
OS ごとの違いと注意点
- Windows: NSIS はユーザーがデスクトップのショートカットを断れますが、MSI では選べません。ユーザーに選ばせたいなら NSIS を使います(NSIS / MSI)。
- macOS: デスクトップにショートカットを置く習慣がなく、Tauri の .app / DMG も作りません。Dock への追加はユーザーに任せます。
- Linux: deb / rpm はアプリ一覧に登録するための
.desktopファイルを入れます。中身はdesktopTemplateで変えられます(Deb パッケージ)。AppImage はファイルを置くだけなので何も登録しません。デスクトップにアイコンを表示するか、置いた.desktopをそのまま起動できるかはデスクトップ環境の設定によります。 desktopDir()の場所: Linux はXDG_DESKTOP_DIR、macOS は$HOME/Desktop、Windows はユーザーのデスクトップフォルダーです。- ログイン時の起動も登録したい場合は OS 起動時にアプリを自動起動させる を参照してください。
