Update Windows installer to use FarmControl.exe and enhance process handling
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Replaced references to launcher.exe with FarmControl.exe in the installer script, ensuring consistency in application execution. - Added taskkill command to stop running FarmControl processes during installation. - Updated shortcut creation to point to FarmControl.exe for both desktop and start menu. - Enhanced cleanup process to remove old executable backups and ensure proper application state after updates. - Refactored updater logic to check for the presence of FarmControl.exe, improving application launch reliability.
This commit is contained in:
parent
893804a17c
commit
eb1ee1e82b
@ -100,6 +100,7 @@ Var RestartAfterInstall
|
||||
!insertmacro progressPhase "Stopping Farm Control"
|
||||
!insertmacro progressStatus "Stopping running Farm Control processes..."
|
||||
DetailPrint "Stopping running Farm Control processes..."
|
||||
ExecWait 'taskkill /F /IM FarmControl.exe /T' $R0
|
||||
ExecWait 'taskkill /F /IM launcher.exe /T' $R0
|
||||
!macroend
|
||||
|
||||
@ -126,6 +127,7 @@ Var RestartAfterInstall
|
||||
ClearErrors
|
||||
Rename "$INSTDIR\bin" "$INSTDIR\bin.old"
|
||||
${If} ${Errors}
|
||||
!insertmacro moveAsideIfPresent "$INSTDIR\bin\FarmControl.exe"
|
||||
!insertmacro moveAsideIfPresent "$INSTDIR\bin\launcher.exe"
|
||||
!insertmacro moveAsideIfPresent "$INSTDIR\bin\bun.exe"
|
||||
!insertmacro moveAsideIfPresent "$INSTDIR\bin\bspatch.exe"
|
||||
@ -179,14 +181,14 @@ Var RestartAfterInstall
|
||||
!macro createDesktopShortcut
|
||||
SetShellVarContext current
|
||||
SetOutPath "$INSTDIR\bin"
|
||||
CreateShortCut "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
||||
CreateShortCut "$DESKTOP\Farm Control.lnk" "$INSTDIR\bin\FarmControl.exe" "" "$INSTDIR\bin\FarmControl.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
||||
!macroend
|
||||
|
||||
!macro createStartMenuShortcut
|
||||
SetShellVarContext current
|
||||
CreateDirectory "$SMPROGRAMS\Farm Control"
|
||||
SetOutPath "$INSTDIR\bin"
|
||||
CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin\launcher.exe" "" "$INSTDIR\bin\launcher.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
||||
CreateShortCut "$SMPROGRAMS\Farm Control\Farm Control.lnk" "$INSTDIR\bin\FarmControl.exe" "" "$INSTDIR\bin\FarmControl.exe" 0 SW_SHOWNORMAL "" "Farm Control"
|
||||
!macroend
|
||||
|
||||
!macro removeDesktopShortcut
|
||||
@ -203,6 +205,7 @@ Var RestartAfterInstall
|
||||
!macro cleanupUpdateBackups
|
||||
; Best-effort; may fail while the previous process still holds handles.
|
||||
RMDir /r "$INSTDIR\bin.old"
|
||||
Delete "$INSTDIR\bin\FarmControl.exe.old"
|
||||
Delete "$INSTDIR\bin\launcher.exe.old"
|
||||
Delete "$INSTDIR\bin\bun.exe.old"
|
||||
Delete "$INSTDIR\bin\bspatch.exe.old"
|
||||
@ -216,7 +219,7 @@ Var RestartAfterInstall
|
||||
DeleteRegKey HKCU "Software\Classes\farmcontrol"
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol" "" "URL:farmcontrol"
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol" "URL Protocol" ""
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol\DefaultIcon" "" "$INSTDIR\bin\launcher.exe"
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol\DefaultIcon" "" "$INSTDIR\bin\FarmControl.exe"
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol\shell" "" ""
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open" "" ""
|
||||
WriteRegStr HKCU "Software\Classes\farmcontrol\shell\Open\command" "" '"$INSTDIR\bin\bun.exe" "$INSTDIR\bin\deeplink.js" "%1"'
|
||||
@ -248,6 +251,11 @@ Function restartFarmControlAfterUpdate
|
||||
Sleep 1000
|
||||
|
||||
restart_wait_loop:
|
||||
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq FarmControl.exe" 2>nul | find /I "FarmControl.exe"' $R0
|
||||
${If} $R0 == 0
|
||||
Sleep 1000
|
||||
Goto restart_wait_loop
|
||||
${EndIf}
|
||||
ExecWait 'cmd.exe /c tasklist /FI "IMAGENAME eq launcher.exe" 2>nul | find /I "launcher.exe"' $R0
|
||||
${If} $R0 == 0
|
||||
Sleep 1000
|
||||
@ -256,5 +264,5 @@ Function restartFarmControlAfterUpdate
|
||||
|
||||
!insertmacro progressStatus "Starting Farm Control..."
|
||||
SetOutPath "$INSTDIR\bin"
|
||||
Exec "$INSTDIR\bin\launcher.exe"
|
||||
Exec "$INSTDIR\bin\FarmControl.exe"
|
||||
FunctionEnd
|
||||
|
||||
@ -69,10 +69,10 @@ if ($stagedAppBytes -lt 5242880) {
|
||||
throw "Staged application is only $([math]::Round($stagedAppBytes / 1KB, 1)) KiB. Expected a full Electrobun app bundle before building the installer."
|
||||
}
|
||||
|
||||
$requiredExe = Join-Path $stagingAppDir "bin\launcher.exe"
|
||||
$requiredExe = Join-Path $stagingAppDir "bin\FarmControl.exe"
|
||||
$requiredDeeplinkScript = Join-Path $stagingAppDir "bin\deeplink.js"
|
||||
if (-not (Test-Path $requiredExe)) {
|
||||
throw "Staged application is missing bin\launcher.exe"
|
||||
throw "Staged application is missing bin\FarmControl.exe"
|
||||
}
|
||||
if (-not (Test-Path $requiredDeeplinkScript)) {
|
||||
throw "Staged application is missing bin\deeplink.js"
|
||||
|
||||
@ -619,7 +619,7 @@ async function main() {
|
||||
platformDir
|
||||
)
|
||||
stageWindowsDeeplinkScript(appDir)
|
||||
await patchWindowsBinaries(appDir)
|
||||
patchWindowsBinaries(appDir)
|
||||
|
||||
let published
|
||||
try {
|
||||
|
||||
@ -1,79 +1,37 @@
|
||||
import { copyFileSync, existsSync, unlinkSync } from 'node:fs'
|
||||
import { existsSync, renameSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import rcedit from 'rcedit'
|
||||
|
||||
const PATCH_TARGETS = new Set(['launcher.exe'])
|
||||
const MAX_ATTEMPTS = 6
|
||||
const BASE_DELAY_MS = 500
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async function patchExecutable(executablePath) {
|
||||
let lastError
|
||||
|
||||
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
||||
const tempPath = `${executablePath}.rcedit-${process.pid}-${attempt}.tmp`
|
||||
|
||||
try {
|
||||
// Patch a copy then replace — avoids AV/indexer locks on the live PE.
|
||||
copyFileSync(executablePath, tempPath)
|
||||
await rcedit(tempPath, {
|
||||
'requested-execution-level': 'asInvoker'
|
||||
})
|
||||
copyFileSync(tempPath, executablePath)
|
||||
return
|
||||
} catch (error) {
|
||||
lastError = error
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
console.warn(
|
||||
`patch-windows-binaries: attempt ${attempt}/${MAX_ATTEMPTS} failed for ${executablePath}: ${message}`
|
||||
)
|
||||
|
||||
if (attempt < MAX_ATTEMPTS) {
|
||||
await sleep(BASE_DELAY_MS * 2 ** (attempt - 1))
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
unlinkSync(tempPath)
|
||||
} catch {
|
||||
// Ignore cleanup failures.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError
|
||||
}
|
||||
|
||||
export async function patchWindowsBinaries(appDir) {
|
||||
if (process.platform !== 'win32') {
|
||||
return
|
||||
}
|
||||
export const WINDOWS_LAUNCHER_EXE = 'FarmControl.exe'
|
||||
const SOURCE_LAUNCHER_EXE = 'launcher.exe'
|
||||
|
||||
export function patchWindowsBinaries(appDir) {
|
||||
const binDir = path.join(appDir, 'bin')
|
||||
if (!existsSync(binDir)) {
|
||||
throw new Error(`patch-windows-binaries: bin directory not found at ${binDir}`)
|
||||
}
|
||||
|
||||
for (const executable of PATCH_TARGETS) {
|
||||
const executablePath = path.join(binDir, executable)
|
||||
if (!existsSync(executablePath)) {
|
||||
console.warn(`patch-windows-binaries: skipping missing ${executablePath}`)
|
||||
continue
|
||||
const sourcePath = path.join(binDir, SOURCE_LAUNCHER_EXE)
|
||||
const destPath = path.join(binDir, WINDOWS_LAUNCHER_EXE)
|
||||
|
||||
if (existsSync(destPath) && !existsSync(sourcePath)) {
|
||||
console.log(`patch-windows-binaries: ${WINDOWS_LAUNCHER_EXE} already present`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await patchExecutable(executablePath)
|
||||
console.log(`patch-windows-binaries: set asInvoker on ${executablePath}`)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`patch-windows-binaries: failed to patch ${executablePath}:`,
|
||||
error instanceof Error ? error.message : error
|
||||
if (!existsSync(sourcePath)) {
|
||||
console.warn(`patch-windows-binaries: skipping missing ${sourcePath}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (existsSync(destPath)) {
|
||||
renameSync(destPath, `${destPath}.old`)
|
||||
}
|
||||
|
||||
renameSync(sourcePath, destPath)
|
||||
console.log(
|
||||
`patch-windows-binaries: renamed ${SOURCE_LAUNCHER_EXE} -> ${WINDOWS_LAUNCHER_EXE}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
@ -83,5 +41,5 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await patchWindowsBinaries(path.resolve(appDirArg))
|
||||
patchWindowsBinaries(path.resolve(appDirArg))
|
||||
}
|
||||
|
||||
@ -29,9 +29,13 @@ console.log('[deeplink] no running instance found, launching Farm Control')
|
||||
|
||||
writeDeeplinkSignal(payload)
|
||||
|
||||
const launcherPath = join(dirname(process.execPath), 'launcher.exe')
|
||||
if (!existsSync(launcherPath)) {
|
||||
console.error('[deeplink] launcher.exe not found at', launcherPath)
|
||||
const binDir = dirname(process.execPath)
|
||||
const launcherPath = ['FarmControl.exe', 'launcher.exe']
|
||||
.map((name) => join(binDir, name))
|
||||
.find((candidate) => existsSync(candidate))
|
||||
|
||||
if (!launcherPath) {
|
||||
console.error('[deeplink] FarmControl.exe not found in', binDir)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
|
||||
@ -9,14 +9,18 @@ const WINDOWS_INSTALL_DIR_KEYS = [
|
||||
'HKCU\\Software\\Tom Butcher\\Farm Control',
|
||||
'HKLM\\Software\\Tom Butcher\\Farm Control'
|
||||
]
|
||||
const WINDOWS_DEFAULT_LAUNCHER = path.join(
|
||||
const WINDOWS_LAUNCHER_EXE = 'FarmControl.exe'
|
||||
const WINDOWS_LEGACY_LAUNCHER_EXE = 'launcher.exe'
|
||||
const WINDOWS_DEFAULT_BIN_DIR = path.join(
|
||||
process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'),
|
||||
'Programs',
|
||||
'Farm Control',
|
||||
'bin',
|
||||
'launcher.exe'
|
||||
'bin'
|
||||
)
|
||||
|
||||
const resolveExistingLauncher = (...candidates) =>
|
||||
candidates.find((candidate) => existsSync(candidate)) || null
|
||||
|
||||
const quoteBatchArg = (value) => `"${String(value).replaceAll('"', '""')}"`
|
||||
|
||||
const findMacAppBundle = (startPath) => {
|
||||
@ -81,32 +85,44 @@ export const resolveAppLaunchPath = () => {
|
||||
// (in-app updates may leave the running process under bin.old).
|
||||
const installDir = readWindowsInstallDirFromRegistry()
|
||||
if (installDir) {
|
||||
const launcherFromRegistry = path.join(installDir, 'bin', 'launcher.exe')
|
||||
if (existsSync(launcherFromRegistry)) {
|
||||
const launcherFromRegistry = resolveExistingLauncher(
|
||||
path.join(installDir, 'bin', WINDOWS_LAUNCHER_EXE),
|
||||
path.join(installDir, 'bin', WINDOWS_LEGACY_LAUNCHER_EXE)
|
||||
)
|
||||
if (launcherFromRegistry) {
|
||||
return launcherFromRegistry
|
||||
}
|
||||
}
|
||||
|
||||
if (existsSync(WINDOWS_DEFAULT_LAUNCHER)) {
|
||||
return WINDOWS_DEFAULT_LAUNCHER
|
||||
const defaultLauncher = resolveExistingLauncher(
|
||||
path.join(WINDOWS_DEFAULT_BIN_DIR, WINDOWS_LAUNCHER_EXE),
|
||||
path.join(WINDOWS_DEFAULT_BIN_DIR, WINDOWS_LEGACY_LAUNCHER_EXE)
|
||||
)
|
||||
if (defaultLauncher) {
|
||||
return defaultLauncher
|
||||
}
|
||||
|
||||
const execBase = path.basename(process.execPath).toLowerCase()
|
||||
if (execBase === 'launcher.exe') {
|
||||
if (
|
||||
execBase === WINDOWS_LAUNCHER_EXE.toLowerCase() ||
|
||||
execBase === WINDOWS_LEGACY_LAUNCHER_EXE
|
||||
) {
|
||||
return process.execPath
|
||||
}
|
||||
|
||||
const launcherBesideExec = path.join(path.dirname(process.execPath), 'launcher.exe')
|
||||
if (existsSync(launcherBesideExec)) {
|
||||
const launcherBesideExec = resolveExistingLauncher(
|
||||
path.join(path.dirname(process.execPath), WINDOWS_LAUNCHER_EXE),
|
||||
path.join(path.dirname(process.execPath), WINDOWS_LEGACY_LAUNCHER_EXE)
|
||||
)
|
||||
if (launcherBesideExec) {
|
||||
return launcherBesideExec
|
||||
}
|
||||
|
||||
const launcherInBin = path.join(
|
||||
path.dirname(process.execPath),
|
||||
'bin',
|
||||
'launcher.exe'
|
||||
const launcherInBin = resolveExistingLauncher(
|
||||
path.join(path.dirname(process.execPath), 'bin', WINDOWS_LAUNCHER_EXE),
|
||||
path.join(path.dirname(process.execPath), 'bin', WINDOWS_LEGACY_LAUNCHER_EXE)
|
||||
)
|
||||
if (existsSync(launcherInBin)) {
|
||||
if (launcherInBin) {
|
||||
return launcherInBin
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ import { existsSync } from 'node:fs'
|
||||
import { dirname, join } from 'node:path'
|
||||
|
||||
const NATIVE_WRAPPER_DLL = 'libNativeWrapper.dll'
|
||||
export const WINDOWS_LAUNCHER_EXE = 'FarmControl.exe'
|
||||
const LEGACY_LAUNCHER_EXE = 'launcher.exe'
|
||||
|
||||
export function resolveWindowsBinDir() {
|
||||
const candidates = [
|
||||
@ -13,7 +15,8 @@ export function resolveWindowsBinDir() {
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (
|
||||
existsSync(join(candidate, 'launcher.exe')) ||
|
||||
existsSync(join(candidate, WINDOWS_LAUNCHER_EXE)) ||
|
||||
existsSync(join(candidate, LEGACY_LAUNCHER_EXE)) ||
|
||||
existsSync(join(candidate, NATIVE_WRAPPER_DLL))
|
||||
) {
|
||||
return candidate
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user