今日は、特定のWin32コントロール(ウィンドウ)でIMEを無効化する方法についてです。

近頃はタブレットも珍しくない存在になりました。そこで、タブレットにおいてなくてはならないWindowsのスクリーンキーボードを意識してみたという話です。

従来の方法とその問題点

これまで、IMEを無効にするにはマイクロソフトのKB171154(特定のウィンドウを IME を無効にする方法)に書かれている方法、つまりImmAssociateContextにヌルを渡すことがセオリーでした。

しかし、タブレットを触っていると、これだけでは不十分ではないかと思いました。なぜなら、スクリーンキーボードにIMEをオン・オフするキー(スペースの左側にある「あ」、押すと「A」と交互に変わる。以下「あ/Aキー」と表記)が表示されるためです。IME無効の場合、押しても何も起こらないだけなので、存在が気になります。

On-Screen-keyboard-normal

今回やってみた手法

幸いなことに、パスワード入力の場面で現るスクリーンキーボードは「あ/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);
}

On-Screen-keyboard-protected

もちろん、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


スポンサード リンク

この記事のカテゴリ

  • ⇒ IMEを無効にすることとスクリーンキーボード
  • ⇒ IMEを無効にすることとスクリーンキーボード
  • ⇒ IMEを無効にすることとスクリーンキーボード