From 12f4bcee0acedcfc2551867366e97146fb747b67 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 18 Sep 2026 18:07:18 +1000
Subject: [PATCH 1/7] 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
---
.../windows/delphi/general/RegistryKeys.pas | 7 +++
windows/src/desktop/kmshell/kmshell.dpr | 3 +-
windows/src/desktop/kmshell/kmshell.dproj | 13 ++--
windows/src/desktop/kmshell/main/UfrmMain.dfm | 1 +
windows/src/desktop/kmshell/main/UfrmMain.pas | 38 ++++++++++--
.../kmshell/render/OptionsXMLRenderer.pas | 6 ++
...n.Configuration.System.StartupSettings.pas | 59 +++++++++++++++++++
windows/src/desktop/kmshell/xml/config.css | 14 +++++
.../desktop/kmshell/xml/keyman_options.xsl | 12 +++-
windows/src/desktop/kmshell/xml/strings.xml | 15 +++++
10 files changed, 155 insertions(+), 13 deletions(-)
create mode 100644 windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 0f627c7329..62dfd4250c 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -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;
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index e2d28b88d2..eac9017de7 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -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}
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 371ce4cd49..8680fa5a65 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -358,6 +358,7 @@
+
Cfg_2
@@ -419,12 +420,6 @@
False
-
-
- .\
- true
-
-
kmshell.rsm
@@ -437,6 +432,12 @@
true
+
+
+ .\
+ true
+
+
1
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.dfm b/windows/src/desktop/kmshell/main/UfrmMain.dfm
index 5a7dce719f..8d9d513ea4 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.dfm
+++ b/windows/src/desktop/kmshell/main/UfrmMain.dfm
@@ -16,6 +16,7 @@ inherited frmMain: TfrmMain
PixelsPerInch = 96
TextHeight = 13
object AppEvents: TApplicationEvents
+ OnActivate = AppEventsActivate
OnMessage = AppEventsMessage
Left = 416
Top = 256
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index a8fdfe9845..a3810ccbed 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -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);
diff --git a/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas b/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
index fb37c9cae1..b0374ca7ff 100644
--- a/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
+++ b/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
@@ -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 + 'True'
+ else
+ Result := Result + 'False';
FGroups := TStringList.Create;
try
Result := Result + '';
diff --git a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
new file mode 100644
index 0000000000..a8c6c119a1
--- /dev/null
+++ b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
@@ -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.
diff --git a/windows/src/desktop/kmshell/xml/config.css b/windows/src/desktop/kmshell/xml/config.css
index 2c00d41adc..cf0316c36f 100644
--- a/windows/src/desktop/kmshell/xml/config.css
+++ b/windows/src/desktop/kmshell/xml/config.css
@@ -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;
diff --git a/windows/src/desktop/kmshell/xml/keyman_options.xsl b/windows/src/desktop/kmshell/xml/keyman_options.xsl
index 0d68ca46ca..1d06a83dc9 100644
--- a/windows/src/desktop/kmshell/xml/keyman_options.xsl
+++ b/windows/src/desktop/kmshell/xml/keyman_options.xsl
@@ -16,6 +16,16 @@
+
+
+
+
+
+ keyman:options_startupsettings
+ 1
+
+
+
@@ -105,4 +115,4 @@
-
\ No newline at end of file
+
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 7d6f53c85d..285ffd4965 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -404,6 +404,21 @@
Start when Windows starts
+
+
+
+ Windows has disabled Keyman from starting automatically. Enable Keyman in Windows Startup settings.
+
+
+
+
+ Open Windows Startup settings
+
+
+
+
+ Windows has disabled Keyman from starting automatically. Open Windows Startup settings now?
+
From edd6bcd8dc6efbcdad7283483b45913beb0476a0 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 25 Sep 2026 16:21:09 +1000
Subject: [PATCH 2/7] fix(windows): add icon and flex layout for warning
---
windows/src/desktop/kmshell/xml/config.css | 26 ++++++++++++++-----
.../desktop/kmshell/xml/keyman_options.xsl | 15 ++++++-----
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/windows/src/desktop/kmshell/xml/config.css b/windows/src/desktop/kmshell/xml/config.css
index cf0316c36f..7293ad9b81 100644
--- a/windows/src/desktop/kmshell/xml/config.css
+++ b/windows/src/desktop/kmshell/xml/config.css
@@ -738,17 +738,29 @@ table tr
}
.options_startup_warning
{
- background: #FFF4CE;
- border: 1px solid #D6B656;
- color: #4A3B00;
+ background: #FFFFFF;
+ border: 2px solid #CC3846;
+ color: #000000;
+ display: flex;
+ align-items: flex-start;
margin: 4px 8px 8px 32px;
padding: 8px;
}
-.options_startup_warning input,
-.options_startup_warning button
+.options_startup_warning_icon
{
- margin-left: 0;
- margin-top: 6px;
+ flex: 0 0 auto;
+ height: 32px;
+ margin: 8px 8px 0 0;
+ width: 32px;
+}
+.options_startup_warning_content
+{
+ flex: 1 1 auto;
+}
+#options .options_startup_warning input,
+#options .options_startup_warning button
+{
+ margin: 6px 0 0;
}
#options_control
{
diff --git a/windows/src/desktop/kmshell/xml/keyman_options.xsl b/windows/src/desktop/kmshell/xml/keyman_options.xsl
index 1d06a83dc9..0fafe7665c 100644
--- a/windows/src/desktop/kmshell/xml/keyman_options.xsl
+++ b/windows/src/desktop/kmshell/xml/keyman_options.xsl
@@ -18,12 +18,15 @@
-
-
-
- keyman:options_startupsettings
- 1
-
+

+
+
+
+
+ keyman:options_startupsettings
+ 1
+
+
From 1ab603d235d7e82b8cde08c73559c5b04adec677 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Fri, 25 Sep 2026 17:22:44 +1000
Subject: [PATCH 3/7] fix(windows): white space clean up
---
.../util/Keyman.Configuration.System.StartupSettings.pas | 4 ----
windows/src/desktop/kmshell/xml/strings.xml | 2 +-
2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
index a8c6c119a1..31e3da09ec 100644
--- a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
+++ b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
@@ -2,8 +2,6 @@
* 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;
@@ -27,8 +25,6 @@ const
StartupEnabled: Byte = $02;
StartupDisabled: Byte = $03;
-
-
class function TWindowsStartupSettings.IsWindowsStartupDisabled: Boolean;
var
reg: TRegistry;
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 285ffd4965..579bb12c46 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -407,7 +407,7 @@
- Windows has disabled Keyman from starting automatically. Enable Keyman in Windows Startup settings.
+ Windows has disabled Keyman from starting automatically. Enable Keyman Configuration in Windows Startup settings.
From 94e400300eb2dd0702fdca014ba30dbc35548a37 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Mon, 28 Sep 2026 13:29:34 +1000
Subject: [PATCH 4/7] fix(windows): change word automatically - on startup
---
windows/src/desktop/kmshell/xml/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index 579bb12c46..eed85a5234 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -407,7 +407,7 @@
- Windows has disabled Keyman from starting automatically. Enable Keyman Configuration in Windows Startup settings.
+ Windows has disabled Keyman from starting on startup. Enable Keyman Configuration in Windows Startup settings.
@@ -417,7 +417,7 @@
- Windows has disabled Keyman from starting automatically. Open Windows Startup settings now?
+ Windows has disabled Keyman from starting on startup. Open Windows Startup settings now?
From 8d5608ee13fb066f592e0a3e4e0bce3329afcccb Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 29 Sep 2026 10:55:23 +1000
Subject: [PATCH 5/7] fix(windows): apply review suggestions
Co-authored-by: Marc Durdin
---
common/windows/delphi/general/RegistryKeys.pas | 7 +------
windows/src/desktop/kmshell/main/UfrmMain.pas | 3 +++
windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas | 1 -
.../util/Keyman.Configuration.System.StartupSettings.pas | 7 +++++--
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/common/windows/delphi/general/RegistryKeys.pas b/common/windows/delphi/general/RegistryKeys.pas
index 62dfd4250c..da513ab12c 100644
--- a/common/windows/delphi/general/RegistryKeys.pas
+++ b/common/windows/delphi/general/RegistryKeys.pas
@@ -283,7 +283,7 @@ const
SRegValue_WindowsRun_Keyman = 'Keyman';
- SRegKey_StartupApproved_Run = 'Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run';
+ SRegKey_StartupApproved_Run = 'Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run'; // CU
SRegValue_LanguageCheckDisabledItems = 'language check disabled items'; // CU
@@ -416,11 +416,6 @@ 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;
diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas
index 48a294d4f9..c8fb4612f3 100644
--- a/windows/src/desktop/kmshell/main/UfrmMain.pas
+++ b/windows/src/desktop/kmshell/main/UfrmMain.pas
@@ -707,6 +707,9 @@ end;
procedure TfrmMain.Options_StartupSettings;
begin
+ // We'll refresh the page when we return to Keyman Configuration
+ // after the Startup Apps settings page is popped up in case the
+ // user has changed the setting
FRefreshOnActivate := TUtilExecute.Shell(0, 'ms-settings:startupapps', '', '');
end;
diff --git a/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas b/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
index b0374ca7ff..5bdbda402e 100644
--- a/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
+++ b/windows/src/desktop/kmshell/render/OptionsXMLRenderer.pas
@@ -45,7 +45,6 @@ uses
Keyman.Configuration.System.StartupSettings,
kmint,
MessageIdentifiers,
- RegistryKeys,
utilxml,
Variants;
diff --git a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
index 31e3da09ec..ff5c126521 100644
--- a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
+++ b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
@@ -2,7 +2,9 @@
* 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.
+ *
+ * This unit assists in determining whether Keyman is enabled to start
+ * with Windows in the Windows startup settings.
*)
unit Keyman.Configuration.System.StartupSettings;
@@ -41,7 +43,8 @@ begin
Exit;
if reg.GetDataSize(SRegValue_WindowsRun_Keyman) <> SizeOf(data) then
Exit;
- reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data));
+ if reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data)) <> sizeof(data) then
+ Exit;
Result := data[0] = StartUpDisabled;
except
on E: ERegistryException do
From 6112104e55b3e889a1c90aa37bcf81677d7ec245 Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Tue, 29 Sep 2026 11:30:21 +1000
Subject: [PATCH 6/7] fix(windows): add docnote on startupapproved key
---
.../Keyman.Configuration.System.StartupSettings.pas | 12 ++++++++++--
windows/src/desktop/kmshell/xml/strings.xml | 2 +-
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
index ff5c126521..2ba972445a 100644
--- a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
+++ b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
@@ -3,7 +3,7 @@
*
* Created by Ross Cruickshank on 2026-09-18
*
- * This unit assists in determining whether Keyman is enabled to start
+ * This unit assists in determining whether Keyman is enabled to start
* with Windows in the Windows startup settings.
*)
unit Keyman.Configuration.System.StartupSettings;
@@ -24,6 +24,14 @@ uses
SysUtils;
const
+// This registry key is not documented by Microsoft, others have determined:
+// The value associated with it is 12 bytes in length the first being the
+// enabled/disabled state. The rest of the byte is the timestamp
+// of when it was disabled.
+// 03 - startup disabled, user with admin rights can enable
+// 02 - startup enabled, user with admin rights can disable
+// See issue #15785 for more details.
+
StartupEnabled: Byte = $02;
StartupDisabled: Byte = $03;
@@ -43,7 +51,7 @@ begin
Exit;
if reg.GetDataSize(SRegValue_WindowsRun_Keyman) <> SizeOf(data) then
Exit;
- if reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data)) <> sizeof(data) then
+ if reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data)) <> sizeof(data) then
Exit;
Result := data[0] = StartUpDisabled;
except
diff --git a/windows/src/desktop/kmshell/xml/strings.xml b/windows/src/desktop/kmshell/xml/strings.xml
index eed85a5234..c09cc98c72 100644
--- a/windows/src/desktop/kmshell/xml/strings.xml
+++ b/windows/src/desktop/kmshell/xml/strings.xml
@@ -407,7 +407,7 @@
- Windows has disabled Keyman from starting on startup. Enable Keyman Configuration in Windows Startup settings.
+ Windows has disabled Keyman from starting on startup. Enable Keyman in Windows Startup settings.
From f09ff0f48732ecd100d2a77ca48c765882231c9f Mon Sep 17 00:00:00 2001
From: rc-swag <58423624+rc-swag@users.noreply.github.com>
Date: Thu, 1 Oct 2026 11:57:44 +1000
Subject: [PATCH 7/7] fix(windows): add other observed cases of disabled
---
...n.Configuration.System.StartupSettings.pas | 38 +++++++++++++------
1 file changed, 27 insertions(+), 11 deletions(-)
diff --git a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
index 2ba972445a..371ad31c60 100644
--- a/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
+++ b/windows/src/desktop/kmshell/util/Keyman.Configuration.System.StartupSettings.pas
@@ -23,17 +23,33 @@ uses
Windows,
SysUtils;
-const
-// This registry key is not documented by Microsoft, others have determined:
-// The value associated with it is 12 bytes in length the first being the
-// enabled/disabled state. The rest of the byte is the timestamp
-// of when it was disabled.
-// 03 - startup disabled, user with admin rights can enable
-// 02 - startup enabled, user with admin rights can disable
-// See issue #15785 for more details.
+(* This registry key is not documented by Microsoft, others have determined:
+The value associated with it is 12 bytes in length the first being the
+enabled/disabled state. The rest of the byte is the timestamp
+of when it was disabled. Drawing from our investigation of the registry
+and a broader search of stackoverflow, superuser and other sources on
+the web, the following table can be derived for the first byte of the value:
+See issue #15785 for more details and links.
- StartupEnabled: Byte = $02;
- StartupDisabled: Byte = $03;
+| First byte | Reported behaviour | Source quality |
+| ---------- | ------------------------------ | --------------------------------------|
+| `00` | enabled, user can disable | Our test on two Win 11 Home Machines |
+| `01` | Disabled, user can enable | Our test + Super User |
+| `02` | Enabled, user can disable | Multiple sources |
+| `03` | Disabled, user can enable | Multiple sources |
+| `06` | Enableduser can disable | Stack Overflow, not seen by us |
+| `07` | Disabled, user can enable | Stack Overflow, not seen by us |
+| `08` | Enabled, user cannot disable | Super User, not seen by us |
+| `09` | Disabled, user cannot enable | Super User, not seen by us |
+
+Maybe it is just bit 0 of the first byte that is enabled disabled? There in no
+point checking for $09 as the user cannot change the setting. We would need a
+different message.
+
+*)
+
+const
+ StartupDisabled: set of Byte = [$01, $03, $07];
class function TWindowsStartupSettings.IsWindowsStartupDisabled: Boolean;
var
@@ -53,7 +69,7 @@ begin
Exit;
if reg.ReadBinaryData(SRegValue_WindowsRun_Keyman, data, SizeOf(data)) <> sizeof(data) then
Exit;
- Result := data[0] = StartUpDisabled;
+ Result := data[0] in StartupDisabled;
except
on E: ERegistryException do
Result := False;