マイクロソフト系技術情報 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 )
{
   ・・・
}

Windowsメッセージキューとメッセージループ

Windowsメッセージキュー

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:ウィンドウハンドルを持つ)。

Form的なもの

自前のウィンドウクラス名を持つウィンドウクラス(.NETで言う、Form的なもの)は、
WndProc?を用意して、WNDCLASS 構造体にまとめて、 RegisterClassEx?で登録する。
その時に 自前のウィンドウクラス名 を登録し、CreateWindowEXで、ウィンドウを作る。

そのイベントは、すべて、 WNDCLASS で登録した WndProc? へ来る(これについては後述)。

Control的なもの

あらかじめ登録されている既定のウィンドウクラスとしては

がある(.NETで言う、Control的なもの)。

これらは登録済みで個別用途のウィンドウクラスであるため
(例えばボタンウィンドウやエディットボックスなど)、
下記のRegisterClassEx?関数を使用せずに作成可能。

自前のウィンドウクラス(Form的な)の中へ、
既定のウィンドウクラス(Control的な)を貼り付けたければ、
自前で CreateWindow?す(自前のウィンドウクラス の hwnd,...)する。

ダイアログ

VC++のダイアログエディタで、ダイアログリソースを作ってCreateDialog?する。

ダイアログのイベントは、WndProc?ではなく、DlgProc? へ来る(これについては後述)。

ウィンドウプロシージャ

イベントハンドラみたいなもの。

以下のように、イベントハンドラを実装する。

WndProc?

//(2) ウィンドウプロシージャ
WndProc (
HWND hWnd,
UNIT message,
WPARAM wParam,
LPARAM lParam )
{
   ・・・
}

DlgProc?

???

サブクラス化

この既定のWndProc?を置き換えることで、
イベントの挙動(≠イベント・ハンドラ、ボタン押下時にボタンが凹む等)
をカスタマイズすることが可能である。

Win32PI

ウィンドウの生成

メッセージの送信

PostMessage

PostMessageはメッセージ・ループにキューイングする。  

SendMessage

SendMessageはウィンドウ・プロシージャ(WndProc?)を直接呼び出す。

以下は、SendMessageの同期呼出ハングの問題を回避するための関数(送信側)。

以下は、SendMessageの同期呼出ハングの問題を回避するための関数(受信側)。

メッセージ・ループで使用

GetMessage?

while (GetMessage (&msg,NULL,0,0)) { /* メッセージループ */
 TranslateMessage(&msg);
 DispatchMessage(&msg);
}

ウィンドウプロシージャで使用

WindowProc?

サブクラス化

ハードウェア入力モデル

関連するWin32API

フォーカス

アクティブ化

フォアグラウンド化

キー入力状態

カーソル入力状態

VIQの共有

については自分のものを使い続ける。

DLL注入とAPIフック

UIオートメーションの裏側。

ちなみに、操作ログの取得って

GetMessege?のフックと

AttachThreadInput?

の2つの方法がありそうですが、

現行はどっちを使用しているんでしょうか?

答えは、GetMessege?のフックらしい。

↓↓↓

例えばAttachThreadInput?SetWindowLongPtr?を使用して
別プロセスのウィンドウをサブクラス化できるか?と言うとこれは当然できない。
これは、呼び出し元のWndProc?のアドレスが呼び出し先のプロセスで有効でないから。

しかし、これを可能にする方法がある。「DLL注入」と「APIフック」である。

DLL注入

APIフック

別プロセスのウィンドウにGetMsgProc?フックプロシージャをインストールしてメッセージを監視する。
GetMsgProc?フックプロシージャは、前述のDLL注入のDLL中に実装しておく。

SetWindowsHookEx? 関数

http://msdn.microsoft.com/ja-jp/library/cc430103.aspx

フックタイプと関数ポインタ

スパイ

Spy++やWinspector Spyを使用すると、ウィンドウのメッセージの分析が可能。

Spy++

Winspector Spy

別スレッド

別スレッドでウィンドウが起動する例

発生する問題の例

ここまで、色々と説明してきたように、スレッド関係で色々な問題が発生する。

サーバーの同期呼出でUIがハングする。

メッセージループを持つスレッドで同期呼出を行うと、
同一のスレッド(メッセージループ)を共有する全てのスレッドがハングする。

そのため、サーバー呼出を別スレッドで処理を行うことがあるが、
以下の様な問題が発生するため、基本的に、UI処理はUIスレッドで処理するようにする。

モーダル・ダイアログがモーダルにならない。

バックグラウンド・スレッドから上げたモーダル・ダイアログがモーダルにならない。

これは、UIスレッドと別スレッドのメッセージループが異なるので、
スレッド間のウィンドウはモーダル・ダイアログ的な動作にならないため。

IME制御がおかしくなる。

UI処理をUIスレッドで処理する方法

非同期処理が必要になっても、UI処理はUIスレッドで処理するようにする。

それには以下の技術を使用できる。

Control.Invoke、.BeginInvoke

async/await

参考

書籍

msdn

標準 Windows API

Win32 API 階梯

Windowsプログラムの正しい雛形

WindowsAPI Programming

EternalWindows?

http://eternalwindows.jp/index.html


Tags: :Windows, :ウィンドウ・システム, :プログラミング


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS