Delphi 許多控件內建 IMEMode、IMEName 等屬性,輕鬆控制輸入法行為。但 Visual C++ (MFC) 並未提供對應的封裝類別。本文示範如何透過登錄檔枚舉已安裝輸入法,再呼叫 Windows API LoadKeyboardLayout 與 ActivateKeyboardLayout 實現動態切換,並提供完整 MFC 對話框範例程式碼。
| API | 用途 |
|---|---|
RegOpenKey / RegQueryValueEx / RegCloseKey | 登錄檔讀取:枚舉已安裝/已註冊的輸入法 |
LoadKeyboardLayout | 載入指定 KLID(輸入法識別碼),回傳 HKL 輸入法句柄 |
ActivateKeyboardLayout | 啟動指定 HKL,使其對當前執行緒/進程生效 |
路徑:HKEY_USERS\.DEFAULT\Keyboard Layout\Preload
子鍵名為序號(1, 2, 3...),值為 KLID(8 位十六進位),例如 E0040804。
前 4 碼 E004 為裝置識別碼(如智慧 ABC),後 4 碼 0804 為語言識別碼(簡體中文)。
路徑:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\
子鍵名即為 KLID,值包含 Layout Text(顯示名稱)、Layout File(IME DLL 檔名)等詳細資訊。
BOOL CMyDialog::OnInitDialog() {
CDialog::OnInitDialog();
HKEY hKeyPreload, hKeyLayout;
char szSubKey[16], szKLID[16];
DWORD dwSize = 16;
int nIndex = 0;
CString strPreloadBase = _T(".DEFAULT\\Keyboard Layout\\Preload\\");
// 遍歷 Preload 下的數字子鍵 1,2,3...
for (int i = 1; ; ++i) {
CString strSubKey;
strSubKey.Format(_T("%s%d"), strPreloadBase, i);
if (RegOpenKey(HKEY_USERS, strSubKey, &hKeyPreload) != ERROR_SUCCESS)
break; // 無更多子鍵
if (RegQueryValueEx(hKeyPreload, NULL, NULL, NULL, (LPBYTE)szKLID, &dwSize) == ERROR_SUCCESS) {
// 到 Keyboard Layouts 讀取顯示名稱
CString strLayoutPath = _T("SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\") + CString(szKLID);
if (RegOpenKey(HKEY_LOCAL_MACHINE, strLayoutPath, &hKeyLayout) == ERROR_SUCCESS) {
BYTE szName[80];
DWORD dwType = REG_SZ, dwNameSize = 80;
if (RegQueryValueEx(hKeyLayout, _T("Layout Text"), NULL, &dwType, szName, &dwNameSize) == ERROR_SUCCESS) {
m_cbIME.AddString((LPCTSTR)szName); // 顯示友善名稱
m_arrKLID.Add(CString(szKLID)); // 陣列保存對應 KLID
}
RegCloseKey(hKeyLayout);
}
}
RegCloseKey(hKeyPreload);
}
m_cbIME.SetCurSel(0);
return TRUE;
}
void CMyDialog::OnSetfocusEdit() {
int nSel = m_cbIME.GetCurSel();
if (nSel == CB_ERR) return;
CString strKLID = m_arrKLID[nSel];
HKL hkl = LoadKeyboardLayout(strKLID, KLF_ACTIVATE);
if (hkl) {
ActivateKeyboardLayout(hkl, KLF_SETFORPROCESS);
}
}
void CMyDialog::OnSelchangeComboIME() {
m_editTarget.SetFocus(); // 觸發 EN_SETFOCUS 再次執行步驟 2
}
Win2000 之後,HKEY_USERS\.DEFAULT\Keyboard Layout\Preload 下不再有數字子鍵,改為直接在該鍵值中以數字為名稱、KLID 為資料。枚舉時需改用 RegEnumValue 而非 RegEnumKeyEx。
雖然 MFC 無內建 IME 控制項,但結合登錄檔枚舉與 LoadKeyboardLayout/ActivateKeyboardLayout 兩大核心 API,即可構建完整的動態輸入法切換功能。關鍵在於正確解析 KLID、對應顯示名稱,並在編輯控制項焦點事件中適時啟動。