unit Keyman.System.Process.KPUninstallKeyboardLanguage;
interface
uses
System.Win.Registry,
Winapi.Windows,
kpbase,
KeymanContext,
Windows8LanguageList,
Winapi.msctf,
keyman_msctf;
type
TKPUninstallKeyboardLanguage = class(TKPBase)
/// Unregisters all TIPs from the Local Machine registry for a given
/// keyboard, including transient profiles
/// The ID of the keyboard
procedure UnregisterTip(const KeyboardID: string); overload;
/// Uninstalls a TIP for the current user for a given keyboard
/// The ID of the keyboard
/// This should be a BCP47 tag that is currently installed
procedure UninstallTip(const KeyboardID, BCP47Tag: string); overload;
/// Uninstalls a TIP for the current user for a given keyboard
/// The ID of the keyboard
/// The transient profile to uninstall, $2000, $2400, $2800, $2C00
procedure UninstallTip(const KeyboardID: string; LangID: Word); overload;
/// Uninstalls all TIPs for the current user for a given keyboard
/// The ID of the keyboard
procedure UninstallTip(const KeyboardID: string); overload;
protected
/// Unregisters a TIP from the Local Machine registry
/// The ID of the keyboard
/// This should be a BCP47 tag
procedure UnregisterTip(const KeyboardID, BCP47Tag: string); overload;
/// Unregisters a transient profile tip from the Local Machine registry
/// The ID of the keyboard
/// The transient profile to unregister, $2000, $2400, $2800 or $2C00
procedure UnregisterTip(const KeyboardID: string; LangID: Word); overload;
private
procedure DoUnregisterTip(const KeyboardID, BCP47Tag: string; pInputProcessorProfileMgr: ITfInputProcessorProfileMgr);
procedure DoUnregisterTipTransientProfile(const KeyboardID: string; LangID: Word; pInputProcessorProfileMgr: ITfInputProcessorProfileMgr);
function GetInputProcessorProfileMgr: ITfInputProcessorProfileMgr;
end;
implementation
uses
System.Classes,
System.SysUtils,
System.Variants,
System.Win.ComObj,
Winapi.ActiveX,
input_installlayoutortip,
isadmin,
Keyman.System.MitigateWin10_1803LanguageInstall,
keymanerrorcodes,
KeymanPaths,
RegistryKeys,
BCP47Tag,
glossary,
TempFileManager,
utilexecute,
utilfiletypes,
utilkeyman,
utilstr,
utiltsf;
{ TKPUninstallKeyboardLanguage }
procedure TKPUninstallKeyboardLanguage.DoUnregisterTip(const KeyboardID,
BCP47Tag: string; pInputProcessorProfileMgr: ITfInputProcessorProfileMgr);
var
reg: TRegistry;
FIsAdmin: Boolean;
guid: TGUID;
LangID: Integer;
RootPath: string;
begin
if not KeyboardInstalled(KeyboardID, FIsAdmin) then
ErrorFmt(KMN_E_ProfileUninstall_KeyboardNotFound, VarArrayOf([KeyboardID])); // I3888
reg := TRegistry.Create;
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_LanguageProfiles;
if not reg.OpenKeyReadOnly(RootPath + '\' + BCP47Tag) then
ErrorFmt(KMN_E_ProfileUninstall_ProfileNotFound, VarArrayOf([KeyboardID]));
if not reg.ValueExists(SRegValue_LanguageProfileLangID) or
not reg.ValueExists(SRegValue_KeymanProfileGUID) then
begin
WarnFmt(KMN_W_ProfileUninstall_RegistryCorrupt, VarArrayOf([KeyboardID]));
Exit;
end;
guid := StringToGuid(reg.ReadString(SRegValue_KeymanProfileGUID));
LangID := reg.ReadInteger(SRegValue_LanguageProfileLangID);
if not reg.DeleteKey('\' + RootPath + '\' + BCP47Tag) then
ErrorFmt(KMN_E_ProfileUninstall_FailedToDeleteProfile, VarArrayOf([KeyboardID, reg.LastError]));
try
OleCheck(pInputProcessorProfileMgr.UnregisterProfile( // I3743
c_clsidKMTipTextService,
LangID,
guid,
0));
except
on E:EOleSysError do
begin
ErrorFmt(KMN_W_TSF_COMError, VarArrayOf(['UnregisterProfile: '+E.Message+' ('+IntToHex(E.ErrorCode,8)+')']));
end;
end;
finally
reg.Free;
end;
Context.Control.AutoApplyKeyman;
end;
procedure TKPUninstallKeyboardLanguage.DoUnregisterTipTransientProfile(
const KeyboardID: string; LangID: Word;
pInputProcessorProfileMgr: ITfInputProcessorProfileMgr);
var
reg: TRegistry;
FIsAdmin: Boolean;
guid: TGUID;
RootPath: string;
begin
if not KeyboardInstalled(KeyboardID, FIsAdmin) then
ErrorFmt(KMN_E_ProfileUninstall_KeyboardNotFound, VarArrayOf([KeyboardID])); // I3888
if not IsTransientLanguageID(LangID) then
ErrorFmt(KMN_E_ProfileUninstall_NotATransientLanguageCode, VarArrayOf([KeyboardID, LangID]));
reg := TRegistry.Create;
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_TransientLanguageProfiles;
if not reg.OpenKey(RootPath + '\' + IntToHex(LangID, 4), False) then
// Assume that the profile has been removed already
Exit;
if not reg.ValueExists(SRegValue_KeymanProfileGUID) then
// Assume that the profile has been removed already
Exit;
guid := StringToGuid(reg.ReadString(SRegValue_KeymanProfileGUID));
if not reg.DeleteKey('\' + RootPath + '\' + IntToHex(LangID, 4)) then
ErrorFmt(KMN_E_ProfileUninstall_FailedToDeleteProfile, VarArrayOf([KeyboardID, reg.LastError]));
try
OleCheck(pInputProcessorProfileMgr.UnregisterProfile( // I3743
c_clsidKMTipTextService,
LangID,
guid,
0));
except
on E:EOleSysError do
begin
WarnFmt(KMN_W_TSF_COMError, VarArrayOf(['UnregisterProfile: '+E.Message+' ('+IntToHex(E.ErrorCode,8)+')']));
end;
end;
finally
reg.Free;
end;
Context.Control.AutoApplyKeyman;
end;
function TKPUninstallKeyboardLanguage.GetInputProcessorProfileMgr: ITfInputProcessorProfileMgr;
var
pInputProcessorProfiles: ITfInputProcessorProfiles;
begin
pInputProcessorProfiles := nil;
OleCheck(CoCreateInstance(CLASS_TF_InputProcessorProfiles, nil, CLSCTX_INPROC_SERVER,
IID_ITfInputProcessorProfiles, pInputProcessorProfiles));
if not Supports(pInputProcessorProfiles, IID_ITfInputProcessorProfileMgr, Result) then // I3743
raise Exception.Create('Missing interface IID_ITfInputProcessorProfileMgr');
end;
procedure TKPUninstallKeyboardLanguage.UnregisterTip(const KeyboardID, BCP47Tag: string);
begin
DoUnregisterTip(KeyboardID, BCP47Tag, GetInputProcessorProfileMgr);
end;
procedure TKPUninstallKeyboardLanguage.UninstallTip(const KeyboardID, BCP47Tag: string);
var
reg: TRegistry;
FIsAdmin: Boolean;
langid: Integer;
guid: TGUID;
FLayoutInstallString: string;
RootPath: string;
begin
if not KeyboardInstalled(KeyboardID, FIsAdmin) then
ErrorFmt(KMN_E_ProfileInstall_KeyboardNotFound, VarArrayOf([KeyboardID])); // I3888
reg := TRegistry.Create(KEY_READ);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_LanguageProfiles;
if not reg.OpenKeyReadOnly(RootPath + '\' + BCP47Tag) then
ErrorFmt(KMN_E_ProfileUninstall_ProfileNotFound, VarArrayOf([KeyboardID]));
if not reg.ValueExists(SRegValue_KeymanProfileGUID) or
not reg.ValueExists(SRegValue_LanguageProfileLangID) then
begin
WarnFmt(KMN_W_ProfileUninstall_RegistryCorrupt, VarArrayOf([KeyboardID]));
Exit;
end;
guid := StringToGuid(reg.ReadString(SRegValue_KeymanProfileGUID));
langid := reg.ReadInteger(SRegValue_LanguageProfileLangID);
finally
reg.Free;
end;
if not IsTipInstalledForCurrentUser(BCP47Tag, langid, guid) then
Exit;
//
// Uninstall the TIP from Windows current user
//
FLayoutInstallString := GetLayoutInstallString(LangID, guid);
if not InstallLayoutOrTip(PChar(FLayoutInstallString), ILOT_UNINSTALL) then // I4302
WarnFmt(KMN_W_ProfileUninstall_InstallLayoutOrTipFailed, VarArrayOf([KeyboardID]));
end;
procedure TKPUninstallKeyboardLanguage.UninstallTip(const KeyboardID: string;
LangID: Word);
var
reg: TRegistry;
FIsAdmin: Boolean;
guid: TGUID;
FLayoutInstallString: string;
RootPath: string;
begin
if not KeyboardInstalled(KeyboardID, FIsAdmin) then
ErrorFmt(KMN_E_ProfileUninstall_KeyboardNotFound, VarArrayOf([KeyboardID]));
if not IsTransientLanguageID(LangID) then
ErrorFmt(KMN_E_ProfileUninstall_NotATransientLanguageCode, VarArrayOf([KeyboardID, LangID]));
reg := TRegistry.Create(KEY_READ);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_TransientLanguageProfiles;
if not reg.OpenKeyReadOnly(RootPath + '\' + IntToHex(LangID, 4)) then
// We'll assume it's not installed
Exit;
if not reg.ValueExists(SRegValue_KeymanProfileGUID) then
begin
WarnFmt(KMN_W_ProfileUninstall_RegistryCorrupt, VarArrayOf([KeyboardID]));
Exit;
end;
guid := StringToGuid(reg.ReadString(SRegValue_KeymanProfileGUID));
finally
reg.Free;
end;
//
// We need to check if the TIP is installed before attempting to remove it,
// because otherwise Windows will attempt to 'tidy up' and may leave a stray
// keyboard layout installed.
//
// This scenario can arise if the user has removed the TIP through Windows
// UI/APIs after we install it.
//
if not IsTipInstalledForCurrentUser('', langid, guid) then
Exit;
//
// Uninstall the TIP from Windows current user
//
FLayoutInstallString := GetLayoutInstallString(LangID, guid);
if not InstallLayoutOrTip(PChar(FLayoutInstallString), ILOT_UNINSTALL) then // I4302
WarnFmt(KMN_W_ProfileUninstall_InstallLayoutOrTipFailed, VarArrayOf([KeyboardID]));
end;
procedure TKPUninstallKeyboardLanguage.UninstallTip(const KeyboardID: string);
var
sLocales: TStrings;
sLocale: string;
FInstByAdmin: Boolean;
reg: TRegistry;
RootPath: string;
begin
if not KeyboardInstalled(KeyboardID, FInstByAdmin) then
ErrorFmt(KMN_E_ProfileUninstall_KeyboardNotFound, VarArrayOf([KeyboardID])); // I3888
// We need to iterate through the Windows language profiles?
sLocales := TStringList.Create;
try
reg := TRegistry.Create(KEY_READ);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_LanguageProfiles;
if reg.OpenKeyReadOnly(RootPath) then
begin
reg.GetKeyNames(sLocales);
end;
finally
reg.Free;
end;
for sLocale in sLocales do
// Note: if TIP is not installed, this still succeeds
UninstallTip(KeyboardID, sLocale);
finally
sLocales.Free;
end;
Context.Control.AutoApplyKeyman;
end;
procedure TKPUninstallKeyboardLanguage.UnregisterTip(const KeyboardID: string);
var
sLocales: TStrings;
sLocale: string;
FInstByAdmin: Boolean;
RootPath: string;
pInputProcessorProfileMgr: ITfInputProcessorProfileMgr;
reg: TRegistry;
begin
if not KeyboardInstalled(KeyboardID, FInstByAdmin) then
ErrorFmt(KMN_E_ProfileUninstall_KeyboardNotFound, VarArrayOf([KeyboardID])); // I3888
if FInstByAdmin and not IsAdministrator then // I3723
ErrorFmt(KMN_E_ProfileUninstall_MustBeAllUsers, VarArrayOf([KeyboardID])); // I3764
pInputProcessorProfileMgr := GetInputProcessorProfileMgr;
sLocales := TStringList.Create;
try
reg := TRegistry.Create(KEY_READ);
try
reg.RootKey := HKEY_LOCAL_MACHINE;
RootPath := GetRegistryKeyboardInstallKey_LM(KeyboardID) + SRegSubKey_LanguageProfiles;
if reg.OpenKeyReadOnly(RootPath) then
reg.GetKeyNames(sLocales);
finally
reg.Free;
end;
for sLocale in sLocales do
DoUnregisterTip(KeyboardID, sLocale, pInputProcessorProfileMgr);
finally
sLocales.Free;
end;
DoUnregisterTipTransientProfile(KeyboardID, $2000, pInputProcessorProfileMgr);
DoUnregisterTipTransientProfile(KeyboardID, $2400, pInputProcessorProfileMgr);
DoUnregisterTipTransientProfile(KeyboardID, $2800, pInputProcessorProfileMgr);
DoUnregisterTipTransientProfile(KeyboardID, $2C00, pInputProcessorProfileMgr);
Context.Control.AutoApplyKeyman;
end;
procedure TKPUninstallKeyboardLanguage.UnregisterTip(const KeyboardID: string;
LangID: Word);
begin
DoUnregisterTipTransientProfile(KeyboardID, LangID, GetInputProcessorProfileMgr);
end;
end.