サポート技術情報 233263 の方法を Windows 7 上の Internet Explorer でホストされる ActiveX コントロールに適用すると、Aero フリップ 3D などの操作後にエディット コントロールの入力で文字化けが発生する現象について
こんにちは。Visual Studio サポート チームです。
今回は、サポート技術情報 233263 で案内している IsDialogMessage 関数の制限に対する解決方法に関し、この方法を利用する際に発生する可能性のある文字化けの問題についてご案内します。
上記サポート技術情報の方法を、Windows Vista や Windows 7 上の、バージョン 9 以上の Internet Explorer (IE) 上でホストされる ActiveX コントロールに適用した場合、エディット コントロールへの文字入力で文字化けが発生する場合があります。以下に、現象の詳細と対処方法をご案内します。
なお、本現象は Windows 8 以降の OS では発生しません。
■ 現象の発生条件
以下でご案内する文字化けの現象は、次の全ての条件を満たす場合に発生します。
- IE でホストされる ActiveX コントロールなどのアプリケーションで、エディットコントロールを持つモードレス ダイアログを使用している。
- サポート技術情報 233263 の方法を使用してダイアログ メッセージをハンドルしている。
- Windows 8 より前のバージョンの OS を利用している。
- バージョン 9 以上の Internet Explorer を利用し、GPU レンダリングをしている。(GPU レンダリングは既定で有効となっています。)
- IE ウィンドウのアクティブ化、非アクティブ化を繰り返し行っている。
■ 現象の詳細
IE 9 以降の新しいバージョンの IE では GPU を活用したレンダリングの強化が行われており、この機能の実現のために、内部で Direct2D や DirectWrite といった DirectX の機能を利用しています。これらの機能を利用するため、IE 内部では DirectX 関連モジュールを使用したメッセージ フックを利用しています。
サポート技術情報 233263 の方法ではメッセージ フックを利用していますが、このメッセージ フックと、前述の DirectX 用に登録したメッセージ フックの処理順序によって、入力された文字コードが 2 つのメッセージ フックを正しく伝搬できずに文字化けを起こす場合があります。この問題は、DirectX 関連モジュール (DXGI.dll) 内の処理でフックしたメッセージを、フックチェーン上の次のフックに渡す処理において、文字コードの扱いに関する不具合が存在するために発生します。(本動作は Windows 8 で修正されています。)
本現象は、IE 上で対象の ActiveX アプリケーションを起動した初期の状態では発生しませんが、IE からフォーカスが外れるなどして非アクティブになり、その後再び IE がアクティブになるなどして、DirectX のフックが登録解除・再登録されるとフックの実行順序が変化し、アプリケーションが登録したフックよりも先に、DirectX のフックが実行される状態となった場合に発生します。
上述の DirectX のフック登録・再登録動作の実行タイミングは不確定的ですが、Windows 7 の Aero フリップ 3D を利用した場合には高確率で実行されます。
◇ 通常時のフックの実行順序
① アプリケーションが登録したメッセージフック
↓
② DirectX が登録したメッセージ フック
入力した文字が適切に表示されます。
◇ 文字化け発生時のフックの実行順序
① DirectX が登録したメッセージ フック
↓
② アプリケーションが登録したメッセージフック
文字化けが発生します。
■ 解決策
この文字化けの現象は、以下のいずれかの方法で対処可能です。
(方法 1) IE の GPU レンダリングを無効にする方法
(方法 2) ダイアログを起動するスレッドを作成し、モーダル ダイアログとして起動する方法
(方法 3) アプリケーションがアクティブになるタイミングでフックを再登録する方法
(方法 1) IE の GPU レンダリングを無効にする方法
IE の GPU レンダリングを無効に設定することで DirectX 関連のフック処理が実行されなくなり、文字化けの現象を回避することが可能です。GPU レンダリングを無効にする方法については、以下のサポート技術情報を参照してください。
本方法は対象のアプリケーションのみではなく、全ての IE のプロセスでに影響する点については、予めご承知おきください。
(方法 2) ダイアログを起動するスレッドを作成し、モーダルダイアログとして起動する方法
サポート技術情報 233263 の方法は、モーダレス ダイアログでダイアログ メッセージをハンドルするために独自のメッセージ フック処理を追加しているものでした。
本処理自体に問題があるものではありませんが、メッセージフックを使用する方法ではなく、新たに作成した UI スレッド (メッセージループを実装したスレッド) 上でダイアログをモーダルで実行することで、ダイアログ メッセージをハンドルしつつ、文字化けの現象を回避することが可能です。
(方法 3) アプリケーションがアクティブになるタイミングでフックを再登録する方法
前述のとおり、文字化けの現象は、文字入力に対応するメッセージをアプリケーションのフックプロシージャより先に DirectX のフック プロシージャが処理した場合に発生します。
このため、文字入力が行われる前の時点で、アプリケーションのフックが DirectX のフックよりも先に実行されるような状態にすることで、文字化けの現象を回避することが可能です。
メッセージ フックでは、フックチェーンの先頭に登録されたフック プロシージャから順に処理が行われますが、対象のフックを登録解除・再登録することで、当該フック処理をフック チェーンの先頭に登録しなおすことが可能です。
参考文書 : Hook Chains
----
The SetWindowsHookEx function always installs a hook procedure at the beginning of a hook chain. When an event occurs that is monitored by a particular type of hook, the system calls the procedure at the beginning of the hook chain associated with the hook.
----
具体的には、以下のコード例のように、ActiveX アプリケーションがフォーカスを得たタイミングなどで、毎回、アプリケーションのフックの登録解除・再登録を行うことで対応可能です。
//// 以下は ATL の CAxDialogImpl 派生クラスの場合の例です。// フック ハンドルは別途グローバルに定義され、// アプリケーション起動時にサポート技術情報 233263 の方法で// 登録されているものとします。//extern HHOOK g_hHook;//// WM_ACTIVATE のハンドラを以下のように実装します。//LRESULT CMyDialog::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){if (wParam != WA_INACTIVE && g_hHook){// フックの登録を解除します。::UnhookWindowsHookEx (g_hHook);// フックを再登録します。// これにより登録したフック プロシージャがフック チェーンの// 先頭に登録されます。g_hHook = ::SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, ::GetCurrentThreadId());}return 0;} |
製品の不具合でご不便をおかけしており誠に申し訳ございませんが、本記事でご案内した情報がお役に立ちましたら幸いです。