【问题原因】
该问题是由于微软从 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;