diff --git a/windows/src/desktop/kmshell/install/Keyman.Configuration.System.TIPMaintenance.pas b/windows/src/desktop/kmshell/install/Keyman.Configuration.System.TIPMaintenance.pas
index fb5f912dc5..1392d13353 100644
--- a/windows/src/desktop/kmshell/install/Keyman.Configuration.System.TIPMaintenance.pas
+++ b/windows/src/desktop/kmshell/install/Keyman.Configuration.System.TIPMaintenance.pas
@@ -33,7 +33,11 @@ type
class function GetFirstLanguage(Keyboard: IKeymanKeyboardFile): string; overload;
/// Get the BCP47 tag for the user's default language
- class function GetUserDefaultLanguage: string; static;
+ class function GetUserDefaultLanguage: string; overload; static;
+ class procedure GetUserDefaultLanguage(var BCP47: string; var LangID: Integer); overload; static;
+
+ /// Get the -default-lang parameter string for kmshell
+ class function GetUserDefaultLangParameterString: string; static;
private
class function GetKeyboardLanguage(const KeyboardID,
BCP47Tag: string): IKeymanKeyboardLanguageInstalled; static;
@@ -152,7 +156,7 @@ begin
if RegistrationRequired then
begin
// This calls back into TTIPMaintenance.RegisterTip
- if WaitForElevatedConfiguration(0, '-register-tip '+IntToHex(LangID,4)+' "'+KeyboardID+'" "'+lang.BCP47Code+'"') <> 0 then
+ if WaitForElevatedConfiguration(0, '-register-tip '+IntToHex(LangID,4)+' "'+KeyboardID+'" "'+lang.BCP47Code+'" '+GetUserDefaultLangParameterString) <> 0 then
Exit(False);
end;
@@ -204,16 +208,61 @@ begin
Result := False;
end;
+class function TTIPMaintenance.GetUserDefaultLangParameterString: string;
+var
+ LangID: Integer;
+ BCP47: string;
+begin
+ GetUserDefaultLanguage(BCP47, LangID);
+ Result := '-default-lang '+BCP47+' '+IntToHex(LangID,4);
+end;
+
class function TTIPMaintenance.GetUserDefaultLanguage: string;
+var
+ LangID: Integer;
+begin
+ GetUserDefaultLanguage(Result, LangID);
+end;
+
+class procedure TTIPMaintenance.GetUserDefaultLanguage(var BCP47: string; var LangID: Integer);
var
r: TRegistry;
tags: TStringList;
v: string;
keys: TStringList;
key: string;
+
+ function GetLangIDFromValueName: Integer;
+ var
+ values: TStringList;
+ v: string;
+ begin
+ // In HKCU\Control Panel\International\User Profile\, already opened
+ // by the caller, look for a value name such as '0453:00000453' or
+ // '0804:{81D4E9C9-1D3B-41BC-9E6C-4B40BF79E35E}{FA550B04-5AD7-411F-A5AC-CA038EC515D7}'
+ // and grab the LangID from there.
+ values := TStringList.Create;
+ try
+ r.GetValueNames(values);
+ for v in values do
+ begin
+ if Copy(v, 5, 1) = ':' then
+ begin
+ Result := StrToIntDef('$' + Copy(v, 1, 4), 0);
+ if Result > 0 then
+ Exit;
+ end;
+ end;
+ finally
+ values.Free;
+ end;
+ Result := 0;
+ end;
+
begin
// Fallback result
- Result := TLanguageCodeUtils.TranslateWindowsLanguagesToBCP47(HKLToLanguageID(GetDefaultHKL));
+ LangID := HKLToLanguageID(GetDefaultHKL);
+ BCP47 := TLanguageCodeUtils.TranslateWindowsLanguagesToBCP47(LangID);
// For Win10, look in CPL/International/UserProfile
r := TRegistry.Create;
@@ -232,7 +281,8 @@ begin
if r.OpenKeyReadOnly('\' + SRegKey_ControlPanelInternationalUserProfile + '\' + key) and
r.ValueExists(v) then
begin
- Result := key;
+ BCP47 := key;
+ LangID := GetLangIDFromValueName;
Break;
end;
end;
@@ -248,7 +298,9 @@ begin
r.ReadMultiString(SRegValue_CPIUP_Languages, tags);
if tags.Count > 0 then
begin
- Result := tags[0].Trim;
+ BCP47 := tags[0].Trim;
+ if r.OpenKeyReadOnly('\' + SRegKey_ControlPanelInternationalUserProfile + '\' + tags[0].Trim) then
+ LangID := GetLangIDFromValueName;
end;
finally
tags.Free;
diff --git a/windows/src/desktop/kmshell/install/Keyman.Configuration.UI.InstallFile.pas b/windows/src/desktop/kmshell/install/Keyman.Configuration.UI.InstallFile.pas
index 00bf0e44a7..2636eba7ea 100644
--- a/windows/src/desktop/kmshell/install/Keyman.Configuration.UI.InstallFile.pas
+++ b/windows/src/desktop/kmshell/install/Keyman.Configuration.UI.InstallFile.pas
@@ -169,12 +169,12 @@ begin
if Length(FilenameBCP47) > 1
then RegisterKeyboardPackageLanguage(FPackage, FilenameBCP47[1])
else RegisterKeyboardPackageLanguage(FPackage, '');
- // The keyboard will be installed for current user as a separate step
+ // The keyboard will be installed for current user as a separate step
end
else
begin
FKeyboard := (kmcom.Keyboards as IKeymanKeyboardsInstalled2).Install2(FileName, True);
- if Length(FilenameBCP47) > 1
+ if (Length(FilenameBCP47) > 1) and (Trim(FilenameBCP47[1]) <> '')
then BCP47Tag := FilenameBCP47[1]
else BCP47Tag := TTIPMaintenance.GetFirstLanguage(FKeyboard);
TTIPMaintenance.DoRegister(FKeyboard.ID, BCP47Tag);
diff --git a/windows/src/desktop/kmshell/install/UfrmInstallKeyboard.pas b/windows/src/desktop/kmshell/install/UfrmInstallKeyboard.pas
index 6250db1030..8e780442e6 100644
--- a/windows/src/desktop/kmshell/install/UfrmInstallKeyboard.pas
+++ b/windows/src/desktop/kmshell/install/UfrmInstallKeyboard.pas
@@ -303,7 +303,8 @@ begin
Manager.UpdateProgress('Installing Keyboard', 0, 0);
t := TTempFileManager.Get('.log');
try
- if WaitForElevatedConfiguration(GetForegroundWindow, '-log "'+t.Name+'" -s -i "'+FInstallFile+'='+BCP47Tag+'" -nowelcome') = 0 then
+ if WaitForElevatedConfiguration(GetForegroundWindow, '-log "'+t.Name+'" -s -i "'+FInstallFile+'='+BCP47Tag+'"'+
+ ' -nowelcome '+TTIPMaintenance.GetUserDefaultLangParameterString) = 0 then
begin
// install the keyboard tip
if not InstallTipForKeyboard(BCP47Tag) then
diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas
index d77ca982f0..c22591e79c 100644
--- a/windows/src/desktop/kmshell/main/initprog.pas
+++ b/windows/src/desktop/kmshell/main/initprog.pas
@@ -195,7 +195,8 @@ begin
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): Boolean;
+ var FLogFile, FQuery: string; var FDisablePackages, FDefaultUILanguage: string; var FStartWithConfiguration: Boolean;
+ var FParentWindow: THandle; var FDefaultBCP47: string; var FDefaultLangID: Integer): Boolean;
var
s: string;
i: Integer;
@@ -269,7 +270,13 @@ begin
FQuery := Trim(FQuery);
end
else if Copy(s, 1, 3) = '-ur' then UnRegCRC := StrToIntDef('$'+Copy(s, 4, 8), 0)
-
+ else if s = '-default-lang' then
+ begin
+ Inc(i);
+ FDefaultBCP47 := ParamStr(i);
+ Inc(i);
+ FDefaultLangID := StrToIntDef('$'+ParamStr(i), 0);
+ end
// Controls from Keyman Engine
else if s = '-showhint' then FMode := fmShowHint
else if s = '-parentwindow' then
@@ -301,7 +308,8 @@ begin
end;
procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FForce, FNoWelcome: Boolean;
- FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean; FParentWindow: THandle); forward;
+ FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean; FParentWindow: THandle;
+ const FDefaultBCP47: string; FDefaultLangID: Integer); forward;
procedure Run;
var
@@ -312,7 +320,8 @@ var
FForce: Boolean;
FParentWindow: THandle;
FLogFile: string;
- FDisablePackages, FDefaultUILanguage: string;
+ FDefaultLangID: Integer;
+ FDefaultBCP47, FDisablePackages, FDefaultUILanguage: string;
FStartWithConfiguration: Boolean;
begin
RegisterControlClasses;
@@ -320,7 +329,7 @@ begin
KeyboardFileNames := TStringList.Create;
try
FParentWindow := 0;
- if not Init(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow) then
+ if not Init(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID) then
begin
//TODO: TUtilExecute.Shell(PChar('hh.exe mk:@MSITStore:'+ExtractFilePath(KMShellExe)+'keyman.chm::/context/keyman_usage.html'), SW_SHOWNORMAL);
Exit;
@@ -328,7 +337,7 @@ begin
if not LoadKMCOM then Exit;
try
- RunKMCOM(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow);
+ RunKMCOM(FMode, KeyboardFileNames, FSilent, FForce, FNoWelcome, FLogFile, FQuery, FDisablePackages, FDefaultUILanguage, FStartWithConfiguration, FParentWindow, FDefaultBCP47, FDefaultLangID);
finally
kmcom := nil;
end;
@@ -366,8 +375,10 @@ begin
end;
procedure RunKMCOM(FMode: TKMShellMode; KeyboardFileNames: TStrings; FSilent, FForce, FNoWelcome: Boolean;
- FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean; FParentWindow: THandle);
+ FLogFile, FQuery: string; FDisablePackages, FDefaultUILanguage: string; FStartWithConfiguration: Boolean;
+ FParentWindow: THandle; const FDefaultBCP47: string; FDefaultLangID: Integer);
var
+ kdl: IKeymanDefaultLanguage;
FIcon: string;
FMutex: TKeymanMutex; // I2720
function FirstKeyboardFileName: WideString;
@@ -440,6 +451,12 @@ begin
if FileExists(FIcon) then
Application.Icon.LoadFromFile(FIcon);
+ if (FDefaultBCP47 <> '') or (FDefaultLangID <> 0) then
+ begin
+ if Supports(kmcom, IKeymanDefaultLanguage, kdl) then
+ kdl.SetDefaultLanguage(FDefaultBCP47, FDefaultLangID);
+ end;
+
case FMode of
fmKeyboardWelcome: // I2569
ShowKeyboardWelcome(FirstKeyboardFileName);
diff --git a/windows/src/engine/kmcomapi/com/keyman_implementation.pas b/windows/src/engine/kmcomapi/com/keyman_implementation.pas
index 74766bad76..fe0c117fa5 100644
--- a/windows/src/engine/kmcomapi/com/keyman_implementation.pas
+++ b/windows/src/engine/kmcomapi/com/keyman_implementation.pas
@@ -48,7 +48,7 @@ uses
keymansysteminfo;
type
- TKeyman = class(TAutoObject, IKeyman, IIntKeyman, IKeymanBCP47Canonicalization)
+ TKeyman = class(TAutoObject, IKeyman, IIntKeyman, IKeymanBCP47Canonicalization, IKeymanDefaultLanguage)
private
FInitialized: Boolean;
FContext: TKeymanContext;
@@ -84,6 +84,8 @@ type
// Reimplement as a special case for this interface
function SerializeXML(Flags: TOleEnum; const ImagePath: WideString; out References: OleVariant): WideString; safecall;
+ { IKeymanDefaultLanguage }
+ procedure SetDefaultLanguage(const BCP47: WideString; LangID: Integer); safecall;
public
procedure Initialize; override;
destructor Destroy; override;
@@ -290,6 +292,12 @@ begin
end;
end;
+procedure TKeyman.SetDefaultLanguage(const BCP47: WideString; LangID: Integer);
+begin
+ FContext.DefaultBCP47 := BCP47;
+ FContext.DefaultLangID := LangID;
+end;
+
procedure TKeyman.Set_AutoApply(Value: WordBool);
begin
if not FInitialized then raise Exception.Create(SErrorUninitialised);
diff --git a/windows/src/engine/kmcomapi/keymanapi_TLB.pas b/windows/src/engine/kmcomapi/keymanapi_TLB.pas
index 56ec2f90e7..3ceb5b15cd 100644
--- a/windows/src/engine/kmcomapi/keymanapi_TLB.pas
+++ b/windows/src/engine/kmcomapi/keymanapi_TLB.pas
@@ -12,7 +12,7 @@ unit keymanapi_TLB;
// ************************************************************************ //
// $Rev: 52393 $
-// File generated on 1/02/2021 8:52:41 AM from Type Library described below.
+// File generated on 16/09/2021 6:54:44 PM from Type Library described below.
// ************************************************************************ //
// Type Lib: C:\Projects\keyman\app\windows\src\engine\kmcomapi\kmcomapi (1)
@@ -93,6 +93,7 @@ const
IID_IKeymanKeyboardLanguageInstalled2: TGUID = '{414C26E6-BFAC-4A70-9EA1-E525BA9BBA7E}';
IID_IKeymanKeyboardLanguagesInstalled2: TGUID = '{628FF2E6-B490-462E-8FC7-7AE53B9D392C}';
CLASS_Keyman: TGUID = '{CF46549D-4D2D-4679-A2E1-23A815F172F8}';
+ IID_IKeymanDefaultLanguage: TGUID = '{77BAB934-B7DF-4304-AFA6-B8F6BEC16516}';
// *********************************************************************//
// Declaration of Enumerations defined in Type Library
@@ -257,6 +258,8 @@ type
IKeymanKeyboardLanguageInstalled2Disp = dispinterface;
IKeymanKeyboardLanguagesInstalled2 = interface;
IKeymanKeyboardLanguagesInstalled2Disp = dispinterface;
+ IKeymanDefaultLanguage = interface;
+ IKeymanDefaultLanguageDisp = dispinterface;
// *********************************************************************//
// Declaration of CoClasses defined in Type Library
@@ -1765,6 +1768,26 @@ type
out References: OleVariant): WideString; dispid 401;
end;
+// *********************************************************************//
+// Interface: IKeymanDefaultLanguage
+// Flags: (4416) Dual OleAutomation Dispatchable
+// GUID: {77BAB934-B7DF-4304-AFA6-B8F6BEC16516}
+// *********************************************************************//
+ IKeymanDefaultLanguage = interface(IDispatch)
+ ['{77BAB934-B7DF-4304-AFA6-B8F6BEC16516}']
+ procedure SetDefaultLanguage(const DefaultBCP47: WideString; DefaultLangID: Integer); safecall;
+ end;
+
+// *********************************************************************//
+// DispIntf: IKeymanDefaultLanguageDisp
+// Flags: (4416) Dual OleAutomation Dispatchable
+// GUID: {77BAB934-B7DF-4304-AFA6-B8F6BEC16516}
+// *********************************************************************//
+ IKeymanDefaultLanguageDisp = dispinterface
+ ['{77BAB934-B7DF-4304-AFA6-B8F6BEC16516}']
+ procedure SetDefaultLanguage(const DefaultBCP47: WideString; DefaultLangID: Integer); dispid 201;
+ end;
+
// *********************************************************************//
// The Class CoKeyman provides a Create and CreateRemote method to
// create instances of the default interface IKeyman exposed by
diff --git a/windows/src/engine/kmcomapi/keymancontext.pas b/windows/src/engine/kmcomapi/keymancontext.pas
index 085810eb2d..fdd3e76567 100644
--- a/windows/src/engine/kmcomapi/keymancontext.pas
+++ b/windows/src/engine/kmcomapi/keymancontext.pas
@@ -32,6 +32,8 @@ type
private
FKeyman: TObject;
FController: TKeymanController;
+ FDefaultBCP47: string;
+ FDefaultLangID: Integer;
function GetErrors: IIntKeymanErrors;
function GetKeyboards: IIntKeymanKeyboardsInstalled;
function GetPackages: IIntKeymanPackagesInstalled;
@@ -50,6 +52,9 @@ type
property SystemInfo: IIntKeymanSystemInfo read GetSystemInfo;
property Control: IIntKeymanControl read GetControl;
property Controller: TKeymanController read FController;
+
+ property DefaultBCP47: string read FDefaultBCP47 write FDefaultBCP47;
+ property DefaultLangID: Integer read FDefaultLangID write FDefaultLangID;
end;
implementation
diff --git a/windows/src/engine/kmcomapi/kmcomapi.dproj b/windows/src/engine/kmcomapi/kmcomapi.dproj
index cf51be0329..fd9b6c51d7 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.dproj
+++ b/windows/src/engine/kmcomapi/kmcomapi.dproj
@@ -124,7 +124,7 @@
false
false
- ..\..\desktop\kmshell\kmshell.exe
+ ..\..\desktop\kmshell\bin\win32\debug\kmshell.exe
CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)
-c
false
diff --git a/windows/src/engine/kmcomapi/kmcomapi.ridl b/windows/src/engine/kmcomapi/kmcomapi.ridl
index bdee680f06..ac20310a87 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.ridl
+++ b/windows/src/engine/kmcomapi/kmcomapi.ridl
@@ -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 1/02/2021 8:52:42 AM (- $Rev: 12980 $, 9519671).
+// File generated on 16/09/2021 6:54:45 PM (- $Rev: 12980 $, 32940875).
[
uuid(F16E2A9A-DA46-4EA3-BFF3-BA46B480C961),
@@ -66,6 +66,7 @@ library keymanapi
interface IKeymanKeyboardLanguageInstalled2;
interface IKeymanKeyboardLanguagesInstalled2;
interface IKeymanBCP47Canonicalization;
+ interface IKeymanDefaultLanguage;
[
@@ -1019,6 +1020,17 @@ library keymanapi
HRESULT _stdcall GetFullTagList([in] BSTR Tag, [out, retval] VARIANT* Result);
};
+ [
+ uuid(77BAB934-B7DF-4304-AFA6-B8F6BEC16516),
+ dual,
+ oleautomation
+ ]
+ interface IKeymanDefaultLanguage: IDispatch
+ {
+ [id(0x000000C9)]
+ HRESULT _stdcall SetDefaultLanguage([in] BSTR DefaultBCP47, [in] long DefaultLangID);
+ };
+
[
uuid(CF46549D-4D2D-4679-A2E1-23A815F172F8),
version(10.0)
diff --git a/windows/src/engine/kmcomapi/processes/keyboard/kpinstallkeyboard.pas b/windows/src/engine/kmcomapi/processes/keyboard/kpinstallkeyboard.pas
index e91cab0eeb..147bb9aef6 100644
--- a/windows/src/engine/kmcomapi/processes/keyboard/kpinstallkeyboard.pas
+++ b/windows/src/engine/kmcomapi/processes/keyboard/kpinstallkeyboard.pas
@@ -326,6 +326,7 @@ var
kpil: TKPInstallKeyboardLanguage;
ml: TMitigateWin10_1803.TMitigatedLanguage;
KeyboardName: string;
+ FDefaultBCP47: string;
type
TWSLCallback = reference to procedure(r: TRegistryErrorControlled);
@@ -366,7 +367,11 @@ begin
ErrorFmt(KMN_E_Install_InvalidFile, VarArrayOf([ExtractFileName(FileName), E.Message]));
end;
- FDefaultHKL := GetDefaultHKL;
+ FDefaultHKL := Context.DefaultLangID;
+ if FDefaultHKL = 0 then
+ FDefaultHKL := GetDefaultHKL;
+
+ FDefaultBCP47 := Context.DefaultBCP47;
KeyboardID := GetShortKeyboardName(FileName);
@@ -456,12 +461,6 @@ begin
begin
FLanguages := GetLanguageCodesFromKeyboard(ki);
- //
- // Final fallback is to install against default language for system // I4607
- //
- if Length(FLanguages) = 0 then
- AddLanguage(HKLToLanguageID(FDefaultHKL));
-
for i := 0 to High(Flanguages) do
if TMitigateWin10_1803.IsMitigationRequired(FLanguages[i], ml) then
begin
@@ -483,6 +482,19 @@ begin
i: Integer;
BCP47Tag: string;
begin
+ if Length(FLanguages) = 0 then
+ begin
+ //
+ // Final fallback is to install against default language for system
+ //
+ kpil := TKPInstallKeyboardLanguage.Create(Context);
+ try
+ kpil.RegisterTip(KeyboardID, FDefaultBCP47, KeyboardName, FDefaultHKL, FIconFileName, ''); //TODO: language name
+ finally
+ kpil.Free;
+ end;
+ r.WriteString(FDefaultBCP47, ''); // TODO: language name
+ end;
for i := 0 to High(FLanguages) do
begin
BCP47Tag := TLanguageCodeUtils.TranslateWindowsLanguagesToBCP47(FLanguages[i]);