今日は、特定のWin32コントロール(ウィンドウ)でIMEを無効化する方法についてです。
近頃はタブレットも珍しくない存在になりました。そこで、タブレットにおいてなくてはならないWindowsのスクリーンキーボードを意識してみたという話です。
従来の方法とその問題点
これまで、IMEを無効にするにはマイクロソフトのKB171154(特定のウィンドウを IME を無効にする方法)に書かれている方法、つまりImmAssociateContextにヌルを渡すことがセオリーでした。
しかし、タブレットを触っていると、これだけでは不十分ではないかと思いました。なぜなら、スクリーンキーボードにIMEをオン・オフするキー(スペースの左側にある「あ」、押すと「A」と交互に変わる。以下「あ/Aキー」と表記)が表示されるためです。IME無効の場合、押しても何も起こらないだけなので、存在が気になります。
今回やってみた手法
幸いなことに、パスワード入力の場面で現るスクリーンキーボードは「あ/Aキー」がないことにすぐに気がつきました。これを通常のエディットコントロールに適用できれば良さそうです。
そこで、WTLでそのような処理を行うクラスCEditWithoutImeを作りました。こちらがソースコードです: Gist: CEditWithoutIme.h。
IAccessible::get_accStateメンバ関数をオーバーライドし、そこで定数STATE_SYSTEM_PROTECTEDを加えるのがミソでした。これによりパスワード用のスクリーンキーボードになります。
IFACEMETHOD(get_accState)( VARIANT varChild, __RPC__out VARIANT* pvarState) override { ATLENSURE_RETURN_HR(pvarState != nullptr, E_POINTER); ATL::CComVariant state; auto hr = m_base->get_accState(varChild, &state); ATLENSURE_RETURN_HR(SUCCEEDED(hr), hr); ATL::CComVariant additionalState(STATE_SYSTEM_PROTECTED); return VarOr(&state, &additionalState, pvarState); } |
もちろん、ImmAssociateContextにヌルを渡す方法も併用しています。
その使用法
使い方は、このようにエディットコントロールをサブクラス化またはスーパークラス化するだけです。ダイアログなら、WM_INITDIALOG内でサブクラス化すれば大丈夫です。そのほかはWTL::CEditと同じです。
#define _ATL_NO_AUTOMATIC_NAMESPACE #define _WTL_NO_AUTOMATIC_NAMESPACE #include "CEditWithoutIme.h" #pragma comment(lib, "imm32.lib") #pragma comment(lib, "oleacc.lib") #pragma comment(linker, "\"/manifestdependency:type='Win32' "\ "name='Microsoft.Windows.Common-Controls' version='6.0.0.0' "\ "processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") class Module : public ATL::CAtlExeModuleT<Module> {}; Module module; class TestWindow : public ATL::CWindowImpl<TestWindow> { public: DECLARE_WND_CLASS_EX(TEXT("Test Window Class"), 0, COLOR_3DFACE); private: BEGIN_MSG_MAP(TestWindow) MSG_WM_CREATE(OnCreate) MSG_WM_DESTROY(OnDestroy) END_MSG_MAP() LRESULT OnCreate(const CREATESTRUCT* pcs) { m_font = WTL::AtlCreateControlFont(); // 代わりにm_edit.Create(…)を呼び出せばスーパークラス化になる。 if (auto hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, TEXT("EDIT"), nullptr, WS_CHILD | WS_VISIBLE, 10, 10, 300, 30, *this, nullptr, nullptr, nullptr)) { m_edit.SubclassWindow(hwnd); } else { return -1; } m_edit.SetFont(m_font); return 0; } void OnDestroy() { PostQuitMessage(0); } CEditWithoutIme m_edit; WTL::CFont m_font; }; int Run(int cmdShow) { TestWindow wnd; RECT rc = { 0, 0, 360, 100 }; ATLENSURE_RETURN_VAL(wnd.Create( nullptr, rc, TEXT("Test"), WS_OVERLAPPEDWINDOW), -1); wnd.ShowWindow(cmdShow); wnd.UpdateWindow(); WTL::CMessageLoop msgLoop; return msgLoop.Run(); } int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int cmdShow) { InitCommonControls(); ATLENSURE_RETURN_VAL(SUCCEEDED(CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)), -1); int ret = Run(cmdShow); CoUninitialize(); return ret; } |
Special Thanks
@d_toybox まだちゃんと確認し切れていないのですが,どうもアクセシビリティ系の設定に依存するようです.元々「InputScopeではIEと同じレイアウトが再現できない」という内容で問い合わせていました.現在はSTATE_SYSTEM_PROTECTEDを確認中です.
— NyaRuRu (@NyaRuRu) 2013, 1月 28
スポンサード リンク |