Windows CE 環境において、独自のユーザーインターフェースを採用しているアプリケーションでは、システム標準の输入法切り替え機能を利用できないケースが多々あります。システムトレイや標準の入力バーを使用しない場合、開発者が自ら输入法のリストを取得し、UI 上で選択可能にする必要があります。
本稿では、レジストリ情報を参照して利用可能な输入法を列挙し、プログラムから活性化する方法について解説します。Windows CE 5.0 環境を想定していますが、基本的なレジストリ構造は共通しています。
実装の仕組み
输入法の情報は Windows レジストリに保存されています。主に以下の 2 つのキーを参照します。
- HKCU\Keyboard Layout\Preload: ユーザーごとに定義された输入法のロード順序と ID が保存されています。
- HKLM\System\CurrentControlSet\Control\Layouts: 各输入法 ID に対応する表示名称(Layout Text)が保存されています。
これらの情報を組み合わせることで、ID と表示名のペアリストを構築できます。選択時には LoadKeyboardLayout および ActivateKeyboardLayout API を使用します。
クラス設計
以下のヘッダーファイルでは、输入法の情報を管理するクラス CImeManager を定義しています。メモリ管理を容易にするために std::vector を採用し、レジストリパスの区切り文字も修正しています。
/****************************************************************************
* File: ImeManager.h
* Description: Windows CE Input Method Enumeration and Activation
* Version: 1.0
*****************************************************************************/
#ifndef __IME_MANAGER_H__
#define __IME_MANAGER_H__
#include <vector>
#include <string>
#include <windows.h>
#include <winreg.h>
struct ImeEntry {
std::wstring layoutId;
std::wstring displayName;
};
class CImeManager {
private:
std::vector<ImeEntry> m_imeList;
bool FetchRegistryData() {
HKEY hKeyPreload = NULL;
HKEY hKeyLayouts = NULL;
const wchar_t* szPreloadPath = L"Keyboard Layout\\Preload";
const wchar_t* szLayoutsBase = L"System\\CurrentControlSet\\Control\\Layouts\\";
// Open Preload key to get IDs
if (RegOpenKeyEx(HKEY_CURRENT_USER, szPreloadPath, 0, 0, &hKeyPreload) != ERROR_SUCCESS) {
return false;
}
DWORD index = 1;
wchar_t szValueName[16];
wchar_t szLayoutId[16];
DWORD dwType = REG_SZ;
DWORD dwDataSize = sizeof(szLayoutId);
while (true) {
_itow(index, szValueName, 10);
dwDataSize = sizeof(szLayoutId);
if (RegQueryValueEx(hKeyPreload, szValueName, NULL, &dwType, (LPBYTE)szLayoutId, &dwDataSize) != ERROR_SUCCESS) {
break;
}
// Open Layouts key to get Name
std::wstring wsLayoutPath = szLayoutsBase;
wsLayoutPath += szLayoutId;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, wsLayoutPath.c_str(), 0, 0, &hKeyLayouts) == ERROR_SUCCESS) {
wchar_t szName[128];
dwDataSize = sizeof(szName);
if (RegQueryValueEx(hKeyLayouts, L"Layout Text", NULL, &dwType, (LPBYTE)szName, &dwDataSize) == ERROR_SUCCESS) {
ImeEntry entry;
entry.layoutId = szLayoutId;
entry.displayName = szName;
m_imeList.push_back(entry);
}
RegCloseKey(hKeyLayouts);
}
index++;
}
RegCloseKey(hKeyPreload);
return !m_imeList.empty();
}
public:
CImeManager() {
FetchRegistryData();
}
~CImeManager() {
m_imeList.clear();
}
bool ActivateIme(size_t index) {
if (index >= m_imeList.size()) {
return false;
}
HKL hKl = LoadKeyboardLayout(m_imeList[index].layoutId.c_str(), KLF_ACTIVATE);
if (hKl) {
ActivateKeyboardLayout(hKl, KLF_SETFORPROCESS);
return true;
}
return false;
}
const std::vector<ImeEntry>& GetImeList() const {
return m_imeList;
}
};
#endif
利用例
アプリケーションのエントリーポイントにおいて、上記クラスをインスタンス化し、取得したリストを処理するサンプル代码如下です。リストを巡回し、デバッグ出力を行い、特定の输入法を活性化します。
// MainApp.cpp
#include "stdafx.h"
#include "ImeManager.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
CImeManager imeMgr;
const std::vector<ImeEntry>& list = imeMgr.GetImeList();
for (size_t i = 0; i < list.size(); ++i) {
// UI 描画処理などはここに記述
// Example: DrawText(hdc, list[i].displayName.c_str(), ...);
// デバッグ出力
RETAILMSG(1, (TEXT("IME [%d]: %s\r\n"), i, list[i].displayName.c_str()));
}
// 例:リストの最初の输入法を活性化
if (!list.empty()) {
imeMgr.ActivateIme(0);
}
return 0;
}
レジストリキーのパス指定にはバックスラッシュを使用し、メモリ管理はコンテナクラスに委ねることで、リークのない実装となっています。