← 返回文章列表

NaiveProxy Windows 开机自启动,避免前台跑出现异常

最初手动双击 `naive.exe` 可以运行,但会打开一个窗口并持续输出日志。日志太多时,进程可能会自动退出。因此改为使用 Windows 内置的任务计划程序加守护脚本运行。

背景

本机有一个 NaiveProxy 程序,需要在 Windows 开机后自动后台运行。

程序目录:

text
C:\Users\halcyon\Documents\naiveproxy

目录内主要文件:

text
naive.exe
config.json

最初手动双击 naive.exe 可以运行,但会打开一个窗口并持续输出日志。日志太多时,进程可能会自动退出。因此改为使用 Windows 内置的任务计划程序加守护脚本运行。

最终方案

使用:

text
Windows Task Scheduler + PowerShell guardian script

优点:

  • 不需要安装第三方工具
  • 开机自动启动
  • 后台隐藏窗口运行
  • 进程退出后自动重启
  • 日志写入文件
  • 自动清理旧日志

守护脚本路径

脚本保存位置:

text
C:\Users\halcyon\Documents\naiveproxy\run-naiveproxy.ps1

脚本内容:

powershell
$ErrorActionPreference = "Stop"

$Root = "C:\Users\halcyon\Documents\naiveproxy"
$Exe = Join-Path $Root "naive.exe"
$LogDir = Join-Path $Root "logs"

New-Item -ItemType Directory -Force -Path $LogDir | Out-Null

function Remove-OldLogs {
    Get-ChildItem -LiteralPath $LogDir -Filter "*.log" -File -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending |
        Select-Object -Skip 20 |
        Remove-Item -Force -ErrorAction SilentlyContinue
}

while ($true) {
    Remove-OldLogs

    $stamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $stdout = Join-Path $LogDir "naiveproxy-$stamp.out.log"
    $stderr = Join-Path $LogDir "naiveproxy-$stamp.err.log"

    $p = Start-Process `
        -FilePath $Exe `
        -ArgumentList "config.json" `
        -WorkingDirectory $Root `
        -WindowStyle Hidden `
        -RedirectStandardOutput $stdout `
        -RedirectStandardError $stderr `
        -PassThru

    $p.WaitForExit()

    Add-Content -LiteralPath $stderr -Value ("[{0}] naiveproxy exited with code {1}; restarting in 5 seconds." -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $p.ExitCode)
    Start-Sleep -Seconds 5
}

创建守护脚本

在管理员 PowerShell 中执行:

powershell
@'
$ErrorActionPreference = "Stop"

$Root = "C:\Users\halcyon\Documents\naiveproxy"
$Exe = Join-Path $Root "naive.exe"
$LogDir = Join-Path $Root "logs"

New-Item -ItemType Directory -Force -Path $LogDir | Out-Null

function Remove-OldLogs {
    Get-ChildItem -LiteralPath $LogDir -Filter "*.log" -File -ErrorAction SilentlyContinue |
        Sort-Object LastWriteTime -Descending |
        Select-Object -Skip 20 |
        Remove-Item -Force -ErrorAction SilentlyContinue
}

while ($true) {
    Remove-OldLogs

    $stamp = Get-Date -Format "yyyyMMdd-HHmmss"
    $stdout = Join-Path $LogDir "naiveproxy-$stamp.out.log"
    $stderr = Join-Path $LogDir "naiveproxy-$stamp.err.log"

    $p = Start-Process `
        -FilePath $Exe `
        -ArgumentList "config.json" `
        -WorkingDirectory $Root `
        -WindowStyle Hidden `
        -RedirectStandardOutput $stdout `
        -RedirectStandardError $stderr `
        -PassThru

    $p.WaitForExit()

    Add-Content -LiteralPath $stderr -Value ("[{0}] naiveproxy exited with code {1}; restarting in 5 seconds." -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $p.ExitCode)
    Start-Sleep -Seconds 5
}
'@ | Set-Content -Encoding UTF8 "C:\Users\halcyon\Documents\naiveproxy\run-naiveproxy.ps1"

注册开机启动任务

在管理员 PowerShell 中执行:

powershell
$action = New-ScheduledTaskAction `
  -Execute "powershell.exe" `
  -Argument '-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Users\halcyon\Documents\naiveproxy\run-naiveproxy.ps1"'

$trigger = New-ScheduledTaskTrigger -AtStartup

$settings = New-ScheduledTaskSettingsSet `
  -AllowStartIfOnBatteries `
  -DontStopIfGoingOnBatteries `
  -ExecutionTimeLimit ([TimeSpan]::Zero) `
  -RestartCount 999 `
  -RestartInterval (New-TimeSpan -Minutes 1) `
  -MultipleInstances IgnoreNew

$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

Register-ScheduledTask `
  -TaskName "naiveproxy-guardian" `
  -Action $action `
  -Trigger $trigger `
  -Settings $settings `
  -Principal $principal

启动任务

powershell
Start-ScheduledTask -TaskName "naiveproxy-guardian"

查看任务状态

powershell
Get-ScheduledTask -TaskName "naiveproxy-guardian"

也可以查看更详细的运行信息:

powershell
Get-ScheduledTaskInfo -TaskName "naiveproxy-guardian"

停止任务

powershell
Stop-ScheduledTask -TaskName "naiveproxy-guardian"

注意:停止任务会停止守护脚本,但如果 naive.exe 已经被脚本启动,必要时可手动结束 naive.exe 进程。

删除任务

powershell
Unregister-ScheduledTask -TaskName "naiveproxy-guardian" -Confirm:$false

日志位置

日志目录:

text
C:\Users\halcyon\Documents\naiveproxy\logs

日志文件格式:

text
naiveproxy-YYYYMMDD-HHMMSS.out.log
naiveproxy-YYYYMMDD-HHMMSS.err.log

脚本会保留最近 20 个 .log 文件,旧日志会自动清理。

维护注意事项

修改 config.json 后,建议重启任务:

powershell
Stop-ScheduledTask -TaskName "naiveproxy-guardian"
Start-ScheduledTask -TaskName "naiveproxy-guardian"

如果修改了 run-naiveproxy.ps1,同样需要重启任务。

如果发现代理不可用,优先检查:

  1. config.json 是否正确
  2. C:\Users\halcyon\Documents\naiveproxy\logs 下最新的 .err.log
  3. 是否已经有其他手动启动的 naive.exe 占用了同一个端口
  4. 任务计划程序中的 naiveproxy-guardian 是否正在运行

当时遇到的问题

尝试过使用 NSSM 将程序包装为 Windows Service,但本机没有 nssm 命令:

text
nssm : 无法将“nssm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称

随后尝试通过 winget 安装 NSSM,但本机也没有 winget。由于 NSSM 无法方便下载,最终改用 Windows 内置任务计划程序方案。

当前结论

当前方案已经验证可用:

text
naiveproxy-guardian 任务计划程序项负责开机启动 run-naiveproxy.ps1
run-naiveproxy.ps1 负责后台启动 naive.exe config.json
naive.exe 退出后,脚本等待 5 秒并自动重启
日志写入 naiveproxy\logs