|
@@ -6,17 +6,22 @@ const fs = require('fs')
|
|
|
const unpackedRoot = app.isPackaged
|
|
const unpackedRoot = app.isPackaged
|
|
|
? path.join(path.dirname(process.execPath), 'resources', 'app.asar.unpacked')
|
|
? path.join(path.dirname(process.execPath), 'resources', 'app.asar.unpacked')
|
|
|
: path.join(__dirname, '..')
|
|
: path.join(__dirname, '..')
|
|
|
-const config = require(path.join(unpackedRoot, 'configs', 'config.js'))
|
|
|
|
|
-const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
|
|
|
|
|
-
|
|
|
|
|
-const staticDir = path.join(unpackedRoot, 'static')
|
|
|
|
|
|
|
+// static 不打包,放在打包根目录(与 exe 同级)作为沙盒目录
|
|
|
|
|
+const sandboxRoot = app.isPackaged ? path.dirname(process.execPath) : path.join(__dirname, '..')
|
|
|
|
|
+const staticDir = path.join(sandboxRoot, 'static')
|
|
|
if (!fs.existsSync(staticDir)) {
|
|
if (!fs.existsSync(staticDir)) {
|
|
|
fs.mkdirSync(staticDir, { recursive: true })
|
|
fs.mkdirSync(staticDir, { recursive: true })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 修复缓存权限:userData 与缓存目录设到临时目录,避免安装目录只读导致 "Unable to move the cache (0x5)"
|
|
|
|
|
|
|
+const config = require(path.join(unpackedRoot, 'configs', 'config.js'))
|
|
|
|
|
+const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
|
|
|
|
|
+
|
|
|
|
|
+// 打包后:userData 放在 exe 同目录 UserData,实现「开包即用」— 整包复制到任意电脑即可使用
|
|
|
|
|
+// 开发时:使用临时目录,避免污染项目
|
|
|
if (process.platform === 'win32') {
|
|
if (process.platform === 'win32') {
|
|
|
- const userDataPath = path.join(os.tmpdir(), 'AndroidRemoteController')
|
|
|
|
|
|
|
+ const userDataPath = app.isPackaged
|
|
|
|
|
+ ? path.join(sandboxRoot, 'UserData')
|
|
|
|
|
+ : path.join(os.tmpdir(), 'AndroidRemoteController')
|
|
|
if (!fs.existsSync(userDataPath)) {
|
|
if (!fs.existsSync(userDataPath)) {
|
|
|
fs.mkdirSync(userDataPath, { recursive: true })
|
|
fs.mkdirSync(userDataPath, { recursive: true })
|
|
|
}
|
|
}
|
|
@@ -34,7 +39,19 @@ if (process.platform === 'win32') {
|
|
|
// 保存主窗口引用,用于推送消息
|
|
// 保存主窗口引用,用于推送消息
|
|
|
let mainWindowInstance = null
|
|
let mainWindowInstance = null
|
|
|
|
|
|
|
|
|
|
+// 启动日志:写入 userData/startup.log,便于排查“无 UI”问题
|
|
|
|
|
+const startupLogPath = path.join(app.getPath('userData'), 'startup.log')
|
|
|
|
|
+function startupLog (msg) {
|
|
|
|
|
+ const line = `[${new Date().toISOString()}] ${msg}\n`
|
|
|
|
|
+ try {
|
|
|
|
|
+ fs.appendFileSync(startupLogPath, line)
|
|
|
|
|
+ } catch (e) {}
|
|
|
|
|
+ console.log(msg)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function createWindow() {
|
|
function createWindow() {
|
|
|
|
|
+ startupLog(`createWindow: isPackaged=${app.isPackaged} isDev=${isDev} unpackedRoot=${unpackedRoot}`)
|
|
|
|
|
+
|
|
|
const mainWindow = new BrowserWindow({
|
|
const mainWindow = new BrowserWindow({
|
|
|
width: config.window.width,
|
|
width: config.window.width,
|
|
|
height: config.window.height,
|
|
height: config.window.height,
|
|
@@ -42,7 +59,7 @@ function createWindow() {
|
|
|
webPreferences: {
|
|
webPreferences: {
|
|
|
nodeIntegration: false,
|
|
nodeIntegration: false,
|
|
|
contextIsolation: true,
|
|
contextIsolation: true,
|
|
|
- preload: path.join(__dirname, 'preload.js')
|
|
|
|
|
|
|
+ preload: path.join(__dirname, 'preload.js')
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -52,16 +69,33 @@ function createWindow() {
|
|
|
if (isDev) {
|
|
if (isDev) {
|
|
|
const vitePort = config.vite?.port || 5173
|
|
const vitePort = config.vite?.port || 5173
|
|
|
const viteHost = config.vite?.host || 'localhost'
|
|
const viteHost = config.vite?.host || 'localhost'
|
|
|
- console.log(`Loading Vite dev server at http://${viteHost}:${vitePort}`)
|
|
|
|
|
|
|
+ startupLog(`Loading Vite dev server at http://${viteHost}:${vitePort}`)
|
|
|
mainWindow.loadURL(`http://${viteHost}:${vitePort}`)
|
|
mainWindow.loadURL(`http://${viteHost}:${vitePort}`)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 根据配置文件决定是否打开调试侧边栏
|
|
// 根据配置文件决定是否打开调试侧边栏
|
|
|
if (config.devTools.enabled) {
|
|
if (config.devTools.enabled) {
|
|
|
mainWindow.webContents.openDevTools()
|
|
mainWindow.webContents.openDevTools()
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- mainWindow.loadFile(path.join(unpackedRoot, 'dist', 'index.html'))
|
|
|
|
|
|
|
+ // 前端 dist(含 assets)通过 extraFiles 拷贝到 exe 同级 dist/,从沙盒根加载
|
|
|
|
|
+ const indexPath = path.join(sandboxRoot, 'dist', 'index.html')
|
|
|
|
|
+ const indexExists = fs.existsSync(indexPath)
|
|
|
|
|
+ startupLog(`Packaged load: index.html path=${indexPath} exists=${indexExists}`)
|
|
|
|
|
+ if (!indexExists) {
|
|
|
|
|
+ startupLog('ERROR: dist/index.html 不存在,请先 npm run build 再打包,并确认 extraFiles 含 dist')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
|
|
|
|
+ startupLog(`did-fail-load: mainFrame=${isMainFrame} code=${errorCode} desc=${errorDescription} url=${validatedURL}`)
|
|
|
|
|
+ })
|
|
|
|
|
+ mainWindow.webContents.on('did-finish-load', () => {
|
|
|
|
|
+ startupLog(`did-finish-load: url=${mainWindow.webContents.getURL()}`)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ mainWindow.loadFile(indexPath)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ startupLog(`启动日志文件: ${startupLogPath}`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const { spawn } = require('child_process')
|
|
const { spawn } = require('child_process')
|
|
@@ -82,9 +116,12 @@ ipcMain.handle('run-nodejs-script', async (event, scriptName, ...parameters) =>
|
|
|
runningProcesses.delete(processKey)
|
|
runningProcesses.delete(processKey)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const nodeProcess = spawn('node', [scriptPath, ...parameters])
|
|
|
|
|
|
|
+ const nodeProcess = spawn('node', [scriptPath, ...parameters], {
|
|
|
|
|
+ env: { ...process.env, STATIC_ROOT: staticDir },
|
|
|
|
|
+ cwd: unpackedRoot
|
|
|
|
|
+ })
|
|
|
runningProcesses.set(processKey, nodeProcess)
|
|
runningProcesses.set(processKey, nodeProcess)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let stdout = ''
|
|
let stdout = ''
|
|
|
let stderr = ''
|
|
let stderr = ''
|
|
|
let resolved = false
|
|
let resolved = false
|
|
@@ -166,7 +203,7 @@ ipcMain.handle('kill-nodejs-script', async (event, scriptName, ...parameters) =>
|
|
|
|
|
|
|
|
/** 检查 scrcpy 是否仍在运行(读 pid 文件并用 process.kill(pid,0) 探测),用于用户直接关窗口时同步按钮状态 */
|
|
/** 检查 scrcpy 是否仍在运行(读 pid 文件并用 process.kill(pid,0) 探测),用于用户直接关窗口时同步按钮状态 */
|
|
|
ipcMain.handle('check-scrcpy-running', async () => {
|
|
ipcMain.handle('check-scrcpy-running', async () => {
|
|
|
- const pidFile = path.join(unpackedRoot, 'static', 'scrcpy-pid.json')
|
|
|
|
|
|
|
+ const pidFile = path.join(staticDir, 'scrcpy-pid.json')
|
|
|
if (!fs.existsSync(pidFile)) return { running: false }
|
|
if (!fs.existsSync(pidFile)) return { running: false }
|
|
|
let pid
|
|
let pid
|
|
|
try {
|
|
try {
|
|
@@ -255,13 +292,16 @@ ipcMain.handle('ipc-request', async (event, channel, data) => {
|
|
|
runningProcesses.delete(processKey)
|
|
runningProcesses.delete(processKey)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const nodeProcess = spawn('node', [scriptPath, ...Object.values(parameters)])
|
|
|
|
|
|
|
+ const nodeProcess = spawn('node', [scriptPath, ...Object.values(parameters)], {
|
|
|
|
|
+ env: { ...process.env, STATIC_ROOT: staticDir },
|
|
|
|
|
+ cwd: unpackedRoot
|
|
|
|
|
+ })
|
|
|
runningProcesses.set(processKey, nodeProcess)
|
|
runningProcesses.set(processKey, nodeProcess)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let stdout = ''
|
|
let stdout = ''
|
|
|
let stderr = ''
|
|
let stderr = ''
|
|
|
let resolved = false
|
|
let resolved = false
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
nodeProcess.stdout.on('data', (chunk) => {
|
|
nodeProcess.stdout.on('data', (chunk) => {
|
|
|
stdout += chunk.toString()
|
|
stdout += chunk.toString()
|
|
|
try {
|
|
try {
|
|
@@ -326,6 +366,7 @@ function pushToFrontend(channel, data) {
|
|
|
global.pushToFrontend = pushToFrontend
|
|
global.pushToFrontend = pushToFrontend
|
|
|
|
|
|
|
|
app.whenReady().then(() => {
|
|
app.whenReady().then(() => {
|
|
|
|
|
+ startupLog('========== 本次启动 ==========')
|
|
|
createWindow()
|
|
createWindow()
|
|
|
|
|
|
|
|
app.on('activate', () => {
|
|
app.on('activate', () => {
|
|
@@ -335,6 +376,21 @@ app.whenReady().then(() => {
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
|
|
+// 退出时结束 adb.exe,避免残留在后台
|
|
|
|
|
+function killAdbOnExit() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (process.platform === 'win32') {
|
|
|
|
|
+ require('child_process').execSync('taskkill /IM adb.exe /F', { stdio: 'ignore', windowsHide: true })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ require('child_process').execSync('pkill -x adb || true', { stdio: 'ignore' })
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+app.on('before-quit', () => {
|
|
|
|
|
+ killAdbOnExit()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
app.on('window-all-closed', () => {
|
|
app.on('window-all-closed', () => {
|
|
|
if (process.platform !== 'darwin') {
|
|
if (process.platform !== 'darwin') {
|
|
|
app.quit()
|
|
app.quit()
|