mirror of
https://github.com/hansjone/netx.git
synced 2026-10-14 21:10:46 +08:00
Modernize the Windows installer, tray and runtime control; fix lifecycle bugs.
Installer: dynamic light/dark Win11 style, new brand wizard images, auto language, exclusive run mode (tray / Windows service) plus autostart and auto-update on the Tasks page (pre-selected from what is installed on upgrades, untouched by silent updates), 5 localized Start Menu entries. Tray: theme-aware rounded menu with live status, icons and status-dot tray icon; health polled off the UI thread; Settings submenu toggles autostart and daily updates; browser opened de-elevated via explorer. Fixes: - tray Stop/Start was ignored in service mode (service_run restarted the children) - all start/stop now goes through runtime_ctl.ps1 - logon tray task and SYSTEM service task were killed after 72h (default ExecutionTimeLimit); autostart was registered for the elevating account and opened a browser on every logon - uninstall left the NetXTray/NetXUpdate tasks behind - silent auto-update killed the tray without restarting it - repair_venv.ps1 was never staged into the release - wildcard NETX_HOST (0.0.0.0) broke health checks - ICO frames were PNG, which System.Drawing decodes as noise; icons now use the current brand mark Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4c4c7d91d0
commit
e21719b66c
27 changed files with 1209 additions and 687 deletions
|
|
@ -14,11 +14,16 @@
|
|||
; Stage directory produced by build_release.ps1 (relative to this .iss)
|
||||
#define StageDir "..\release\netx-win64"
|
||||
#define IconFile "..\assets\netx.ico"
|
||||
#define AssetsDir "..\assets"
|
||||
|
||||
[Setup]
|
||||
AppId={{A7E3C2D1-9F40-4B8E-9C1A-NETXWIN64001}
|
||||
AppName={#MyAppName}
|
||||
AppVersion={#MyAppVersion}
|
||||
AppVerName={#MyAppName} {#MyAppVersion}
|
||||
VersionInfoVersion={#MyAppVersion}
|
||||
VersionInfoProductName={#MyAppName}
|
||||
UninstallDisplayName={#MyAppName} {#MyAppVersion}
|
||||
AppPublisher={#MyAppPublisher}
|
||||
AppPublisherURL={#MyAppURL}
|
||||
DefaultDirName={autopf}\NetX
|
||||
|
|
@ -34,7 +39,11 @@ SetupIconFile={#IconFile}
|
|||
UninstallDisplayIcon={app}\packaging\assets\netx.ico
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
WizardStyle=modern
|
||||
; Follows the Windows light/dark setting; windows11 = flat Fluent-style controls.
|
||||
WizardStyle=modern dynamic windows11 hidebevels
|
||||
WizardImageFile={#AssetsDir}\wizard-100.png,{#AssetsDir}\wizard-125.png,{#AssetsDir}\wizard-150.png,{#AssetsDir}\wizard-200.png
|
||||
WizardSmallImageFile={#AssetsDir}\wizard-small-100.png,{#AssetsDir}\wizard-small-125.png,{#AssetsDir}\wizard-small-150.png,{#AssetsDir}\wizard-small-200.png
|
||||
WizardImageBackColor=#0a1222
|
||||
ArchitecturesAllowed=x64compatible
|
||||
ArchitecturesInstallIn64BitMode=x64compatible
|
||||
PrivilegesRequired=admin
|
||||
|
|
@ -42,7 +51,8 @@ PrivilegesRequired=admin
|
|||
; Do NOT use CloseApplications=yes — it can freeze/beep on the Tasks/Ready
|
||||
; pages while scanning locked NetX tray/service processes with no visible dialog.
|
||||
CloseApplications=no
|
||||
ShowLanguageDialog=yes
|
||||
; Pick from the Windows UI language; no extra dialog.
|
||||
ShowLanguageDialog=auto
|
||||
|
||||
[Languages]
|
||||
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||
|
|
@ -51,10 +61,17 @@ Name: "chinesesimplified"; MessagesFile: "Languages\ChineseSimplified.isl"
|
|||
[CustomMessages]
|
||||
english.CreateDesktopIcon=Create a desktop shortcut
|
||||
english.SetupOptions=After install:
|
||||
english.StartTrayNow=Start NetX tray now
|
||||
english.EnableAutostart=Start NetX tray at Windows logon
|
||||
english.EnableAutoUpdate=Enable daily silent auto-update (needs network later)
|
||||
english.InstallService=Install as Windows Service (uses bundled WinSW, offline)
|
||||
english.StartTrayNow=Start NetX now
|
||||
english.RunModeGroup=How NetX runs:
|
||||
english.RunModeTray=Desktop tray (recommended) - control NetX from the notification area
|
||||
english.EnableAutostart=Start automatically when I sign in to Windows
|
||||
english.RunModeService=Windows service - runs at boot without anyone signed in (servers)
|
||||
english.MaintenanceGroup=Maintenance:
|
||||
english.EnableAutoUpdate=Install updates automatically every day (needs network)
|
||||
english.ServiceInstallFailed=NetX was installed, but the Windows service could not be registered (exit %1).%n%nYou can still start NetX from the Start Menu. Log: %2
|
||||
english.IconStart=Start NetX
|
||||
english.IconStop=Stop NetX
|
||||
english.IconUpdate=Check for updates
|
||||
english.UninstallDeleteData=Also delete NetX data (%ProgramData%\NetX)?%n%nYes = remove database, settings, and secrets%nNo = keep data for a later reinstall
|
||||
english.DbPageCaption=Database
|
||||
english.DbPageDescription=Choose built-in or external PostgreSQL. Optional credential key: paste from an existing install, or leave empty to auto-generate.
|
||||
|
|
@ -86,10 +103,17 @@ english.ModeUpdateHint=Replaces program files only. ProgramData\.env and databas
|
|||
english.ModeReinstallHint=Shows the Database page again. Does not delete ProgramData unless you uninstall and choose to.
|
||||
chinesesimplified.CreateDesktopIcon=创建桌面快捷方式
|
||||
chinesesimplified.SetupOptions=安装完成后:
|
||||
chinesesimplified.StartTrayNow=立即启动 NetX 托盘
|
||||
chinesesimplified.EnableAutostart=开机时自动启动 NetX 托盘
|
||||
chinesesimplified.EnableAutoUpdate=启用每日静默自动更新(以后需要联网)
|
||||
chinesesimplified.InstallService=安装为 Windows 服务(使用已捆绑的 WinSW,可离线)
|
||||
chinesesimplified.StartTrayNow=立即启动 NetX
|
||||
chinesesimplified.RunModeGroup=运行方式:
|
||||
chinesesimplified.RunModeTray=桌面托盘(推荐)— 在任务栏通知区域控制 NetX
|
||||
chinesesimplified.EnableAutostart=登录 Windows 时自动启动
|
||||
chinesesimplified.RunModeService=Windows 服务 — 开机即运行,无需登录(适合服务器)
|
||||
chinesesimplified.MaintenanceGroup=维护:
|
||||
chinesesimplified.EnableAutoUpdate=每天自动安装更新(需要联网)
|
||||
chinesesimplified.ServiceInstallFailed=NetX 已安装,但 Windows 服务注册失败(退出码 %1)。%n%n仍可从开始菜单启动 NetX。日志:%2
|
||||
chinesesimplified.IconStart=启动 NetX
|
||||
chinesesimplified.IconStop=停止 NetX
|
||||
chinesesimplified.IconUpdate=检查更新
|
||||
chinesesimplified.UninstallDeleteData=是否同时删除 NetX 数据(%ProgramData%\NetX)?%n%n是 = 删除数据库、配置和密钥%n否 = 保留数据以便以后重装
|
||||
chinesesimplified.DbPageCaption=数据库
|
||||
chinesesimplified.DbPageDescription=选择内置或外置 PostgreSQL。可选粘贴已有 NETX_CREDENTIAL_SECRET_KEY;留空则自动生成。
|
||||
|
|
@ -121,6 +145,12 @@ chinesesimplified.ModeUpdateHint=仅替换程序文件,保留 ProgramData\.env
|
|||
chinesesimplified.ModeReinstallHint=再次显示数据库页。不会删除 ProgramData(除非卸载时选择删除)。
|
||||
|
||||
[Tasks]
|
||||
; Run mode is exclusive: tray + service together would race for the same port.
|
||||
; On upgrades CurPageChanged pre-selects whatever is actually installed.
|
||||
Name: "runtray"; Description: "{cm:RunModeTray}"; GroupDescription: "{cm:RunModeGroup}"; Flags: exclusive
|
||||
Name: "runtray\autostart"; Description: "{cm:EnableAutostart}"
|
||||
Name: "runservice"; Description: "{cm:RunModeService}"; GroupDescription: "{cm:RunModeGroup}"; Flags: exclusive unchecked
|
||||
Name: "autoupdate"; Description: "{cm:EnableAutoUpdate}"; GroupDescription: "{cm:MaintenanceGroup}"; Flags: unchecked
|
||||
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: checkedonce
|
||||
|
||||
[Files]
|
||||
|
|
@ -163,26 +193,30 @@ Type: filesandordirs; Name: "{app}\.venv"
|
|||
Type: files; Name: "{commondesktop}\NetX Start.lnk"
|
||||
Type: files; Name: "{commondesktop}\NetX First-time setup.lnk"
|
||||
Type: files; Name: "{commondesktop}\首次配置(内置或外置数据库).lnk"
|
||||
; Start Menu entries from <= 0.4.16 (English-only names; toggles moved into the tray / Tasks page).
|
||||
Type: files; Name: "{group}\NetX Tray.lnk"
|
||||
Type: files; Name: "{group}\Start NetX.lnk"
|
||||
Type: files; Name: "{group}\Stop NetX.lnk"
|
||||
Type: files; Name: "{group}\Check for updates.lnk"
|
||||
Type: files; Name: "{group}\Open NetX UI.url"
|
||||
Type: files; Name: "{group}\Install Windows Service (admin).lnk"
|
||||
Type: files; Name: "{group}\Enable silent auto-update (daily).lnk"
|
||||
Type: files; Name: "{group}\Enable start at logon.lnk"
|
||||
|
||||
[Icons]
|
||||
; Prefer .cmd targets so Server 2012 / classic Start Menu shows normal program entries.
|
||||
Name: "{group}\NetX Tray"; Filename: "{app}\NetX-Tray.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Start NetX"; Filename: "{app}\NetX-Start.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Stop NetX"; Filename: "{app}\NetX-Stop.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Check for updates"; Filename: "powershell.exe"; Parameters: "-NoProfile -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action update -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Open NetX UI"; Filename: "http://127.0.0.1:8890/"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
; Autostart / auto-update / service toggles live in the tray Settings menu and the Tasks page.
|
||||
Name: "{group}\NetX"; Filename: "{app}\NetX-Tray.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\{cm:IconStart}"; Filename: "{app}\NetX-Start.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\{cm:IconStop}"; Filename: "{app}\NetX-Stop.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\{cm:IconUpdate}"; Filename: "powershell.exe"; Parameters: "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{app}\packaging\launch_shortcut.ps1"" -Action update -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\{cm:ReconfigureDb}"; Filename: "{app}\NetX-FirstRun.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Install Windows Service (admin)"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_service.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -Start"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Enable silent auto-update (daily)"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_update_task.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
Name: "{group}\Enable start at logon"; Filename: "powershell.exe"; Parameters: "-ExecutionPolicy Bypass -File ""{app}\packaging\install_autostart.ps1"" -ProgramRoot ""{app}"""; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"
|
||||
; One desktop shortcut only (tray = start + tray UI). Start Menu keeps Start/Stop entries.
|
||||
; One desktop shortcut only (tray = start + tray UI).
|
||||
Name: "{commondesktop}\NetX"; Filename: "{app}\NetX-Tray.cmd"; WorkingDir: "{app}"; IconFilename: "{app}\packaging\assets\netx.ico"; Tasks: desktopicon
|
||||
|
||||
[Run]
|
||||
; Autostart / service / auto-update are applied from the Tasks page in CurStepChanged.
|
||||
Filename: "powershell.exe"; Parameters: "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{app}\packaging\netx_tray.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -StartOnLaunch"; WorkingDir: "{app}"; Description: "{cm:StartTrayNow}"; Flags: postinstall runhidden nowait skipifsilent; Check: NetxShouldStartTray
|
||||
Filename: "powershell.exe"; Parameters: "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{app}\packaging\install_autostart.ps1"" -ProgramRoot ""{app}"""; WorkingDir: "{app}"; Description: "{cm:EnableAutostart}"; Flags: postinstall runhidden skipifsilent unchecked
|
||||
Filename: "powershell.exe"; Parameters: "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{app}\packaging\install_update_task.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"""; WorkingDir: "{app}"; Description: "{cm:EnableAutoUpdate}"; Flags: postinstall runhidden skipifsilent unchecked
|
||||
Filename: "powershell.exe"; Parameters: "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File ""{app}\packaging\install_service.ps1"" -ProgramRoot ""{app}"" -DataRoot ""{commonappdata}\NetX"" -Start"; WorkingDir: "{app}"; Description: "{cm:InstallService}"; Flags: postinstall runhidden skipifsilent unchecked
|
||||
|
||||
; Stop runtime BEFORE files are deleted. Do not also Exec prepare from
|
||||
; InitializeUninstall (that previously raced/killed unins000 or hung the UI).
|
||||
|
|
@ -232,6 +266,7 @@ var
|
|||
GPgToolsReady: Boolean;
|
||||
GDbConnOk: Boolean;
|
||||
GNextBtnCaption: String;
|
||||
GTasksSynced: Boolean;
|
||||
|
||||
function EnvHasDbMode(): Boolean;
|
||||
var
|
||||
|
|
@ -822,6 +857,7 @@ begin
|
|||
EdCredKey.Left := ScaleX(120);
|
||||
EdCredKey.Width := DbPage.SurfaceWidth - ScaleX(120);
|
||||
EdCredKey.Text := '';
|
||||
EdCredKey.PasswordChar := '*';
|
||||
|
||||
TopPos := TopPos + ScaleY(24);
|
||||
LblCredHint := TNewStaticText.Create(DbPage);
|
||||
|
|
@ -1120,6 +1156,106 @@ begin
|
|||
Exec('powershell.exe', Params, ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||
end;
|
||||
|
||||
function NetxTaskExists(const Name: String): Boolean;
|
||||
var
|
||||
RC: Integer;
|
||||
begin
|
||||
Result := Exec(ExpandConstant('{sys}\schtasks.exe'), '/Query /TN "\NetX\' + Name + '"', '',
|
||||
SW_HIDE, ewWaitUntilTerminated, RC) and (RC = 0);
|
||||
end;
|
||||
|
||||
function NetxServiceExists(): Boolean;
|
||||
var
|
||||
RC: Integer;
|
||||
begin
|
||||
Result := Exec(ExpandConstant('{sys}\sc.exe'), 'query NetX', '', SW_HIDE, ewWaitUntilTerminated, RC) and (RC = 0);
|
||||
end;
|
||||
|
||||
procedure CurPageChanged(CurPageID: Integer);
|
||||
begin
|
||||
{ Existing install: show the run mode / toggles that are really in place
|
||||
(older versions set them from Finish-page checkboxes Inno never recorded). }
|
||||
if (CurPageID = wpSelectTasks) and (not GTasksSynced) then
|
||||
begin
|
||||
GTasksSynced := True;
|
||||
if FileExists(ExpandConstant('{app}\packaging\netx_tray.ps1')) then
|
||||
begin
|
||||
if NetxServiceExists() then
|
||||
WizardSelectTasks('runservice')
|
||||
else
|
||||
WizardSelectTasks('runtray');
|
||||
if NetxTaskExists('NetXTray') then
|
||||
WizardSelectTasks('runtray\autostart')
|
||||
else
|
||||
WizardSelectTasks('!runtray\autostart');
|
||||
if NetxTaskExists('NetXUpdate') then
|
||||
WizardSelectTasks('autoupdate')
|
||||
else
|
||||
WizardSelectTasks('!autoupdate');
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function RunNetxScript(const Script, Extra: String): Integer;
|
||||
var
|
||||
Params: String;
|
||||
begin
|
||||
Params :=
|
||||
'-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "' + ExpandConstant('{app}\packaging\' + Script) + '"' +
|
||||
' -ProgramRoot "' + ExpandConstant('{app}') + '"' +
|
||||
' -DataRoot "' + ExpandConstant('{commonappdata}\NetX') + '"' + Extra;
|
||||
if not Exec('powershell.exe', Params, ExpandConstant('{app}'), SW_HIDE, ewWaitUntilTerminated, Result) then
|
||||
Result := -1;
|
||||
end;
|
||||
|
||||
function TasksGivenOnCommandLine(): Boolean;
|
||||
var
|
||||
i: Integer;
|
||||
P: String;
|
||||
begin
|
||||
Result := False;
|
||||
for i := 1 to ParamCount do
|
||||
begin
|
||||
P := Uppercase(ParamStr(i));
|
||||
if (Pos('/TASKS=', P) = 1) or (Pos('/MERGETASKS=', P) = 1) then
|
||||
begin
|
||||
Result := True;
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure ApplyRunModeTasks();
|
||||
var
|
||||
Code: Integer;
|
||||
begin
|
||||
{ Silent auto-updates keep whatever is installed unless /TASKS is passed. }
|
||||
if WizardSilent and (not TasksGivenOnCommandLine()) then
|
||||
Exit;
|
||||
if not FileExists(ExpandConstant('{commonappdata}\NetX\.env')) then
|
||||
Exit;
|
||||
|
||||
if WizardIsTaskSelected('runservice') then
|
||||
begin
|
||||
Code := RunNetxScript('install_service.ps1', ' -Start');
|
||||
if Code <> 0 then
|
||||
SuppressibleMsgBox(FmtMessage(ExpandConstant('{cm:ServiceInstallFailed}'), [IntToStr(Code),
|
||||
ExpandConstant('{commonappdata}\NetX\data\runtime')]), mbError, MB_OK, IDOK);
|
||||
end
|
||||
else if NetxServiceExists() then
|
||||
RunNetxScript('install_service.ps1', ' -Uninstall');
|
||||
|
||||
if WizardIsTaskSelected('runtray\autostart') then
|
||||
RunNetxScript('install_autostart.ps1', '')
|
||||
else
|
||||
RunNetxScript('install_autostart.ps1', ' -Remove');
|
||||
|
||||
if WizardIsTaskSelected('autoupdate') then
|
||||
RunNetxScript('install_update_task.ps1', '')
|
||||
else if NetxTaskExists('NetXUpdate') then
|
||||
RunNetxScript('install_update_task.ps1', ' -Remove');
|
||||
end;
|
||||
|
||||
procedure CurStepChanged(CurStep: TSetupStep);
|
||||
var
|
||||
UninstallKey: String;
|
||||
|
|
@ -1138,6 +1274,7 @@ begin
|
|||
UninstallKey :=
|
||||
'Software\Microsoft\Windows\CurrentVersion\Uninstall\{A7E3C2D1-9F40-4B8E-9C1A-NETXWIN64001}_is1';
|
||||
RegDeleteValue(HKLM, UninstallKey, 'QuietUninstallString');
|
||||
ApplyRunModeTasks();
|
||||
ResumeNetxServiceAfterInstall();
|
||||
end;
|
||||
end;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue