17:03:39)追加された行はこの色です。削除された行はこの色です。.NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。[#od51b6e4]ウィンドウ・システムの詳細を理解して、>「バックグラウンド・スレッドから上げた~モーダル・ダイアログがモーダルにならない理由」などのさまざまな挙動を理解できるようにする。[#s05a223e]Windowsプログラムは、-エントリポイント(プログラムの開始)である(1)WinMainと、-Windowsから呼び出されて渡されたメッセージを処理する~(2)ウインドウプロシ-ジャ(通常WinProcと書く)から成り立つ。(1)WinMainの中は、-(A)ウインドウクラスの登録-(B)ウィンドウの作成-(C)ウインドウの表示-(D)メッセージループの4つのパートから成り立つ。メッセージ用のキュー。~ウィンドウ・システム(WindowsのGUI)の根幹をなすメカニズム。[#da9d8264]-Windowsメッセージキューは、~1つのスレッドが1つだけ保持できる。--ウィンドウを作成したスレッドには、~Windowsメッセージキューが与えられる。--スレッドからメッセージ系のAPIを使用した場合も、~Windowsメッセージキューが与えられる。-この際、システムは、--THREADINFO構造体を作成してスレッドに割り当てる。--THREADINFO構造体はWindowsメッセージキューに関する情報を持っている。[#bff89f1a]-Windowsメッセージキューは、複数のウィンドウ間で共有される。--通常、1つのプロセスは、1つのUIスレッドで、複数(全て)のウィンドウを処理する。~このため、通常、1プロセス、1UIスレッド、1Windowsメッセージキューになる。--この理由は、1プロセスで、2つ以上のUIスレッドを持つ場合、~画面間の通信をスレッドセーフに実装する必要があり実装し難くなる。[#j7fd3258]後述のWin32APIを使用する。-PostMessage()~メッセージ・ループにキューイングする。-SendMessage()~ウィンドウ・プロシージャ(WndProc)を直接呼び出す。[#j7fd3258]-プロセス間通信-スレッド間通信-COMのSTA(呼び出しの直列化)などでも利用される。構造体を~DispatchMessage関数でウィンドウ・プロシージャに渡す。[#u8d37135].NETで言う、FormもControlも全てが~ウィンドウ(hwnd:ウィンドウハンドルを持つ)。基本的な情報が登録されている。RegisterClassExで登録する。登録し、CreateWindowEXで、ウィンドウを作る。来る(これについては後述)。[#hb30291b]あらかじめ登録されている既定のウィンドウクラスとしては-"BUTTON"-"COMBOBOX"-"EDIT"-"LISTBOX"-"MDICLIENT"-"RichEdit"-"RICHEDIT_CLASS"-"SCROLLBAR"-"STATIC"がある(.NETで言う、Control的なもの)。これらは登録済みで個別用途のウィンドウクラスであるため~(例えばボタンウィンドウやエディットボックスなど)、~下記のRegisterClassEx関数を使用せずに作成可能。hwnd,...)する。[#u0ed1ff6]VC++のダイアログエディタで、ダイアログリソースを作ってCreateDialogする。来る(これについては後述)。[#ibfcede7]イベントハンドラみたいなもの。以下のように、イベントハンドラを実装する。-Control的なウィンドウクラスの標準WndProcは、~親ウィンドウクラスに対してSendMessageでWM_Commandを送るようになっている。WM_COMMAND:等を使用して追加する。置き換えて、--元あったWndProcを呼ばなければ、そのイベントの処理を置き換え。pMsg)し、~元のWndProcを呼ぶことで、WndProcのチェインに割って入る。この既定のWndProcを置き換えることで、~イベントの挙動(≠イベント・ハンドラ、ボタン押下時にボタンが凹む等)~をカスタマイズすることが可能である。(Windows)~http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms633576.aspx>ウィンドウクラスの属性を表します。関数~http://msdn.microsoft.com/ja-jp/library/cc410996.aspx>ウィンドウクラスを登録します。関数~http://msdn.microsoft.com/ja-jp/library/cc410713.aspx>メモリ上にウィンドウを生成します。関数~http://msdn.microsoft.com/ja-jp/library/cc411211.aspx>ウィンドウを表示します。関数~http://msdn.microsoft.com/ja-jp/library/cc428780.aspx>ウィンドウを更新します。[#p8157cb2]PostMessageはメッセージ・ループにキューイングする。つのメッセージをポストします。--対応するスレッドがメッセージを処理するのを待たずに制御を返します。つのメッセージをポストします。--この関数は、スレッドがメッセージを処理するのを待たず、即座に制御を返します。メッセージをメッセージキューにポストする。[#v0311886]SendMessageはウィンドウ・プロシージャ(WndProc)を直接呼び出す。複数のウィンドウへ、指定されたメッセージを送信します。--指定されたウィンドウのウィンドウプロシージャを呼び出します。--ウィンドウプロシージャがメッセージを処理し終わった後で、制御を返します。--基本的には、サブルーチン的にイベント実装を処理するためのもの。--スレッド跨ぎか否かで動作が大きく異なる。---スレッド跨ぎでない場合、サブルーチン的にイベント実装を処理---スレッド跨ぎの場合、送信・応答メッセージキューを使用する(同期呼出の実現)。--なお、処理の優先度は、PostMessageより高い。以下は、SendMessageの同期呼出ハングの問題を回避するための関数(送信側)。タイムアウト機能付きバージョン。--同一スレッドからの場合はタイムアウト地を無視する。Callback機能付き非同期版(即座に制御を返す)--オーバーラップウィンドウにメッセージをブロードキャストできる。関数~http://msdn.microsoft.com/ja-jp/library/cc411012.aspx--PostMessageに近いが、以下の点がPostMessageと異なる。---PostMessageよりも優先度が高い(先にキューから取り出される)。---スレッド跨ぎでない場合、SendMessage的に動作する。以下は、SendMessageの同期呼出ハングの問題を回避するための関数(受信側)。関数~http://msdn.microsoft.com/ja-jp/library/cc411016.aspx--SendMessage送信側の応答メッセージキューに結果をポストし、制御を返す。--スレッド跨ぎでない場合、何もしない(falseを返す)。関数~http://msdn.microsoft.com/ja-jp/library/cc364787.aspx--スレッド跨ぎでない場合、falseを返す。--スレッド跨ぎの場合、trueを返す。構造体~http://msdn.microsoft.com/ja-jp/library/vstudio/900ks98t.aspx>メッセージのデータを表します。関数とは異なり、~メッセージキューにメッセージがなかった場合、~メッセージがポストされるのを待たずに制御を返す。構造体をウィンドウ・プロシージャに渡す。関数--http://msdn.microsoft.com/ja-jp/library/cc364868.aspx--ウィンドウへ送信されたメッセージを処理する、アプリケーション定義のコールバック関数です。関数--http://msdn.microsoft.com/ja-jp/library/cc410753.aspx--既定のウィンドウプロシージャを呼び出して、既定の処理を提供します。--この関数は、すべてのメッセージが処理されることを保証します。メッセージ--ウィンドウプロシージャはその全てのメッセージに対応できなければならないため。メッセージをメッセージキューにポストする。-RITが接続するVIQのスレッドを識別する--マウスポインタ:マウスカーソルの下のウィンドウのスレッド--キーストローク:フォアグラウンド・ウィンドウのスレッド--その他、Alt+Tabなどの特殊キー・シーケンスを処理する。関数~http://msdn.microsoft.com/ja-jp/library/cc411074.aspx--WM_SETFOCUS--VIQの切り替えはできない。--通常、RITに接続されているときだけ呼び出す。--スレッド1がRITに接続しているときに~スレッド2が自身のウィンドにSetFocusを~呼び出すと2つのフォーカス矩形が現れ問題。関数~http://msdn.microsoft.com/ja-jp/library/cc364641.aspx>ローカル入力状態からフォーカスを持つウィンドウのHWNDを返す。関数~http://msdn.microsoft.com/ja-jp/library/cc411014.aspx--ウィンドウをアクティブにする。~--RITに接続するVIQの切り替えはできない。--通常、RITに接続されているときだけ呼び出す。関数~http://msdn.microsoft.com/ja-jp/library/cc410861.aspx>ローカル入力状態からアクティブなウィンドウのHWNDを返す。関数~http://msdn.microsoft.com/ja-jp/library/cc410618.aspx>HWND_TOPを指定しSetWindowPosを呼び出す(RITに接続するVIQを変更可能)。関数~http://msdn.microsoft.com/ja-jp/library/cc411206.aspx>ウィンドウをアクティブにしてサイズ、位置、Zオーダを変更(RITに接続するVIQを変更可能)。関数~http://msdn.microsoft.com/ja-jp/library/cc364732.aspx>フォアグラウンド・ウィンドウのHWNDを返す。関数~http://msdn.microsoft.com/ja-jp/library/cc411039.aspx--ウィンドウをフォアグラウンドに移動する(RITに接続するVIQを変更可能)--このAPIは、プログラマによって乱用されたため、~MSはRITに接続されているか、RITに接続されている別スレッドが一定時間以上、~入力を受け取っていない場合にのみ成功するように機能を追加した。--失敗の際は、タスクバーをフラッシュさせる。関数~http://msdn.microsoft.com/ja-jp/library/cc430255.aspx>指定したプロセスのスレッドがSetForgroundWindowを呼び出すことを許可する。関数~http://msdn.microsoft.com/ja-jp/library/cc410898.aspx>他のSetForgroundWindow呼出をロックする(Windowsスタートメニューなどで使用されている)。関数~http://msdn.microsoft.com/ja-jp/library/cc364676.aspx--最後のキー入力を返す。--入力フォーカスを持ったウィンドウを持ってる必要はない。関数~http://msdn.microsoft.com/ja-jp/library/cc364583.aspx--キーが押されているか?前回の呼び出し以降にキーが押されたか?を返す。--入力フォーカスを持ったウィンドウを持ていること。関数~http://msdn.microsoft.com/ja-jp/library/cc410663.aspx>マウス・カーソルの範囲を指定する(非同期的なアクティブ化イベントで中止)。関数~http://msdn.microsoft.com/ja-jp/library/cc411051.aspx--マウス・カーソルがウィンドウの範囲内にあるかどうか~に関わり無く、すべてのマウス入力を受け取る。つのウィンドウだけがマウスをキャプチャできる。--通常はマウスのボタン押下後に呼び出す。関数~http://msdn.microsoft.com/ja-jp/library/cc410990.aspx>上記のマウスのキャプチャを停止する。関数~http://msdn.microsoft.com/ja-jp/library/cc429027.aspx>複数のスレッドに複数のVIQとローカル入力状態を共有させる。--ただし、THREADINFO構造体の、---ポストメッセージ---送信メッセージキュー---応答メッセージキュー---ウェイクフラグ>については自分のものを使い続ける。--このAPIはシステムの強度を損なう可能性がある。--用途としてはジャーナルの記録・再生フックをインストールした場合。API関数で自分のフォームを前面に出します。その後もう一度AttachThreadInputAPI関数でデタッチして解除すれば可能です。Windowsの仕様に反しますが。API関数で自分のフォームを前面に出します。~その後もう一度AttachThreadInputAPI関数でデタッチして解除すれば可能です。~もちろん、Windowsの仕様に反しますが。[#d5dfeb5c]UIオートメーションの裏側。現行はどっちを使用しているんでしょうか?答えは、GetMessegeのフックらしい。AttachThreadInputでSetWindowLongPtrを使用して~別プロセスのウィンドウをサブクラス化できるか?と言うとこれは当然できない。~これは、呼び出し元のWndProcのアドレスが呼び出し先のプロセスで有効でないから。~しかし、これを可能にする方法がある。「DLL注入」と「APIフック」である。これを可能にする方法がある。「[[DLL注入>#qdb30fde]]」と「[[APIフック>#le7d156d]]」である。インジェクション~http://keicode.com/windows/win08.php>レジストリエントリを使用してDLLを注入する。~ただし、問題としては汎用性がない点が挙げられる。~DLLを注入してSetWindowLongPtrを実行する。[#le7d156d]別プロセスのウィンドウにGetMsgProcフックプロシージャをインストールしてメッセージを監視する。~GetMsgProcフックプロシージャは、前述のDLL注入のDLL中に実装しておく。[#p8555cb5]-フックタイプにはWH_GETMESSAGEを指定する。--メッセージキューへポストされた~メッセージを監視する1個のフックプロシージャをインストール。--フックは、メッセージ系を処理するものが多数である。~その他、シェル、デバッガ、CBT用途のフックなどがある。-関数ポインタには、GetMsgProcフックプロシージャを指定する。~詳細については、GetMsgProcフックプロシージャの説明を参照。関数がアプリケーションのメッセージキューからメッセージを取得するときに、必ずこの関数を呼び出す。---システムは、取得したメッセージを目的のウィンドウプロシージャへ渡す前に、そのメッセージをこのフックプロシージャに渡す。---GetMsgProcを実装したDLLは、呼出元プロセスと同じ位置にロードされるよう努力されるが、~同じ位置でなければ、システムが自動的に呼出先プロセスの関数アドレスを算出する。---これにより、「呼出先プロセス」でSetWindowLongPtrを実行できる。---(ただし本来の用途とは異なる。.etc~--また、フックチェーンを実装することもできる。Spyを使用すると、ウィンドウのメッセージの分析が可能。-イベントの順番を確認する。-欲しいイベントのメッセージが来ているか?調べる。-当該イベントではどのようなメッセージが来るのか?調べる。-SetCaptureにより別のマウスイベントが来てしまっていないか?確認する。-SendMessage/PostMessageどちらで送られてきているか?確認する。-当該ウィンドウの--ウィンドウ名を確認する。--ウィンドウクラス名を確認する。--ウィンドウ位置、大きさを確認する。値が変わっているかを確認する。[#i6a6c87b]-スレッドを明示的に起動して、そこからForm等、新規ウィンドウを生成する。-以下は、暗黙的に、別スレッドからウィンドウを起動する例。-[[async/await]]を使用する。-System.Timers.Timerを使用する。制度が高い反面、別スレッドで実行される。[#od2c91ab]ここまで、色々と説明してきたように、スレッド関係で色々な問題が発生する。***サーバーの同期呼出でUIがハングする。[#tfaa56f4]メッセージループを持つスレッドで同期呼出を行うと、~同一のスレッド(メッセージループ)を共有する全てのスレッドがハングする。そのため、サーバー呼出を別スレッドで処理を行うことがあるが、~以下の様な問題が発生するため、基本的に、[[UI処理はUIスレッドで処理するようにする。>#l3b4d072]]***モーダル・ダイアログがモーダルにならない。[#o4165f4d]バックグラウンド・スレッドから上げたモーダル・ダイアログがモーダルにならない。これは、UIスレッドと別スレッドのメッセージループが異なるので、~スレッド間のウィンドウはモーダル・ダイアログ的な動作にならないため。***IME制御がおかしくなる。[#p6d8f316]-IMEはスレッド単位の初期化が必要になる。-基本的には、[[UI処理は、UIスレッドから処理するようにする>#l3b4d072]]。-止む無く、別スレッドから処理する場合。[#l3b4d072]非同期処理が必要になっても、UI処理はUIスレッドで処理するようにする。それには以下の技術を使用できる。