重做设置界面并修复登录自启:页签单选、滚轮归属、任务缺失判定、发布去掉自带运行时

设置窗口
- 改为独立非模态窗口(进任务栏、可最小化),不再被托盘浮窗挡住导致点不到
- 位置改为按屏幕可用区域居中裁剪,不再默认落在右下角
- 三个页签重构为卡片式布局:统一卡片内边距、页签行独立成行、正文左右留白、
  帮助文字按实际换行测量高度、标题栏与底栏加分隔线
- 直连 DNS 复选框、下拉框、滚动条改为自绘控件,配色与整体统一

页签
- PanelChip 的选中/强调改为值变化即重绘,修掉“切换页签后旧页签仍显示为激活、
  鼠标移上去才更新”的陈旧绘制

滚轮
- 输入框滚不动自己时把滚轮交给外层页面,单行输入框不再吞掉滚轮事件

登录自启
- 改用任务 XML + schtasks 写入,COM 失败时用 schtasks 复核;写入后读回校验
- 任务不存在按 HRESULT 判定(0x80070002 / 0x80070003 / 0x8004130F),修掉
  “关闭登录自启必定报错并回滚”(晚绑定 COM 抛的是 FileNotFoundException)
- 状态读取 COM 失败时回退到 schtasks,失败按异常类型 + HResult 去重记录

诊断
- 界面报错统一写日志;控制器记录自启读写结果;启动自检记录管理员/自启/进程/参数

发布
- 去掉 -SelfContained 分支,只保留单文件运行时依赖包,脚本与文档同步
- 新增 tools/unblock-publish.ps1,清除发布产物的网络来源标记

测试
- 新增登录自启任务 XML 用例与“任务缺失按 HRESULT 判定”回归测试(46 → 47),探测 16/16
This commit is contained in:
chuzhongzai
2026-10-24 01:32:03 +08:00
parent 2d85527656
commit 7be117057a
23 changed files with 1549 additions and 217 deletions
+22
View File
@@ -0,0 +1,22 @@
# 去掉发布目录里 exe 的“来自网络”标记,减少首次运行时系统弹出的“未知发布者”安全警告。
# 用法: .\tools\unblock-publish.ps1 [-Path artifacts\publish]
param(
[string]$Path = (Join-Path (Split-Path -Parent $PSScriptRoot) 'artifacts\publish')
)
$ErrorActionPreference = 'Stop'
if (-not (Test-Path -LiteralPath $Path)) {
Write-Host "找不到目录:$Path(先运行 .\build.ps1 -Publish)"
exit 1
}
$cleared = 0
Get-ChildItem -LiteralPath $Path -Recurse -File -Include *.exe, *.dll | ForEach-Object {
$zone = Get-Item -LiteralPath $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue
if ($null -eq $zone) { return }
Remove-Item -LiteralPath $_.FullName -Stream Zone.Identifier -ErrorAction SilentlyContinue
Write-Host "已解除标记:$($_.FullName)"
$cleared++
}
if ($cleared -eq 0) { Write-Host '发布目录里没有需要解除的网络标记。' }
Write-Host ''
Write-Host '说明:本程序未做代码签名,因此 Windows 仍可能在首次运行时提示“未知发布者”并请求管理员权限。'
Write-Host '这两次提示都来自系统(安全警告 + UAC 提权),程序本身不联网、不安装服务、不写注册表自启项。'
+7 -9
View File
@@ -1,21 +1,19 @@
param([switch]$SelfContained)
$ErrorActionPreference = 'Stop'
$root = Split-Path -Parent $PSScriptRoot
$dotnet = Join-Path $env:USERPROFILE '.dotnet-sdk\dotnet.exe'
$flavor = if ($SelfContained) { 'standalone' } else { 'runtime-dependent' }
$out = Join-Path $root "artifacts\publish\$flavor"
$out = Join-Path $root 'artifacts\publish'
$env:DOTNET_ROOT = Split-Path -Parent $dotnet
& $dotnet publish (Join-Path $root 'src\GatewayKeepalive.Tray\GatewayKeepalive.Tray.csproj') -c Release -r win-x64 --self-contained $SelfContained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=embedded -o $out | Select-Object -Last 2
& $dotnet publish (Join-Path $root 'src\GatewayKeepalive.Tray\GatewayKeepalive.Tray.csproj') -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true -p:DebugType=embedded -o $out | Select-Object -Last 2
if ($LASTEXITCODE -ne 0) { throw 'publish failed' }
& $dotnet publish (Join-Path $root 'src\GatewayKeepalive.Cli\GatewayKeepalive.Cli.csproj') -c Release -r win-x64 --self-contained $SelfContained -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=embedded -o (Join-Path $out 'diagnostics') | Select-Object -Last 2
& $dotnet publish (Join-Path $root 'src\GatewayKeepalive.Cli\GatewayKeepalive.Cli.csproj') -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true -p:DebugType=embedded -o (Join-Path $out 'diagnostics') | Select-Object -Last 2
if ($LASTEXITCODE -ne 0) { throw 'cli publish failed' }
$preview = Join-Path $root "artifacts\preview-$flavor"
$preview = Join-Path $root 'artifacts\preview'
Remove-Item -LiteralPath $preview -Recurse -Force -ErrorAction SilentlyContinue
$stderr = Join-Path $root "artifacts\$flavor-stderr.txt"
$stderr = Join-Path $root 'artifacts\gui-stderr.txt'
# Arguments containing spaces must be quoted explicitly; Start-Process does not escape them.
$process = Start-Process -FilePath (Join-Path $out 'GatewayKeepalive.exe') -ArgumentList @('--preview', ('"' + $preview + '"')) -WindowStyle Hidden -PassThru -Wait -RedirectStandardError $stderr
$pngs = @(Get-ChildItem -LiteralPath $preview -Filter '*.png' -ErrorAction SilentlyContinue).Count
$report = Join-Path $root "artifacts\$flavor-diagnostics.json"
$report = Join-Path $root 'artifacts\diagnostics.json'
& (Join-Path $out 'diagnostics\GatewayKeepalive.Cli.exe') --diagnose --output $report | Out-Null
$cliExit = $LASTEXITCODE
$diagnostics = Get-Content -LiteralPath $report -Raw | ConvertFrom-Json
@@ -24,4 +22,4 @@ if (Test-Path -LiteralPath $stderr) {
$rawError = Get-Content -LiteralPath $stderr -Raw -ErrorAction SilentlyContinue
if ($null -ne $rawError) { $guiError = $rawError.Trim() }
}
[pscustomobject]@{ Flavor = $flavor; GuiExit = $process.ExitCode; PreviewPngs = $pngs; GuiStderr = $guiError; CliExit = $cliExit; ReadOnly = $diagnostics.ReadOnly; Gateway = $diagnostics.BestRoute.Gateway; Interface = $diagnostics.BestRoute.InterfaceIndex; Files = @(Get-ChildItem -LiteralPath $out -File | Select-Object Name, Length) } | ConvertTo-Json -Depth 4
[pscustomobject]@{ GuiExit = $process.ExitCode; PreviewPngs = $pngs; GuiStderr = $guiError; CliExit = $cliExit; ReadOnly = $diagnostics.ReadOnly; Gateway = $diagnostics.BestRoute.Gateway; Interface = $diagnostics.BestRoute.InterfaceIndex; Files = @(Get-ChildItem -LiteralPath $out -File | Select-Object Name, Length) } | ConvertTo-Json -Depth 4