人人都会AI编程

14.5 右键菜单扩展、系统级文件关联

更新时间:2026-07-11

优秀的桌面应用应当能深度融入操作系统,让用户在“双击文件”或“右击文件”时就能直接调用你的软件。文件关联和右键菜单扩展是用户感知“原生感”的两个关键特性。Electron 结合 electron-builder 可以在不写任何 C++ 代码的前提下,完成这两件事。

14.5.1 文件关联:双击就打开我的应用

文件关联的本质是向操作系统注册“我能处理哪类文件”。用户双击 .txt 文件,系统启动记事本,这就是文件关联的作用。在 Electron 中,我们只需要在构建配置中声明关联关系,运行时监听文件打开事件即可。

1. 配置 electron-builder

package.jsonbuild 字段中加入 fileAssociations 数组:

{
  "build": {
    "fileAssociations": [
      {
        "ext": "md",
        "name": "MyMarkdownEditor",
        "description": "Markdown 文件",
        "role": "Editor",
        "icon": "assets/file-icon.icns"
      },
      {
        "ext": "txt",
        "name": "MyTextDocument",
        "role": "Editor"
      }
    ]
  }
}
  • ext:关联的扩展名,小写且不要带点,可以同时为同一个扩展名指定多个格式(例如 .jpeg.jpg)。
  • name(macOS 必备):文件类型的内部标识符。
  • description(Windows):在“文件类型”对话框中显示的描述。
  • role:推荐填写 EditorViewer,影响系统的行为偏好。
  • icon:为关联文件指定图标,macOS 用 .icns,Windows 用 .ico,放在 build/ 目录下。

真实经验:macOS 的文件关联由 Info.plist 控制,electron-builder 会自动生成。Windows 方面,安装包(NSIS 或 MSI)会自动向注册表写入 HKEY_CLASSES_ROOT\.md 和对应程序路径。卸载时会一并清理这些注册项,无需手动处理。

2. 在应用内接收文件路径

当用户双击已关联的文件时,操作系统会启动你的应用并传入文件路径。获取文件路径有两种典型场景:

  • 冷启动(应用尚未运行):通过 process.argv 在启动时解析。
  • 热启动(应用已在运行):macOS 会触发 open-file 事件,Windows 则会触发 second-instance 事件。因此推荐统一监听如下两个事件:
// main.js
const { app, BrowserWindow } = require('electron');

let mainWindow = null;

// 处理 macOS 的“打开文件”事件
app.on('open-file', (event, filePath) => {
  event.preventDefault();                     // 阻止默认行为
  if (mainWindow) {
    mainWindow.webContents.send('file-open', filePath);
  } else {
    // 窗口还未创建,暂存路径,待窗口就绪后再处理
    app.pendingFilePath = filePath;
  }
});

app.whenReady().then(() => {
  mainWindow = new BrowserWindow({ /* ... */ });
  
  // 处理冷启动参数(包括双击文件启动)
  const filePath = process.argv.find(arg => arg.endsWith('.md'));
  if (filePath) {
    mainWindow.webContents.send('file-open', filePath);
  } else if (app.pendingFilePath) {
    mainWindow.webContents.send('file-open', app.pendingFilePath);
  }
  delete app.pendingFilePath;
});

确保单实例:为了避免多个窗口同时打开不同文件,应限制应用只运行一个实例:

const gotTheLock = app.requestSingleInstanceLock();

if (!gotTheLock) {
  app.quit();
} else {
  app.on('second-instance', (event, argv) => {
    // Windows / Linux 下,当用户双击另一个文件时,会走到这里
    const filePath = argv.find(arg => arg.endsWith('.md'));
    if (mainWindow) {
      // 窗口可能已最小化,先还原显示
      if (mainWindow.isMinimized()) mainWindow.restore();
      mainWindow.focus();
      mainWindow.webContents.send('file-open', filePath);
    }
  });
}

这样,无论应用是否正在运行,都能可靠地接住用户双击的文件。

14.5.2 右键菜单:在资源管理器中集成应用入口

Windows 资源管理器右键菜单(Shell 扩展)可以让用户直接对文件执行“用我的应用打开”“转换为 PDF”等操作。Electron 自身不提供注册右键菜单的 API,但可以通过安装包脚本向系统注册表写入相应条目。

1. 通过 NSIS 脚本注册右键菜单

electron-builder 默认使用 NSIS 制作 Windows 安装程序,我们可以在 build 配置中注入自定义的 NSIS 脚本:

{
  "build": {
    "nsis": {
      "include": "build/installer.nsh"
    }
  }
}

新建 build/installer.nsh,内容如下:

!macro customInstall
  ; 注册右键菜单:对所有 .md 文件添加“用 MyApp 打开”选项
  WriteRegStr HKCR ".md" "" "MyApp.md"
  WriteRegStr HKCR "MyApp.md" "" "Markdown 文档"
  WriteRegStr HKCR "MyApp.md\shell\open" "" "用 MyApp 打开"
  WriteRegStr HKCR "MyApp.md\shell\open\command" "" '"$INSTDIR\MyApp.exe" "%1"'

  ; 可选:在“发送到”菜单也添加一项
  CreateDirectory "$SMPROGRAMS\SendTo"
  CreateShortCut "$SMPROGRAMS\SendTo\MyApp.lnk" "$INSTDIR\MyApp.exe"
!macroend

!macro customUninstall
  ; 卸载时清理注册表
  DeleteRegKey HKCR ".md"
  DeleteRegKey HKCR "MyApp.md"
  ; 删除发送到快捷方式
  Delete "$SMPROGRAMS\SendTo\MyApp.lnk"
!macroend
  • WriteRegStr 将关联信息写入 HKEY_CLASSES_ROOT,这是 Windows 文件关联和右键菜单的标准位置。
  • "$INSTDIR\MyApp.exe" "%1" 中的 %1 会被实际文件路径替换,等同于调用 MyApp.exe "C:\Users\...\note.md"
  • 卸载宏 customUninstall 确保用户删除应用后系统恢复原状。

运行效果:用户在 .md 文件上右键,菜单中会出现“用 MyApp 打开”,点击即启动应用并打开所选文件。

无需管理员权限:上述操作只需标准用户权限即可写入 HKEY_CURRENT_USER\Software\Classes(如果你担心权限问题,可以把 HKCR 改为 HKCU\Software\Classes,效果类似但仅限于当前用户)。

2. 使用更灵活的 Electron API(运行时注册)

如果不想动安装脚本,也可以在主进程启动时用 child_process 调用 reg 命令注册,但需要注意路径的可变性和权限问题,一般不推荐长住逻辑,更多用于便携版软件:

const { exec } = require('child_process');

function registerFileContextMenu() {
  const appPath = process.execPath; // 当前运行的可执行文件
  const commands = [
    `reg add "HKCU\\Software\\Classes\\*\\shell\\MyApp" /ve /d "用 MyApp 打开" /f`,
    `reg add "HKCU\\Software\\Classes\\*\\shell\\MyApp\\command" /ve /d "\\"${appPath}\\" \\"%1\\"" /f`
  ];
  commands.forEach(cmd => {
    exec(cmd, (err) => {
      if (err) console.error('右键菜单注册失败:', err);
    });
  });
}

注意:这种方法不会在卸载时自动清理,用户需要手动在注册表中删除,因此更建议随安装包处理。

14.5.3 跨平台差异与最佳实践

  • macOS:没有传统意义上的右键菜单扩展,但可以通过创建系统服务(Service)实现类似效果。不过这对 Electron 应用而言开发成本较高,通常不推荐。文件关联已经是 macOS 上最主流的集成方式。
  • Linux:各桌面环境差异较大,文件关联一般通过 .desktop 文件中的 MimeType 字段完成,electron-builder 会自动生成,无需额外配置右键菜单。如需特定右键动作,需编写 Nautilus 或 Thunar 扩展,这类扩展通常用原生代码实现,Electron 层面很少介入。
  • 调试注意:开发环境下,Windows 的文件关联可能指向了 electron.exe 而非最终的打包程序。建议用好打包后的安装版测试右键菜单功能,避免路径错误导致“找不到应用程序”。

最后,无论是右键菜单还是文件关联,都遵循同一个原则:安装时建立约定,运行时接收参数。你只需在主进程中稳妥地监听文件打开事件,就能为用户提供“点击即开”的流畅体验,让 Electron 应用真正融入操作系统的日常使用。