【问题原因】
该问题是由于微软从 Windows 8 开始,GetKeyboadLayouts 函数不再有效,而 VCL 中仍然是通过该方法获取输入法列表造成的。希望下个版本的 Delphi/C++ Builder 能够解决。
【解决办法】
如果是 Win8+,则自己从注册表读,如果是Win 7 等以前的版本,则直接取 Screen.Imes。这个函数做了一个简单的封装。
procedure EnumImeNames(AList: TStrings);
var
AReg: TRegistry;
AKeyList: TStringList;
I: Integer;
ALayout: Cardinal;
function NameByLayout: String;
const
KbLayoutRegkeyFmt =
‘System\CurrentControlSet\Control\Keyboard Layouts\%.8x‘;
KbLayoutRegSubkey = ‘layout text‘;
var
AIMEReg: TRegistry;
begin
SetLength(Result, 0);
AIMEReg := TRegistry.Create;
try
AIMEReg.RootKey := HKEY_LOCAL_MACHINE;
if AIMEReg.OpenKeyReadOnly(Format(KbLayoutRegkeyFmt, [ALayout])) then
begin
if AIMEReg.ValueExists(KbLayoutRegSubkey) then
Result := AIMEReg.ReadString(KbLayoutRegSubkey)
end;
finally
FreeAndNil(AIMEReg);
end;
end;
function NameByClsId(AClsId: String): String;
var
AIMEReg: TRegistry;
begin
SetLength(Result, 0);
AIMEReg := TRegistry.Create;
try
AIMEReg.RootKey := HKEY_CLASSES_ROOT;
if AIMEReg.OpenKeyReadOnly(‘CLSID\‘ + AClsId) then
Result := AIMEReg.ReadString(‘‘);
finally
FreeAndNil(AIMEReg);
end;
end;
const
CnImeRoot =
‘Software\Microsoft\CTF\SortOrder\AssemblyItem\0x00000804\{34745C63-B2F0-4784-8B67-5E12C8701A31}‘;
begin
if CheckWin32Version(6,2) then
begin
AKeyList := TStringList.Create;
AReg := TRegistry.Create;
AList.BeginUpdate;
try
AReg.RootKey := HKEY_CURRENT_USER;
if AReg.OpenKeyReadOnly(CnImeRoot) then
begin
AReg.GetKeyNames(AKeyList);
AReg.CloseKey;
for I := 0 to AKeyList.Count - 1 do
begin
if AReg.OpenKeyReadOnly(CnImeRoot+‘\‘+AKeyList[I]) then
begin
if AReg.ValueExists(‘KeyboardLayout‘) then
ALayout := AReg.ReadInteger(‘KeyboardLayout‘)
else
ALayout := 0;
if ALayout > 0 then
AList.Add(NameByLayout)
else if AReg.ValueExists(‘CLSID‘) then
AList.Add(NameByClsId(AReg.ReadString(‘CLSID‘)));
AReg.CloseKey;
end;
end;
end;
finally
FreeAndNil(AReg);
FreeAndNil(AKeyList);
AList.EndUpdate;
end;
end
else
AList.Assign(Screen.Imes);
end;
黑夜杀手提供的一个设置默认输入法的函数,提供给需要的朋友:
procedure SetDefaultCNIME(AForm: TForm; IMEKeyStr: string = ‘五笔‘);
var
I, idx: Integer;
IMESList: TStringList;
begin
IMESList := TStringList.Create;
EnumImeNames(IMESList);
try
if IMESList.Count > 0 then
begin
{ 开始初始化默认中文输入法 }
idx := -1;
for I := 0 to IMESList.Count - 1 do
begin
if IMESList[I].Contains(IMEKeyStr) then
begin
idx := I;
Break;
end;
end;
if idx = -1 then
begin
for I := 0 to IMESList.Count - 1 do
begin
if IMESList[I].Contains(‘拼音‘) then
// 如果找不到指定的输入法,则设置为拼音
begin
idx := I;
Break;
end;
end;
end;
if idx = -1 then
begin
if IMESList.Count > 0 then
begin
idx := 0;
end
else
begin
exit;
end;
end;
for I := 0 to AForm.ComponentCount - 1 do
begin
if AForm.Components[I] is TMemo then
TMemo(AForm.Components[I]).ImeName := IMESList[idx];
if AForm.Components[I] is TEdit then
TEdit(AForm.Components[I]).ImeName := IMESList[idx];
if AForm.Components[I] is TLabeledEdit then
TEdit(AForm.Components[I]).ImeName := IMESList[idx];
end;
{ 结束初始化默认中文输入法 }
end;
finally
IMESList.Free;
end;
end;
【注意】
在执行上述操作之前,请确认已经修改注册表,允许每个应用独立控制输入法,黑夜杀手提供的参考代码如下:
procedure SetDiffInputMethodInSeperateApp;
//http://superuser.com/questions/839993/find-registry-key-for-windows-8-per-application-input-method-setting
var
AReg: TRegistry;
MyValue: array [0 .. 7] of Byte;
// =[$9e,$1e,$07,$80,$92,$00,$00,$00]; on
// =[$9e,$1e,$07,$80,$12,$00,$00,$00]; off
const
ImeMethodSetRoot =
‘Control Panel\Desktop‘;
begin
if CheckWin32Version(6, 2) then
begin
AReg := TRegistry.Create;
try
AReg.RootKey := HKEY_CURRENT_USER;
if AReg.OpenKey(ImeMethodSetRoot, False) then
begin
if AReg.ValueExists(‘UserPreferencesMask‘) then
begin
AReg.ReadBinaryData(‘UserPreferencesMask‘, MyValue, 8);
MyValue[4] := $92;
AReg.WriteBinaryData(‘UserPreferencesMask‘, MyValue, 8);
end;
AReg.CloseKey;
end;
finally
FreeAndNil(AReg);
end;
end;
end;