Merge pull request #16162 from keymanapp/fix/windows/15152/basekeyboard-user-not-admin

fix(windows): set basekeyboard as current user not the admin user on an elevated process
This commit is contained in:
rc-swag 2026-09-23 09:52:25 +10:00 committed by GitHub
commit f8ee1b7c8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 329 additions and 105 deletions

View file

@ -91,6 +91,27 @@ function IsProjectFile(const FileName: string): Boolean;
function IsKeyboardFile(const FileName: string): Boolean;
function RemoveFileExtension(Filename, Extension: string): string;
(**
* Builds the compiled keyboard filename by inserting the base keyboard ID
* before the .kmx extension.
*
* @param KeyboardFileName Keyboard filename, in the form '[path\]keyboardid[.kmx]'
* @param BaseKeyboardIDHex Base keyboard KLID in eight digit hexadecimal form
* @return Compiled keyboard filename, in the form '[path\]keyboardid-<KLID>.kmx'
*)
function BuildKeyboardFilenameWithBaseKeyboardID(const KeyboardFileName: string; BaseKeyboardIDHex: string): string;
(**
* Builds the dead-key compiled keyboard filename by inserting the base
* keyboard ID and -d suffix before the .kmx extension.
*
* @param KeyboardFileName Keyboard filename, in the form '[path]\keyboardid[.kmx]'
* @param BaseKeyboardIDHex Base keyboard KLID in eight digit hexadecimal form
* @return Dead-key compiled keyboard filename, in the form '[path\]keyboardid-<KLID>-d.kmx'
*)
function BuildKeyboardFilenameWithBaseKeyboardIDAndDeadkey(const KeyboardFileName: string; BaseKeyboardIDHex: string): string;
type
TKeymanFileTypeInfo = class
public
@ -208,4 +229,14 @@ begin
SameText(ExtractFileExt(Filename), ExtractFileExt(PackageFile_Welcome)));
end;
function BuildKeyboardFilenameWithBaseKeyboardID(const KeyboardFileName: string; BaseKeyboardIDHex: string): string;
begin
Result := ChangeFileExt(KeyboardFileName, '') + '-' + BaseKeyboardIDHex + '.kmx';
end;
function BuildKeyboardFilenameWithBaseKeyboardIDAndDeadkey(const KeyboardFileName: string; BaseKeyboardIDHex: string): string;
begin
Result := ChangeFileExt(KeyboardFileName, '') + '-' + BaseKeyboardIDHex + '-d.kmx';
end;
end.

View file

@ -1,18 +1,18 @@
(*
Name: utilstr
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 1 Aug 2006
Modified Date: 8 Jun 2012
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 01 Aug 2006 - mcdurdin - Refactor util functions into multiple units
23 Aug 2006 - mcdurdin - Add StringToExtString and WideQuotedStr functions
14 Sep 2006 - mcdurdin - Add RectToString, StringToRect, use widestrings for some functions
@ -58,8 +58,6 @@ function GetTokenFromCaret(line: string; var selx, sellen: Integer): string;
function WideQuotedStr(const str: WideString): WideString; deprecated; // I3310
implementation
uses
@ -78,7 +76,7 @@ begin
Result := '';
Exit;
end;
if s[1] = '"' then
begin
Delete(s,1,1);

View file

@ -46,7 +46,8 @@ uses
kmint,
RegistryKeys,
utilexecute,
utilkmshell;
utilkmshell,
utilfiletypes;
const
{ CurrentMnemonicLayoutVersion = 476; // First 9.0 build with fixes for mnemonic layouts }
@ -170,7 +171,7 @@ begin
FBaseKeyboardIDHex := IntToHex(BaseKeyboardID, 8);
FBaseFileName := Keyboard.Filename;
FDestFileName := OutputFileName;
FDestDeadkeyFileName := ChangeFileExt(FDestFileName, '') + '-d.kmx'; // I4552
FDestDeadkeyFileName := BuildKeyboardFilenameWithBaseKeyboardIDAndDeadkey(FBaseFileName, FBaseKeyboardIDHex); // I4552
FMCompilePath := TKeymanPaths.KeymanEngineInstallPath(TKeymanPaths.S_MCompileExe);
FDestPath := ExtractFileDir(Keyboard.Filename);

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.BaseKeyboard in 'main\Keyman.Configuration.System.BaseKeyboard.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="main\Keyman.Configuration.System.BaseKeyboard.pas"/>
<None Include="Profiling\AQtimeModule1.aqt"/>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
@ -419,6 +420,12 @@
<Platform value="Win64">False</Platform>
</Platforms>
<Deployment Version="3">
<DeployFile LocalName="bin\Win32\Debug\kmshell.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>kmshell.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="Profiling\AQtimeModule1.aqt" Configuration="Debug" Class="ProjectFile">
<Platform Name="Win32">
<RemoteDir>.\</RemoteDir>
@ -431,12 +438,6 @@
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployFile LocalName="bin\Win32\Debug\kmshell.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>kmshell.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="OSX32">
<Operation>1</Operation>

View file

@ -0,0 +1,108 @@
(*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Ross Cruickshank on 2026-09-12
*
*
* This unit assists in setting the base keyboard configuration,
* including compiling the installed keyboard layouts against
* the selected base keyboard.
*)
unit Keyman.Configuration.System.BaseKeyboard;
interface
uses
Winapi.Windows,
System.SysUtils,
keymanapi_TLB;
(**
* Returns true if the keyboard files need to be compiled for the specified KLID.
* @param BaseKeyboardID KLID of the base keyboard to compile.
* @returns True If the keyboard files need to be compiled.
*)
function BaseKeyboardNeedsMCompile(BaseKeyboardID: Integer): Boolean;
(**
* Sets the base keyboard KLID for the current user and compiles the keyboard
* layout files if necessary. In the case the compiled keyboard files are
*not present, it will require elevation.
* @param WindowHandle Window handle to own the elevation prompt.
* @param BaseKeyboardID KLID of the base keyboard KLID to set.
* @returns True when the base keyboard setting has been applied.
*)
function SetBaseKeyboard(WindowHandle: THandle; BaseKeyboardID: Integer): Boolean;
(**
* Compiles the installed keyboard layouts for the specified KLID.
* Must run elevated.
*
* @param BaseKeyboardID KLID of the base keyboard to compile.
* @returns True when the compilation is successful.
*)
function MCompileBaseKeyboard(BaseKeyboardID: Integer): Boolean;
implementation
uses
kmint,
utilkmshell,
utilfiletypes;
function BaseKeyboardNeedsMCompile(BaseKeyboardID: Integer): Boolean;
var
I: Integer;
Keyboard: IKeymanKeyboardInstalled;
KeyboardFileName: string;
BaseKeyboardIDHex: string;
begin
BaseKeyboardIDHex := IntToHex(BaseKeyboardID, 8);
for I := 0 to kmcom.Keyboards.Count - 1 do
begin
Keyboard := kmcom.Keyboards.Items[I];
KeyboardFileName := Keyboard.Filename;
if FileExists(KeyboardFileName) and
(not FileExists(BuildKeyboardFilenameWithBaseKeyboardID(KeyboardFileName, BaseKeyboardIDHex)) or
not FileExists(BuildKeyboardFilenameWithBaseKeyboardIDAndDeadkey(KeyboardFileName, BaseKeyboardIDHex))) then
Exit(True);
end;
Result := False;
end;
function SetBaseKeyboard(WindowHandle: THandle; BaseKeyboardID: Integer): Boolean;
begin
Result := True;
if BaseKeyboardNeedsMCompile(BaseKeyboardID) then
begin
if not kmcom.SystemInfo.IsAdministrator then
begin
Result := WaitForElevatedConfiguration(WindowHandle, '-mcompilekbds ' + IntToHex(BaseKeyboardID, 8)) = 0;
end
else
Result := MCompileBaseKeyboard(BaseKeyboardID);
end;
if not Result then
Exit;
kmcom.Options['koBaseLayout'].Value := BaseKeyboardID;
kmcom.Options.Apply;
end;
function MCompileBaseKeyboard(BaseKeyboardID: Integer): Boolean;
var
i: Integer;
kbd: IKeymanKeyboardInstalled;
begin
Result := False;
// can be called from command line so test for admin
if not kmcom.SystemInfo.IsAdministrator then
Exit;
for i := 0 to kmcom.Keyboards.Count - 1 do
begin
kbd := kmcom.Keyboards[i];
(kbd as IKeymanKeyboardInstalled2).MCompileForBaseKeyboard(BaseKeyboardID);
end;
Result := True;
end;
end.

View file

@ -11,13 +11,21 @@ type
TfrmBaseKeyboard = class(TfrmWebContainer)
procedure TntFormCreate(Sender: TObject);
private
FBaseKeyboardID: Integer;
procedure Footer_Cancel;
procedure Footer_OK(params: TStringList);
protected
procedure FireCommand(const command: WideString; params: TStringList); override;
end;
function ConfigureBaseKeyboard: Boolean;
(**
* Displays a form for the user to select a base keyboard. If the user selects a base
* keyboard, the KLID is used to Set the Base Keyboard.
*
* @returns True if the user selected base keyboard has been set.
*)
function ConfigureAndSetBaseKeyboard(WindowHandle: THandle): Boolean;
implementation
@ -25,15 +33,22 @@ implementation
uses
BaseKeyboards,
kmint;
ErrorControlledRegistry,
RegistryKeys,
keymanapi_TLB,
Keyman.Configuration.System.BaseKeyboard,
kmint,
utilkmshell;
function ConfigureBaseKeyboard: Boolean;
function ConfigureAndSetBaseKeyboard(WindowHandle: THandle): Boolean;
var BaseKeyboardID: Integer;
begin
with TfrmBaseKeyboard.Create(nil) do
try
Result := ShowModal = mrOk;
if Result then
kmcom.Apply;
SetBaseKeyboard(WindowHandle, FBaseKeyboardID)
finally
Free;
end;
@ -65,9 +80,9 @@ var
v: Integer;
begin
if not TryStrToInt('$'+params.Values['id'], v) then Exit;
kmcom.Options['koBaseLayout'].Value := v;
kmcom.Options.Apply;
FBaseKeyboardID := v;
ModalResult := mrOk;
end;
end.

View file

@ -174,6 +174,7 @@ uses
Hints,
HotkeyUtils,
initprog,
Keyman.Configuration.System.BaseKeyboard,
Keyman.Configuration.System.TIPMaintenance,
Keyman.Configuration.UI.UfrmDiagnosticTests,
KeymanOptionNames,
@ -198,6 +199,7 @@ uses
Keyman.Configuration.UI.UfrmStartInstall,
RegistryKeys,
SupportXMLRenderer,
UfrmBaseKeyboard,
UfrmChangeHotkey,
UfrmHTML,
UfrmInstallKeyboardFromWeb,
@ -664,9 +666,14 @@ end;
------------------------------------------------------------------------------}
procedure TfrmMain.Options_BaseKeyboard; // I4169
var
BaseKeyboardID: Integer;
begin
WaitForElevatedConfiguration(Handle, '-basekeyboard');
// Refresh will be triggered by elevated process
if ConfigureAndSetBaseKeyboard(Handle) then
begin
DoRefresh;
end;
end;
procedure TfrmMain.Options_SettingsManager;

View file

@ -90,6 +90,7 @@ type
fmKeyboardWelcome, // I2569
fmKeyboardPrint, // I2329
fmBaseKeyboard, // I4169
fmMCompileKbds,
fmUpgradeMnemonicLayout, // I4553
fmRepair,
fmKeepInTouch,
@ -114,6 +115,7 @@ uses
GetOsVersion,
help,
HTMLHelpViewer,
Keyman.Configuration.System.BaseKeyboard,
Keyman.Configuration.UI.InstallFile,
Keyman.Configuration.System.TIPMaintenance,
Keyman.Configuration.System.UImportOlderVersionKeyboards11To13,
@ -205,7 +207,7 @@ end;
function Init(var FMode: TKMShellMode; KeyboardFileNames: TStrings; var FSilent, FForce, FNoWelcome: Boolean;
var FLogFile, FQuery: string; var FDisablePackages, FDefaultUILanguage: string; var FStartWithConfiguration: Boolean;
var FParentWindow: THandle; var FDefaultBCP47: string; var FDefaultLangID: Integer): Boolean;
var FParentWindow: THandle; var FDefaultBCP47: string; var FDefaultLangID, FBaseKeyboard: Integer): Boolean;
var
s: string;
i: Integer;
@ -262,6 +264,14 @@ begin
else if s = '-bd' then FMode := fmBackgroundDownload
else if s = '-an' then FMode := fmApplyInstallNow
else if s = '-basekeyboard' then FMode := fmBaseKeyboard // I4169
else if s = '-mcompilekbds' then
begin
// Requires elevated context
FMode := fmMCompileKbds;
Inc(i);
if i > ParamCount then Exit;
FBaseKeyboard := StrToInt('$' + ParamStr(i));
end
else if s = '-nowelcome' then FNoWelcome := True
else if s = '-kw' then FMode := fmKeyboardWelcome // I2569
else if s = '-kp' then FMode := fmKeyboardPrint // I2329
@ -321,7 +331,7 @@ end;
procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FForce, FNoWelcome: Boolean;
FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean; FParentWindow: THandle;
const FDefaultBCP47: string; FDefaultLangID: Integer); forward;
const FDefaultBCP47: string; FDefaultLangID, FBaseKeyboard: Integer); forward;
procedure Run;
var
@ -332,7 +342,7 @@ var
FForce: Boolean;
FParentWindow: THandle;
FLogFile: string;
FDefaultLangID: Integer;
FDefaultLangID, FBaseKeyboard: Integer;
FDefaultBCP47, FDisablePackages, FDefaultUILanguage: string;
FStartWithConfiguration: Boolean;
begin
@ -341,7 +351,7 @@ begin
KeyboardFileNames := TStringList.Create;
try
FParentWindow := 0;
if not Init(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID) then
if not Init(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID, FBaseKeyboard) then
begin
//TODO: TUtilExecute.Shell(PChar('hh.exe mk:@MSITStore:'+ExtractFilePath(KMShellExe)+'keyman.chm::/context/keyman_usage.html'), SW_SHOWNORMAL);
Exit;
@ -349,7 +359,7 @@ begin
if not LoadKMCOM then Exit;
try
RunKMCOM(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID);
RunKMCOM(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID, FBaseKeyboard);
finally
kmcom := nil;
end;
@ -388,7 +398,7 @@ end;
procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FForce, FNoWelcome: Boolean;
FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean;
FParentWindow: THandle; const FDefaultBCP47: string; FDefaultLangID: Integer);
FParentWindow: THandle; const FDefaultBCP47: string; FDefaultLangID, FBaseKeyboard: Integer);
var
kdl: IKeymanDefaultLanguage;
FIcon: string;
@ -540,7 +550,12 @@ begin
end;
fmBaseKeyboard: // I4169
if ConfigureBaseKeyboard
if ConfigureAndSetBaseKeyboard(0)
then ExitCode := 0
else ExitCode := 1;
fmMCompileKbds:
if MCompileBaseKeyboard(FBaseKeyboard)
then ExitCode := 0
else ExitCode := 1;

View file

@ -63,7 +63,8 @@ type
TKeymanKeyboardInstalled = class( // I3581
TKeymanKeyboard,
IIntKeymanKeyboardInstalled,
IKeymanKeyboardInstalled)
IKeymanKeyboardInstalled,
IKeymanKeyboardInstalled2)
private
FRegKeyboard: TRegKeyboard;
FVisualKeyboard: IKeymanVisualKeyboard;
@ -109,9 +110,11 @@ type
{ IIntKeymanKeyboardInstalled }
function RegKeyboard: TRegKeyboard;
procedure ClearVisualKeyboard;
procedure UpdateBaseLayout; // I4169
procedure RefreshInstallation;
{ IKeymanKeyboardInstalled2 }
procedure MCompileForBaseKeyboard(KLID: Integer); safecall;
public
constructor Create(AContext: TKeymanContext; const Name: string);
destructor Destroy; override;
@ -151,17 +154,6 @@ begin
end;
end;
procedure TKeymanKeyboardInstalled.UpdateBaseLayout; // I4169
begin
if FRegKeyboard.MnemonicLayout and FileExists(FRegKeyboard.KeymanFile) then // I4615
with TKPRecompileMnemonicKeyboard.Create(Context) do
try
Execute(FRegKeyboard.KeymanFile, FRegKeyboard.PackageName);
finally
Free;
end;
end;
function TKeymanKeyboardInstalled.Get_Copyright: WideString;
begin
Result := FRegKeyboard.Copyright;
@ -475,5 +467,21 @@ begin
Result := FRegKeyboard;
end;
{ IKeymanKeyboardInstalled2 }
procedure TKeymanKeyboardInstalled.MCompileForBaseKeyboard(KLID: Integer); safecall;
var
RecompileMnemonicKeyboard: TKPRecompileMnemonicKeyboard;
begin
if FRegKeyboard.MnemonicLayout and FileExists(FRegKeyboard.KeymanFile) then
begin
RecompileMnemonicKeyboard := TKPRecompileMnemonicKeyboard.Create(Context);
try
RecompileMnemonicKeyboard.Execute(FRegKeyboard.KeymanFile, FRegKeyboard.PackageName, KLID);
finally
RecompileMnemonicKeyboard.Free;
end;
end;
end;
end.

View file

@ -1,18 +1,18 @@
(*
Name: keymanoptions
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 20 Jun 2006
Modified Date: 6 Feb 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 20 Jun 2006 - mcdurdin - Initial version
01 Aug 2006 - mcdurdin - Add AutoRefershKeyman call
12 Aug 2008 - mcdurdin - Avoid crash with missing options
@ -67,6 +67,7 @@ uses
ErrorControlledRegistry,
RegistryKeys,
Glossary,
isadmin,
Keyman.System.BaseKeyboard,
KeymanOptionNames,
keymanerrorcodes;
@ -111,29 +112,8 @@ begin
end;
procedure TKeymanOptions.Apply;
var
I, FOldBaseLayout: Integer;
begin
with TRegistryErrorControlled.Create do // I3717
try
if OpenKey(SRegKey_KeymanEngine_CU, True) then
begin
if ValueExists(SRegValue_UnderlyingLayout)
then FOldBaseLayout := StrToIntDef('$'+ReadString(SRegValue_UnderlyingLayout),0) // I3759
else FOldBaseLayout := TBaseKeyboard.GetDefaultBaseLayoutID;
end
else
FOldBaseLayout := TBaseKeyboard.GetDefaultBaseLayoutID;
finally
Free;
end;
FInternalOptions.Save(Context);
if FOldBaseLayout <> Get_Items('koBaseLayout').Value then
for I := 0 to Context.Keyboards.Count - 1 do // I4169
(Context.Keyboards.Items[I] as IIntKeymanKeyboardInstalled).UpdateBaseLayout;
Context.Control.AutoApplyKeyman;
end;

View file

@ -12,7 +12,7 @@ unit keymanapi_TLB;
// ************************************************************************ //
// $Rev: 52393 $
// File generated on 16/09/2021 6:54:44 PM from Type Library described below.
// File generated on 7/09/2026 3:54:23 PM from Type Library described below.
// ************************************************************************ //
// Type Lib: C:\Projects\keyman\app\windows\src\engine\kmcomapi\kmcomapi (1)
@ -87,6 +87,7 @@ const
IID_IKeymanKeyboardLanguagesInstalled: TGUID = '{7DC22BC0-85BB-45C0-8EDB-A2F4BD1D500B}';
IID_IKeymanKeyboardLanguagesFile: TGUID = '{5F90BCDA-F1C1-433A-8FD0-B498299D3C30}';
IID_IKeymanKeyboardsInstalled2: TGUID = '{EA57C94F-C140-485E-941A-3F1D5A229024}';
IID_IKeymanKeyboardInstalled2: TGUID = '{3086C85C-932A-4726-BF76-2D74DD133AC9}';
IID_IKeymanPackagesInstalled2: TGUID = '{F23B9848-2AEF-4A2B-BC3A-292E3A00D691}';
IID_IKeymanKeyboardFile2: TGUID = '{EDE4326B-51F4-42D5-8251-B20B71993EC8}';
IID_IKeymanPackageFile2: TGUID = '{9B43B6BC-C622-47EF-915E-6780CF53BAAA}';
@ -248,6 +249,8 @@ type
IKeymanKeyboardLanguagesFileDisp = dispinterface;
IKeymanKeyboardsInstalled2 = interface;
IKeymanKeyboardsInstalled2Disp = dispinterface;
IKeymanKeyboardInstalled2 = interface;
IKeymanKeyboardInstalled2Disp = dispinterface;
IKeymanPackagesInstalled2 = interface;
IKeymanPackagesInstalled2Disp = dispinterface;
IKeymanKeyboardFile2 = interface;
@ -1590,6 +1593,51 @@ type
out References: OleVariant): WideString; dispid 401;
end;
// *********************************************************************//
// Interface: IKeymanKeyboardInstalled2
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {3086C85C-932A-4726-BF76-2D74DD133AC9}
// *********************************************************************//
IKeymanKeyboardInstalled2 = interface(IKeymanKeyboardInstalled)
['{3086C85C-932A-4726-BF76-2D74DD133AC9}']
procedure MCompileForBaseKeyboard(KLID: Integer); safecall;
end;
// *********************************************************************//
// DispIntf: IKeymanKeyboardInstalled2Disp
// Flags: (4416) Dual OleAutomation Dispatchable
// GUID: {3086C85C-932A-4726-BF76-2D74DD133AC9}
// *********************************************************************//
IKeymanKeyboardInstalled2Disp = dispinterface
['{3086C85C-932A-4726-BF76-2D74DD133AC9}']
procedure MCompileForBaseKeyboard(KLID: Integer); dispid 288;
property IconFilename: WideString readonly dispid 257;
procedure InstallVisualKeyboard(const Filename: WideString); dispid 258;
property KeymanID: Integer readonly dispid 259;
property Languages: IKeymanKeyboardLanguagesInstalled readonly dispid 260;
property Loaded: WordBool dispid 261;
property Options: IKeymanKeyboardOptions readonly dispid 262;
property OwnerPackage: IKeymanPackageInstalled readonly dispid 263;
property VisualKeyboard: IKeymanVisualKeyboard readonly dispid 264;
procedure Uninstall; dispid 265;
property Bitmap: IPicture readonly dispid 1;
property Copyright: WideString readonly dispid 2;
property DefaultBCP47Languages: WideString readonly dispid 3;
property DefaultPrimaryLanguage: Integer readonly dispid 4;
property DefaultWindowsLanguages: WideString readonly dispid 5;
property DefaultHotkey: IKeymanHotkey readonly dispid 6;
property Encodings: KeymanKeyboardEncodings readonly dispid 7;
property Filename: WideString readonly dispid 8;
function GetCharsUsed: WideString; dispid 9;
property ID: WideString readonly dispid 10;
property LayoutType: KeymanKeyboardLayoutType readonly dispid 11;
property Message: WideString readonly dispid 12;
property Name: WideString readonly dispid 13;
property Version: WideString readonly dispid 14;
function SerializeXML(Flags: tagKeymanSerializeFlags; const ImagePath: WideString;
out References: OleVariant): WideString; dispid 401;
end;
// *********************************************************************//
// Interface: IKeymanPackagesInstalled2
// Flags: (4416) Dual OleAutomation Dispatchable

View file

@ -6,7 +6,7 @@
// However, when applying changes via the Editor this file will be regenerated
// and comments or formatting changes will be lost.
// ************************************************************************ //
// File generated on 16/09/2021 6:54:45 PM (- $Rev: 12980 $, 32940875).
// File generated on 7/09/2026 4:05:40 PM (- $Rev: 12980 $, 1707828).
[
uuid(F16E2A9A-DA46-4EA3-BFF3-BA46B480C961),
@ -60,6 +60,7 @@ library keymanapi
interface IKeymanKeyboardLanguagesInstalled;
interface IKeymanKeyboardLanguagesFile;
interface IKeymanKeyboardsInstalled2;
interface IKeymanKeyboardInstalled2;
interface IKeymanPackagesInstalled2;
interface IKeymanKeyboardFile2;
interface IKeymanPackageFile2;
@ -936,6 +937,19 @@ library keymanapi
HRESULT _stdcall RefreshInstalledKeyboards(void);
};
[
uuid(3086C85C-932A-4726-BF76-2D74DD133AC9),
version(19.0),
helpstring("https://help.keyman.com/developer/engine/windows/19.0/api/IKeymanKeyboardInstalled2"),
dual,
oleautomation
]
interface IKeymanKeyboardInstalled2: IKeymanKeyboardInstalled
{
[id(0x00000120)]
HRESULT _stdcall MCompileForBaseKeyboard(long KLID);
};
[
uuid(F23B9848-2AEF-4A2B-BC3A-292E3A00D691),
version(14.0),

View file

@ -125,6 +125,7 @@ var
FExitCode: Integer;
FKVKName: WideString;
FCreatedIcon: Boolean;
BaseKeyboardID: Integer;
begin
KL.MethodEnter(Self, 'Execute', [FileName,PackageID,ikPartOfPackage in FInstallOptions ,Force]);
try
@ -248,9 +249,11 @@ begin
// Recompile a mnemonic layout to the user's selected base layout
if ki.MnemonicLayout then // I4169
begin
with Context as TKeymanContext do
BaseKeyboardID := (Options as IKeymanOptions).Items['koBaseLayout'].Value;
with TKPRecompileMnemonicKeyboard.Create(Context) do
try
Execute(FDestFileName, PackageID);
Execute(FDestFileName, PackageID, BaseKeyboardID);
finally
Free;
end;

View file

@ -1,18 +1,18 @@
(*
Name: kprecompilemnemonickeyboard
Copyright: Copyright (C) SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 24 Apr 2014
Modified Date: 13 Mar 2015
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 24 Apr 2014 - mcdurdin - I4174 - V9 - mcompile logs should be stored in diag folder
06 Feb 2015 - mcdurdin - I4552 - V9.0 - Add mnemonic recompile option to ignore deadkeys
13 Mar 2015 - mcdurdin - I4615 - CrashID:kmshell.exe_9.0.481.0_2C6795CE_EOleException
@ -27,7 +27,7 @@ uses
type
TKPRecompileMnemonicKeyboard = class(TKPBase)
procedure Execute(const FileName: string; const PackageName: string);
procedure Execute(const FileName: string; const PackageName: string; BaseKeyboardID: Cardinal);
end;
implementation
@ -42,13 +42,12 @@ uses
Winapi.Windows,
errorcontrolledregistry,
keymancontext,
keymanerrorcodes,
KeymanPaths,
keymanapi_TLB,
RegistryKeys,
utilexecute,
utilkeyman,
utilfiletypes,
utilsystem;
function GetKeyboardLayoutFileName(id: Integer): string;
@ -67,7 +66,7 @@ begin
Result := '';
end;
procedure TKPRecompileMnemonicKeyboard.Execute(const FileName,PackageName: string);
procedure TKPRecompileMnemonicKeyboard.Execute(const FileName,PackageName: string; BaseKeyboardID: Cardinal);
var
FDestPath, FDestFileName: string;
FBaseKeyboardIDHex: string;
@ -76,7 +75,6 @@ var
FExitCode: Integer;
FMCompilePath: string;
FBaseKeyboardFileName: string;
BaseKeyboardID: Cardinal;
FDestDeadkeyFileName: string;
FCommand: string;
begin
@ -84,13 +82,10 @@ begin
then FDestPath := GetPackageInstallPath(PackageName) // I3581
else FDestPath := GetKeyboardInstallPath(FileName); // I3581
with Context as TKeymanContext do
BaseKeyboardID := (Options as IKeymanOptions).Items['koBaseLayout'].Value;
FBaseKeyboardIDHex := IntToHex(BaseKeyboardID, 8);
FBaseFileName := FDestPath + '\' + ExtractFileName(FileName); // I3581
FDestFileName := ChangeFileExt(FBaseFileName, '') + '-'+FBaseKeyboardIDHex + '.kmx';
FDestDeadkeyFileName := ChangeFileExt(FBaseFileName, '') + '-'+FBaseKeyboardIDHex + '-d.kmx'; // I4552
FDestFileName := BuildKeyboardFilenameWithBaseKeyboardID(FBaseFileName, FBaseKeyboardIDHex);
FDestDeadkeyFileName := BuildKeyboardFilenameWithBaseKeyboardIDAndDeadkey(FBaseFileName, FBaseKeyboardIDHex); // I4552
FMCompilePath := TKeymanPaths.KeymanEngineInstallPath(TKeymanPaths.S_MCompileExe);
{ Recompile with the traditional deadkey behaviour }

View file

@ -1,24 +1,24 @@
(*
Name: internalinterfaces
Copyright: Copyright (C) 2003-2017 SIL International.
Documentation:
Description:
Documentation:
Description:
Create Date: 25 Jan 2011
Modified Date: 17 Aug 2014
Authors: mcdurdin
Related Files:
Dependencies:
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
Bugs:
Todo:
Notes:
History: 25 Jan 2011 - mcdurdin - I2569 - Keyboard welcome should always shown from kmshell
01 Jan 2013 - mcdurdin - I3717 - V9.0 - Need ability to select base keyboard in Keyman Configuration
16 Apr 2014 - mcdurdin - I4169 - V9.0 - Mnemonic layouts should be recompiled to positional based on user-selected base keyboard
17 Aug 2014 - mcdurdin - I4376 - V9.0 - Unticked keyboards in configuration should be removed from language profile
17 Aug 2014 - mcdurdin - I4381 - V9.0 - Keyman keyboards should be removed from language bar when Keyman exits
*)
unit internalinterfaces;
@ -41,7 +41,7 @@ type
IIntKeymanInterface = interface
['{D1EBBED5-B9E3-4807-969D-DCF9E1FFB287}']
function XMLClassName: WideString;
function Serialize(Flags: TOleEnum; const ImagePath: WideString; References: TStrings): WideString;
function Serialize(Flags: TOleEnum; const ImagePath: WideString; References: TStrings): WideString;
function DoSerialize(Flags: TOleEnum; const ImagePath: WideString; References: TStrings): WideString; // Wraps serialize with tag
end;
@ -67,7 +67,6 @@ type
['{4876E6DF-C557-46E2-84F4-787BE5F55DDA}']
function RegKeyboard: TRegKeyboard;
procedure ClearVisualKeyboard;
procedure UpdateBaseLayout; // I4169
procedure RefreshInstallation;
end;