「マイクロソフト系技術情報 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。
ウィンドウ・システムの詳細を理解して、
「バックグラウンド・スレッドから上げた
モーダル・ダイアログがモーダルにならない理由」
などのさまざまな挙動を理解できるようにする。
Windowsプログラムは、
から成り立つ。
そして(1)WinMain?の中は、
の4つのパートから成り立つ。
//(1)Winmain WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { // ウィンドウクラス構造体を設定 //(A) ウインドウクラスの登録 RegisterClassEx //(B) ウインドウを作成 CreateWindow //(C) ウインドウを表示 ShowWindow UpdateWindow //(D) メッセージループ } //(2) ウィンドウプロシージャ WndProc ( HWND hWnd, UNIT message, WPARAM wParam, LPARAM lParam ) { ・・・ }
MSMQではなく、ウィンドウ メッセージ用のキュー。
ウィンドウ・システム(WindowsのGUI)の根幹をなすメカニズム。
THREADINFO構造体 | |
項目 | APIとの関連 |
ポストメッセージ | PostMessage()で送信されるメッセージ |
送信メッセージキュー | SendMessage()で送信されるメッセージ、別スレッドの場合 |
応答メッセージキュー | SendMessage()で送信されるメッセージ、別スレッドの場合 |
仮想入力キュー(ハードウェア入力) | - |
ウェイクフラグ | PostQuitMessage?() |
nExitCode? | PostQuitMessage?() |
スレッドローカル・入力状態管理変数 | - |
後述のWin32APIを使用する。
などでも利用される。
GetMessage?関数でWindowsメッセージキューから取り出したMSG 構造体を
DispatchMessage?関数でウィンドウ・プロシージャに渡す。
while (GetMessage (&msg,NULL,0,0)) { /* メッセージループ */ TranslateMessage(&msg); DispatchMessage(&msg); }
.NETで言う、FormもControlも全てが
ウィンドウ(hwnd:ウィンドウハンドルを持つ)。
ウィンドウクラスとは、ウィンドウの雛形(テンプレート)のようなもので、
対応するウィンドウ関数・アイコン・カーソル・背景色など 基本的な情報が登録されている。
自前のウィンドウクラス名を持つウィンドウクラス(.NETで言う、Form的なもの)は、
WndProc?を用意して、WNDCLASS 構造体にまとめて、 RegisterClassEx?で登録する。
その時に 自前のウィンドウクラス名 を登録し、CreateWindowEXで、ウィンドウを作る。
そのイベントは、すべて、 WNDCLASS で登録した WndProc? へ来る(これについては後述)。
あらかじめ登録されている既定のウィンドウクラスとしては
がある(.NETで言う、Control的なもの)。
これらは登録済みで個別用途のウィンドウクラスであるため
(例えばボタンウィンドウやエディットボックスなど)、
下記のRegisterClassEx?関数を使用せずに作成可能。
自前のウィンドウクラス(Form的な)の中へ、
既定のウィンドウクラス(Control的な)を貼り付けたければ、
自前で CreateWindow?す(自前のウィンドウクラス の hwnd,...)する。
VC++のダイアログエディタで、ダイアログリソースを作ってCreateDialog?する。
ダイアログのイベントは、WndProc?ではなく、DlgProc? へ来る(これについては後述)。
イベントハンドラみたいなもの。
以下のように、イベントハンドラを実装する。
//(2) ウィンドウプロシージャ WndProc ( HWND hWnd, UNIT message, WPARAM wParam, LPARAM lParam ) { ・・・ }
???
この既定のWndProc?を置き換えることで、
イベントの挙動(≠イベント・ハンドラ、ボタン押下時にボタンが凹む等)
をカスタマイズすることが可能である。
ウィンドウクラスの属性を表します。
ウィンドウクラスを登録します。
メモリ上にウィンドウを生成します。
ウィンドウを表示します。
ウィンドウを更新します。
指定されたウィンドウを作成したスレッドの ID を取得します。
PostMessageはメッセージ・ループにキューイングする。
WM_NCLBUTTONDOWN メッセージなどを取得した場合に、
WM_QUIT メッセージをメッセージキューにポストする。
SendMessageはウィンドウ・プロシージャ(WndProc?)を直接呼び出す。
以下は、SendMessageの同期呼出ハングの問題を回避するための関数(送信側)。
以下は、SendMessageの同期呼出ハングの問題を回避するための関数(受信側)。
メッセージのデータを表します。
Windowsメッセージキューからメッセージを MSG 構造体として取得
GetMessage? 関数とは異なり、
メッセージキューにメッセージがなかった場合、
メッセージがポストされるのを待たずに制御を返す。
仮想キーメッセージを文字メッセージへ変換(ショートカットの変換)
GetMessage?関数で取得したMSG 構造体をウィンドウ・プロシージャに渡す。
while (GetMessage (&msg,NULL,0,0)) { /* メッセージループ */ TranslateMessage(&msg); DispatchMessage(&msg); }
当該スレッドのWindowsメッセージキューの状態確認
ウィンドウが Unicode のネイティブウィンドウであるかどうかをチェック
WndProc?のサブクラス化
元WndProc?にメッセージを送信
ローカル入力状態からフォーカスを持つウィンドウのHWNDを返す。
ローカル入力状態からアクティブなウィンドウのHWNDを返す。
HWND_TOPを指定しSetWindowPos?を呼び出す(RITに接続するVIQを変更可能)。
ウィンドウをアクティブにしてサイズ、位置、Zオーダを変更(RITに接続するVIQを変更可能)。
フォアグラウンド・ウィンドウのHWNDを返す。
指定したプロセスのスレッドがSetForgroundWindow?を呼び出すことを許可する。
他のSetForgroundWindow?呼出をロックする(Windowsスタートメニューなどで使用されている)。
マウス・カーソルのセット(砂時計など)
マウス・カーソルの表示・非表示
マウス・カーソルの範囲を指定する(非同期的なアクティブ化イベントで中止)。
上記のマウスのキャプチャを停止する。
複数のスレッドに複数のVIQとローカル入力状態を共有させる。
については自分のものを使い続ける。
AttachThreadInput? API関数で無理やり現在アクティブなスレッドに自分のスレッドをアタッチして、
その間にBringWindowToTop? API関数で自分のフォームを前面に出します。
その後もう一度AttachThreadInputAPI関数でデタッチして解除すれば可能です。
もちろん、Windowsの仕様に反しますが。
UIオートメーションの裏側。
ちなみに、操作ログの取得って
GetMessege?のフックと
AttachThreadInput?の
2つの方法がありそうですが、
現行はどっちを使用しているんでしょうか?
答えは、GetMessege?のフックらしい。
↓↓↓
例えばAttachThreadInput?でSetWindowLongPtr?を使用して
別プロセスのウィンドウをサブクラス化できるか?と言うとこれは当然できない。
これは、呼び出し元のWndProc?のアドレスが呼び出し先のプロセスで有効でないから。
しかし、これを可能にする方法がある。
レジストリエントリを使用してDLLを注入する。
ただし、問題としては汎用性がない点が挙げられる。
DLLを注入してSetWindowLongPtr?を実行する。
別プロセスのウィンドウにGetMsgProc?フックプロシージャをインストールしてメッセージを監視する。
GetMsgProc?フックプロシージャは、前述のDLL注入のDLL中に実装しておく。
http://msdn.microsoft.com/ja-jp/library/cc430103.aspx
・所定のプロセスに対する Windows メッセージの監視・捕捉
・所定のプロセスでの特定のイベントに呼応する自作コードの注入
・既存のアプリケーションの所作を変更 .etc
Spy++やWinspector Spyを使用すると、ウィンドウのメッセージの分析が可能。
ここまで、色々と説明してきたように、スレッド関係で色々な問題が発生する。
メッセージループを持つスレッドで同期呼出を行うと、
同一のスレッド(メッセージループ)を共有する全てのスレッドがハングする。
そのため、サーバー呼出を別スレッドで処理を行うことがあるが、
以下の様な問題が発生するため、基本的に、UI処理はUIスレッドで処理するようにする。
バックグラウンド・スレッドから上げたモーダル・ダイアログがモーダルにならない。
これは、UIスレッドと別スレッドのメッセージループが異なるので、
スレッド間のウィンドウはモーダル・ダイアログ的な動作にならないため。
非同期処理が必要になっても、UI処理はUIスレッドで処理するようにする。
それには以下の技術を使用できる。
http://eternalwindows.jp/index.html
Tags: :Windows, :ウィンドウ・システム, :プログラミング