none
MDI(タブ付きドキュメント)のMFCアプリケーションにおいて、Viewにフォーカスがあるにも関わらず、IMEが無効になってしまう場合がある RRS feed

  • 質問

  • VC++ 2010 で MFCアプリケーションを開発しております。

    タブ付きMDIアプリケーションで、テキストエディタのようなものを作っているのですが、Viewにフォーカスがあるにも関わらずIMEが無効になってしまい、日本語入力をONにすることができない状態が発生する場合があることが判明しました。

    その後、調査の結果、MFCアプリケーションウィザードでアプリケーションを作成しただけでも、IMEが無効になることがわかりました。

    発生手順は以下の通りです。

    1. マルチドキュメント(タブ付きドキュメント)でMFCアプリケーションを新規作成。
    2. 作成したアプリケーションを実行し、2つ以上タブを開く。
    3. タスクバーに表示されるアプリケーションのアイコンをクリックし、サムネイルをクリックすると、IMEが無効になり、日本語入力をONにすることができなくなる。

    ポイント

    • タブ付きドキュメントでないときは発生しない。
    • タブを1つだけ開いているときは発生しない。
    • アプリケーション上のタブをクリックすると、IMEを有効になり、日本語入力をONにすることができるようになる。
    • Windows 7, Microsoft Office IME 2010 で実行。

    現状ではユーザーの使い勝手を大きく損ねてしまうことから、プログラムの修正によって回避したいと考えております。

    もし、回避策がございましたら、教えていただけるとありがたいです。

    ご協力のほどよろしくお願い申し上げます。





    • 編集済み yoshinor 2016年5月20日 17:30
    2016年5月20日 17:26

回答

  • ご連絡ありがとうございます。

    VS2013のソースコードを調べたところ、CChildFrame と CMainFrame を次のように修正することで、対処することができましたのでご報告します。

    ChildFrame.h

    class CChildFrame : public CMDIChildWndEx
    {
    	DECLARE_DYNCREATE(CChildFrame)
    public:
    	CChildFrame();
    	virtual ~CChildFrame();
    
    protected:
    	DECLARE_MESSAGE_MAP()
    	virtual void ActivateTopLevelFrame(); //←追加
    };


    ChildFrame.cpp

    UINT AFX_WM_AFTER_TASKBAR_ACTIVATE = ::RegisterWindowMessage(_T("AFX_WM_AFTER_TASKBAR_ACTIVATE")); void CChildFrame::ActivateTopLevelFrame() { CMDIFrameWndEx* pTopLevel = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame()); if (pTopLevel == NULL) { return; } ActivateFrame(); pTopLevel->SetForegroundWindow(); BOOL bIsMinimized = pTopLevel->IsIconic(); pTopLevel->ShowWindow(bIsMinimized ? SW_RESTORE : SW_SHOW); pTopLevel->PostMessage(AFX_WM_AFTER_TASKBAR_ACTIVATE, (WPARAM)bIsMinimized, (LPARAM)GetSafeHwnd()); }


    MainFrame.h

    class CMainFrame : public CMDIFrameWndEx
    {
    	DECLARE_DYNAMIC(CMainFrame)
    public:
    	CMainFrame();
    	virtual ~CMainFrame();
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
    	afx_msg LRESULT OnAfterTaskbarActivate(WPARAM wParam, LPARAM lParam); //←追加
    };
    

    MainFrame.cpp

    extern UINT AFX_WM_AFTER_TASKBAR_ACTIVATE;
    
    EGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx)
    	ON_REGISTERED_MESSAGE(AFX_WM_AFTER_TASKBAR_ACTIVATE, &CMainFrame::OnAfterTaskbarActivate)
    END_MESSAGE_MAP()
    
    LRESULT CMainFrame::OnAfterTaskbarActivate(WPARAM, LPARAM lp)
    {
    	HWND hwndMDIChild = (HWND)lp;
    	if(hwndMDIChild != NULL && ::IsWindow(hwndMDIChild))
    	{
    		::SetFocus(hwndMDIChild);
    	}
    	return 0;
    }

    以上の修正により、タスクバーをクリックしてもIMEの切り替えが有効のままになります。

    今まで、いろいろとご助言いただきありがとうございました。

    • 回答としてマーク 星 睦美 2016年7月6日 8:03
    2016年5月27日 14:18

すべての返信

  • れす付きませんね。
    ご指摘の症状を確認しました。VS2010 Ver4.5.51029 SP1Rel on Windows7pro(64bit)+SP1

    1.ウィザードコードのまんまビルドしても発生します。
    2.VS2013のではウィザードコード発生しませんでした。
    3.両ウィザードコードに、本症状に関連しそうな違いは見つかりません。

    以上を勘案すると、VS2010に同梱されているMFCの瑕疵である疑いが濃厚です。
    対策ですが、可能であれば新しいVisual Studioに変更するのが確実だと考えられます。
    タブグループ、タブ等の内部コードを追うのは結構ほねがおれます。

    2016年5月24日 6:15
  • ご回答ありがとうございます。

    おっしゃるとおり、VS2013のウィザードコードでは発生しないことを確認しました。

    本来は新しいVisual Studioにアップグレードすればよいのですが、他に使っているライブラリの問題などもあり、プロジェクトを簡単には更新できない状況です。

    できましたら、IMEのAPIを呼び出すなどの方法で解決できると幸いです。

    2016年5月24日 11:42
  • 残念ながら、ごく普通のあたりまえなアイデアしか浮かびません。

    1.WM_IME_NOTFY時、WPARMAMがIMN_SETOPENSTATUSの時にImmGetOpenStatus()して、
      IMEの起動状況を保持しておく。
    2.タブのアクティベートタイミング、例えばAFX_WM_ONCHANGE_ACTIVE_TABメッセージなど
     で、記憶しておいたIMEの起動状況を再現する。

    とかですね。あしからず。

    2016年5月25日 7:44
  • ご連絡ありがとうございます。

    VS2013のソースコードを調べたところ、CChildFrame と CMainFrame を次のように修正することで、対処することができましたのでご報告します。

    ChildFrame.h

    class CChildFrame : public CMDIChildWndEx
    {
    	DECLARE_DYNCREATE(CChildFrame)
    public:
    	CChildFrame();
    	virtual ~CChildFrame();
    
    protected:
    	DECLARE_MESSAGE_MAP()
    	virtual void ActivateTopLevelFrame(); //←追加
    };


    ChildFrame.cpp

    UINT AFX_WM_AFTER_TASKBAR_ACTIVATE = ::RegisterWindowMessage(_T("AFX_WM_AFTER_TASKBAR_ACTIVATE")); void CChildFrame::ActivateTopLevelFrame() { CMDIFrameWndEx* pTopLevel = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame()); if (pTopLevel == NULL) { return; } ActivateFrame(); pTopLevel->SetForegroundWindow(); BOOL bIsMinimized = pTopLevel->IsIconic(); pTopLevel->ShowWindow(bIsMinimized ? SW_RESTORE : SW_SHOW); pTopLevel->PostMessage(AFX_WM_AFTER_TASKBAR_ACTIVATE, (WPARAM)bIsMinimized, (LPARAM)GetSafeHwnd()); }


    MainFrame.h

    class CMainFrame : public CMDIFrameWndEx
    {
    	DECLARE_DYNAMIC(CMainFrame)
    public:
    	CMainFrame();
    	virtual ~CMainFrame();
    protected:
    	DECLARE_MESSAGE_MAP()
    public:
    	afx_msg LRESULT OnAfterTaskbarActivate(WPARAM wParam, LPARAM lParam); //←追加
    };
    

    MainFrame.cpp

    extern UINT AFX_WM_AFTER_TASKBAR_ACTIVATE;
    
    EGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx)
    	ON_REGISTERED_MESSAGE(AFX_WM_AFTER_TASKBAR_ACTIVATE, &CMainFrame::OnAfterTaskbarActivate)
    END_MESSAGE_MAP()
    
    LRESULT CMainFrame::OnAfterTaskbarActivate(WPARAM, LPARAM lp)
    {
    	HWND hwndMDIChild = (HWND)lp;
    	if(hwndMDIChild != NULL && ::IsWindow(hwndMDIChild))
    	{
    		::SetFocus(hwndMDIChild);
    	}
    	return 0;
    }

    以上の修正により、タスクバーをクリックしてもIMEの切り替えが有効のままになります。

    今まで、いろいろとご助言いただきありがとうございました。

    • 回答としてマーク 星 睦美 2016年7月6日 8:03
    2016年5月27日 14:18