fix(windows): add warning when system startup disabled

This is the initial commit to add a check to see if the Windows
System setting for Keyman startup is disabled when the users
as select Keyman to start with Windows.

Fixes: #15785
This commit is contained in:
rc-swag 2026-09-18 18:07:18 +10:00
parent 9ff8c72fb5
commit 12f4bcee0a
10 changed files with 155 additions and 13 deletions

View file

@ -283,6 +283,8 @@ const
SRegValue_WindowsRun_Keyman = 'Keyman';
SRegKey_StartupApproved_Run = 'Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run';
SRegValue_LanguageCheckDisabledItems = 'language check disabled items'; // CU
{-------------------------------------------------------------------------------
@ -414,6 +416,11 @@ function BuildKeyboardLanguagesKey_CU(const KeyboardID: string): string;
implementation
uses
System.Win.Registry,
Windows,
SysUtils;
function BuildKeyboardOptionKey_CU(const KeyboardID: string): string;
begin
Result := SRegKey_ActiveKeyboards_CU + '\' + KeyboardID + '\' + SRegSubKey_KeyboardOptions;

View file

@ -183,7 +183,8 @@ uses
Keyman.System.DownloadUpdate in 'main\Keyman.System.DownloadUpdate.pas',
Keyman.System.ExecutionHistory in '..\..\..\..\common\windows\delphi\general\Keyman.System.ExecutionHistory.pas',
Keyman.Configuration.UI.UfrmStartInstall in 'main\Keyman.Configuration.UI.UfrmStartInstall.pas' {frmStartInstall},
Keyman.Configuration.Util.NetworkConnection in 'util\Keyman.Configuration.Util.NetworkConnection.pas';
Keyman.Configuration.Util.NetworkConnection in 'util\Keyman.Configuration.Util.NetworkConnection.pas',
Keyman.Configuration.System.StartupSettings in 'util\Keyman.Configuration.System.StartupSettings.pas';
{$R VERSION.RES}
{$R manifest.res}

View file

@ -358,6 +358,7 @@
<Form>frmStartInstall</Form>
</DCCReference>
<DCCReference Include="util\Keyman.Configuration.Util.NetworkConnection.pas"/>
<DCCReference Include="util\Keyman.Configuration.System.StartupSettings.pas"/>
<None Include="Profiling\AQtimeModule1.aqt"/>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
@ -419,12 +420,6 @@
<Platform value="Win64">False</Platform>
</Platforms>
<Deployment Version="3">
<DeployFile LocalName="Profiling\AQtimeModule1.aqt" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="bin\Win32\Debug\kmshell.rsm" Configuration="Debug" Class="DebugSymbols">
<Platform Name="Win32">
<RemoteName>kmshell.rsm</RemoteName>
@ -437,6 +432,12 @@
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Profiling\AQtimeModule1.aqt" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="OSX32">
<Operation>1</Operation>

View file

@ -16,6 +16,7 @@ inherited frmMain: TfrmMain
PixelsPerInch = 96
TextHeight = 13
object AppEvents: TApplicationEvents
OnActivate = AppEventsActivate
OnMessage = AppEventsMessage
Left = 416
Top = 256

View file

@ -99,10 +99,12 @@ type
procedure TntFormClose(Sender: TObject; var Action: TCloseAction);
procedure TntFormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure AppEventsMessage(var Msg: tagMSG; var Handled: Boolean);
procedure AppEventsActivate(Sender: TObject);
private
FPageTag: Integer;
FClosing: Boolean;
FRefreshOnActivate: Boolean;
wm_keyman_refresh: Cardinal;
LastRefreshToken: System.IntPtr;
@ -133,6 +135,7 @@ type
procedure Options_ResetHints;
procedure Options_BaseKeyboard; // I4169
procedure Options_SettingsManager;
procedure Options_StartupSettings;
procedure Hotkey_Set(params: TStringList);
procedure Hotkey_Clear(params: TStringList);
@ -193,6 +196,7 @@ uses
Keyman.Configuration.Util.NetworkConnection,
Keyman.Configuration.System.UmodWebHttpServer,
Keyman.Configuration.System.HttpServer.App.ConfigMain,
Keyman.Configuration.System.StartupSettings,
Keyman.Configuration.UI.InstallFile,
Keyman.Configuration.UI.UfrmSettingsManager,
Keyman.Configuration.UI.UfrmStartInstall,
@ -264,6 +268,15 @@ begin
inherited;
end;
procedure TfrmMain.AppEventsActivate(Sender: TObject);
begin
if FRefreshOnActivate then
begin
FRefreshOnActivate := False;
DoRefresh;
end;
end;
{-------------------------------------------------------------------------------
- Form-level functions -
------------------------------------------------------------------------------}
@ -343,6 +356,7 @@ begin
else if command = 'options_resethints' then Options_ResetHints
else if command = 'options_basekeyboard' then Options_BaseKeyboard // I4169
else if command = 'options_settingsmanager' then Options_SettingsManager
else if command = 'options_startupsettings' then Options_StartupSettings
else if command = 'language_underlyingkeyboard' then Options_BaseKeyboard
@ -686,17 +700,19 @@ begin
end;
end;
procedure TfrmMain.Options_StartupSettings;
begin
FRefreshOnActivate := TUtilExecute.Shell(0, 'ms-settings:startupapps', '', '');
end;
procedure TfrmMain.Options_ClickCheck(params: TStringList);
var
option: IKeymanOption;
startupDisabled: Boolean;
begin
if GetOptionFromParams(params, option) and option.Enabled then
begin
option.Value := not option.Value;
if (option.ID = 'koDebugging') and option.Value then
ShowMessage(MsgFromId(SKDebuggingWarning));
kmcom.Errors.Clear;
DoApply;
if kmcom.Errors.Count > 0 then
@ -706,7 +722,19 @@ begin
// be invalid
ShowMessage(kmcom.Errors[0].Description);
DoRefresh;
end;
end
else
begin
if (option.ID = 'koDebugging') and option.Value then
ShowMessage(MsgFromId(SKDebuggingWarning));
startupDisabled := (option.ID = 'koStartWithWindows') and option.Value and TWindowsStartupSettings.IsWindowsStartupDisabled;
DoRefresh;
if startupDisabled then
if MessageDlg(MsgFromId(S_StartupDisabledWarningOpenSettings),
mtWarning, [mbYes, mbNo], 0) = mrYes then
Options_StartupSettings;
end
end
else
ShowMessage(params.Text);

View file

@ -42,8 +42,10 @@ implementation
uses
custinterfaces,
Keyman.Configuration.System.StartupSettings,
kmint,
MessageIdentifiers,
RegistryKeys,
utilxml,
Variants;
@ -70,6 +72,10 @@ var
begin
References := Null; // I2678
Result := kmcom.Options.SerializeXML(0, '', References);
if TWindowsStartupSettings.IsWindowsStartupDisabled then
Result := Result + '<StartupDisabled>True</StartupDisabled>'
else
Result := Result + '<StartupDisabled>False</StartupDisabled>';
FGroups := TStringList.Create;
try
Result := Result + '<OptionGroups>';

View file

@ -0,0 +1,59 @@
(**
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Ross Cruickshank on 2026-09-18
*
*
* This unit assists in determining whether Keyman is enabled to start with Windows in the Windows startup settings.
*)
unit Keyman.Configuration.System.StartupSettings;
interface
type
TWindowsStartupSettings = class
class function IsWindowsStartupDisabled: Boolean; static;
end;
implementation
uses
RegistryKeys,
System.Win.Registry,
Windows,
SysUtils;
const
StartupEnabled: Byte = $02;
StartupDisabled: Byte = $03;
class function TWindowsStartupSettings.IsWindowsStartupDisabled: Boolean;
var
reg: TRegistry;
data: array[0..11] of Byte;
begin
Result := False;
reg := TRegistry.Create;
try
try
reg.RootKey := HKEY_CURRENT_USER;
if not reg.OpenKeyReadOnly('\' + SRegKey_StartupApproved_Run) then
Exit;
if not reg.ValueExists(SRegValue_WindowsRun_Keyman) then
Exit;
if reg.GetDataSize(SRegValue_WindowsRun_Keyman) <> SizeOf(data) then
Exit;
reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data));
Result := data[0] = StartUpDisabled;
except
on E: ERegistryException do
Result := False;
end;
finally
reg.Free;
end;
end;
end.

View file

@ -736,6 +736,20 @@ table tr
{
display: none;
}
.options_startup_warning
{
background: #FFF4CE;
border: 1px solid #D6B656;
color: #4A3B00;
margin: 4px 8px 8px 32px;
padding: 8px;
}
.options_startup_warning input,
.options_startup_warning button
{
margin-left: 0;
margin-top: 6px;
}
#options_control
{
left: 0;

View file

@ -16,6 +16,16 @@
<xsl:for-each select="//KeymanOption[group=current()/name]">
<xsl:if test="optiontype = 1">
<xsl:call-template name="option" />
<xsl:if test="id='koStartWithWindows' and value='True' and //StartupDisabled='True'">
<div class="options_startup_warning">
<div><xsl:value-of select="$locale/string[@name='S_StartupDisabledWarning']"/></div>
<xsl:call-template name="button">
<xsl:with-param name="caption"><xsl:value-of select="$locale/string[@name='S_Button_StartupSettings']"/></xsl:with-param>
<xsl:with-param name="command">keyman:options_startupsettings</xsl:with-param>
<xsl:with-param name="tabid">1</xsl:with-param>
</xsl:call-template>
</div>
</xsl:if>
</xsl:if>
</xsl:for-each>
<xsl:if test="current() != //OptionGroups/OptionGroup[last()]">
@ -105,4 +115,4 @@
</xsl:if>
</xsl:template>
</xsl:stylesheet>
</xsl:stylesheet>

View file

@ -404,6 +404,21 @@
<!-- Introduced: 7.0.230.0 -->
<string name="koStartWithWindows" comment="Startup options - Start Keyman with Windows start">Start when Windows starts</string>
<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 19.0.286.0 -->
<string name="S_StartupDisabledWarning" comment="Startup options - Windows has disabled Keyman startup">Windows has disabled Keyman from starting automatically. Enable Keyman in Windows Startup settings.</string>
<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 19.0.286.0 -->
<string name="S_Button_StartupSettings" comment="Startup options - open Windows Startup settings">Open Windows Startup settings</string>
<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 19.0.286.0 -->
<string name="S_StartupDisabledWarningOpenSettings" comment="Startup options - Windows has disabled Keyman startup opening settings">Windows has disabled Keyman from starting automatically. Open Windows Startup settings now?</string>
<!-- Context: Configuration Dialog - Options tab -->
<!-- String Type: FormatString -->
<!-- Introduced: 7.0.230.0 -->