158 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { app, BrowserWindow } from 'electron'
/**
* 确保 Electron 应用只运行一个实例的工具函数
* @typedef {Object} SingleInstanceOptions
* @property {Function} [onSecondInstance] - 当第二个实例启动时的回调函数
* @property {boolean} [enableSandbox=false] - 是否启用沙箱模式
* @property {Function} [onSuccess] - 成功获取单例锁后的回调函数
* @property {Function} [onShowWindow] - 主窗口已展示回调
* @property {boolean} [forceFocus=true] - 是否强制聚焦已存在的窗口
* @property {boolean} [silentMode=false] - 静默模式,不显示任何提示
* @property {Function} [onError] - 错误处理回调函数
*/
/**
* 第二个实例启动时的回调函数类型
* @callback OnSecondInstanceCallback
* @param {Event} event - Electron 事件对象
* @param {string[]} commandLine - 命令行参数数组
* @param {string} workingDirectory - 工作目录
* @param {BrowserWindow|null} mainWindow - 主窗口实例,如果存在的话
*/
/**
* 确保应用程序只运行单个实例
* @param {SingleInstanceOptions} options - 配置选项
* @returns {boolean} 是否成功获取单例锁
*
* @example
* // 基础使用
* ensureSingleInstance({
* onSuccess: () => {
* app.whenReady().then(createWindow)
* }
* });
*
* @example
* // 高级使用
* ensureSingleInstance({
* onSecondInstance: (event, commandLine, workingDirectory, mainWindow) => {
* if (mainWindow) {
* mainWindow.webContents.send('new-instance-launched', commandLine);
* }
* },
* onSuccess: () => {
* console.log('Successfully acquired lock');
* createWindow();
* },
* onError: (error) => {
* console.error('Error in single instance check:', error);
* },
* forceFocus: true,
* silentMode: false
* });
*
* @throws {Error} 如果在非 Electron 环境中调用
*/
function ensureSingleInstance(options = {}) {
// 参数解构与默认值设置
const {
onSecondInstance,
enableSandbox = false,
onSuccess,
onShowWindow,
onError,
forceFocus = true,
silentMode = false,
} = options
// 验证运行环境
if (!app || !BrowserWindow) {
const error = new Error('ensureSingleInstance must be called in Electron environment')
if (onError) {
onError(error)
return false
}
throw error
}
try {
// 沙箱模式检查
if (enableSandbox) {
!silentMode && console.log('Sandbox mode enabled, skipping single instance check')
onSuccess?.()
return true
}
// 请求单例锁
const gotTheLock = app.requestSingleInstanceLock()
// 如果无法获取锁,说明已有实例在运行
if (!gotTheLock) {
!silentMode && console.log('Application instance already running, quitting...')
app.quit()
return false
}
// 监听第二个实例的启动
app.on('second-instance', (event, commandLine, workingDirectory) => {
try {
// 获取所有窗口
const windows = BrowserWindow.getAllWindows()
const mainWindow = windows.length ? windows[0] : null
// 处理窗口焦点
onShowWindow?.(mainWindow, commandLine)
if (mainWindow) {
if (mainWindow.isMinimized() || !mainWindow.isVisible()) {
mainWindow.show()
}
if (forceFocus) {
mainWindow.focus()
}
}
// 调用用户自定义的回调
onSecondInstance?.(event, commandLine, workingDirectory, mainWindow)
}
catch (error) {
!silentMode && console.error('Error handling second instance:', error)
onError?.(error)
}
})
// 调用成功回调
onSuccess?.()
return true
}
catch (error) {
!silentMode && console.error('Error in ensureSingleInstance:', error)
onError?.(error)
return false
}
}
/**
* 检查当前是否为应用程序的主实例
* @returns {boolean} 如果是主实例返回 true否则返回 false
*/
function isMainInstance() {
return app.requestSingleInstanceLock()
}
/**
* 释放单例锁,允许其他实例启动
* @returns {void}
*/
function releaseSingleInstanceLock() {
app.releaseSingleInstanceLock()
}
export {
ensureSingleInstance,
isMainInstance,
releaseSingleInstanceLock,
}