unit PM.AutoStart; { Start-with-Windows toggle. Mechanism: HKCU\Software\Microsoft\Windows\CurrentVersion\Run - Per-user (no admin needed) - Visible in Task Manager → Startup tab (user can disable from there) - Standard pattern used by 1Password, Bitwarden, Slack, Discord, etc. Command line: launched with --tray so PMServer starts minimized to the tray instead of popping a window during Windows login. Value name "PMServer" — fixed string, not exe path-derived, so moving the exe + flipping the toggle off/on cleans up the old entry. } interface function IsAutoStartEnabled: Boolean; // Returns True on success. Failures (registry locked, etc.) are silent — // caller surfaces the resulting status via IsAutoStartEnabled. function SetAutoStart(AEnabled: Boolean): Boolean; implementation uses System.SysUtils, System.Win.Registry, Winapi.Windows; const RUN_KEY = 'Software\Microsoft\Windows\CurrentVersion\Run'; VALUE_NAME = 'PMServer'; TRAY_ARG = '-tray'; // single dash so Delphi's FindCmdLineSwitch picks it up function BuildAutoStartCommand: string; begin // Quote the exe path (may contain spaces) and append the silent-start // arg. Single string written to a REG_SZ value — Windows splits args // the same way CommandLineToArgvW does. Result := '"' + ParamStr(0) + '" ' + TRAY_ARG; end; function IsAutoStartEnabled: Boolean; var R: TRegistry; LCurrent: string; begin Result := False; R := TRegistry.Create(KEY_READ); try R.RootKey := HKEY_CURRENT_USER; if not R.OpenKeyReadOnly(RUN_KEY) then Exit; try if not R.ValueExists(VALUE_NAME) then Exit; LCurrent := R.ReadString(VALUE_NAME); // Consider the toggle "on" only if the registered command points // to OUR exe — a stale entry from a moved exe should read as off // so the user can re-enable to refresh the path. Result := SameText(LCurrent, BuildAutoStartCommand); finally R.CloseKey; end; finally R.Free; end; end; function SetAutoStart(AEnabled: Boolean): Boolean; var R: TRegistry; begin Result := False; R := TRegistry.Create(KEY_READ or KEY_WRITE); try R.RootKey := HKEY_CURRENT_USER; if not R.OpenKey(RUN_KEY, True) then Exit; try if AEnabled then begin R.WriteString(VALUE_NAME, BuildAutoStartCommand); Result := True; end else begin if R.ValueExists(VALUE_NAME) then R.DeleteValue(VALUE_NAME); Result := True; end; finally R.CloseKey; end; finally R.Free; end; end; end.