レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API メッセージとメッセージ キューの概要

Microsoft(R) Windows(TM) 用に作成されたアプリケーションは、 今までのアプリケーションとは異なり、 (getcharなどの) 明示的な関数呼び出しで入力を取得せずに、 Windowsが入力を渡してくれるのを待ちます。

Windowsは、 アプリケーションに対するすべての入力を、 アプリケーションのさまざまなウィンドウに渡します。各ウィンドウには「ウィンドウ プロシージャ」と呼ばれる関数があり、 Windowsは、 ウィンドウに対する入力が発生するとこの関数を呼び出します。ウィンドウ プロシージャは入力を処理し、 Windowsに制御を返します。ウィンドウ プロシージャについて、 詳しくはウィンドウ プロシージャの概要を参照してください。

次に示すこのトピックでは、 メッセージやメッセージ キューとその使い方を説明します。

メッセージ

メッセージのディスパッチ

メッセージの処理

メッセージのポストと送信

メッセージの種類

メッセージのフィルタ処理

メッセージのデッドロック

メッセージ ループの作成

メッセージ キューの調査

メッセージのポスト

メッセージの送信

メッセージとメッセージ キュー関数

メッセージ

Windowsは、 入力を「メッセージ」としてウィンドウ プロシージャに渡します。メッセージは、 Windowsやアプリケーションが生成します。Windowsは、 ユーザーのキー入力、 マウスの移動、 スクロール バーなどのコントロールでのクリックが発生したときに、 各入力イベントごとにメッセージを生成します。また、 Windowsは、 アプリケーションがシステム フォント リソースのプールを変更したり、 ウィンドウのサイズを変更したときなど、 アプリケーションがシステムの状態を変更したときにもメッセージを生成します。アプリケーションは、 メッセージを生成して、 自分のウィンドウに作業を実行させたり、 ほかのアプリケーションと通信したりできます。

Windowsは、 パラメータを4つ持つメッセージをウィンドウ プロシージャに送ります。パラメータには、 ウィンドウ ハンドル、 メッセージ識別子および「メッセージ パラメータ」と呼ばれる2つの32ビット値があります。ウィンドウ ハンドルは、 メッセージの送り先のウィンドウを識別します。Windowsは、 ウィンドウ ハンドルを使って、 メッセージの送り先のウィンドウ プロシージャを判断します。

メッセージ識別子は、 メッセージの目的を識別する名前付き定数です。ウィンドウ プロシージャは、 メッセージを受け取ったら、 メッセージ識別子を使ってメッセージの処理方法を判断します。たとえば、 WM_PAINTメッセージ識別子は、 ウィンドウのクライアント領域が変更されたために再描画しなければならないことをウィンドウ プロシージャに伝えます。

メッセージ パラメータは、 ウィンドウ プロシージャがメッセージを処理するときに使うデータまたはデータの位置を示します。メッセージ パラメータの意味や値はメッセージによって異なります。メッセージ パラメータには、 整数、 パックされたビット フラグ、 パラメータに収まらないデータを含む構造体を指すポインタなどがあります。通常、 使われていないメッセージ パラメータには、 NULLが設定されます。ウィンドウ プロシージャは、 メッセージ識別子を調べて、 メッセージ パラメータの解釈方法を判断しなければなりません。

メッセージのディスパッチ

Windowsは、 2つの方法でメッセージをウィンドウ プロシージャにディスパッチします。1つは、 「メッセージ キュー」と呼ばれる先入れ先出しキューにメッセージをポストする方法です。メッセージ キューは、 システム定義のメモリ オブジェクトで、 メッセージを一時的に格納します。もう1つは、 ウィンドウ プロシージャにメッセージを直接送る方法です。

メッセージ キューにポストされたメッセージは、 「キュー メッセージ」と呼ばれます。通常、 キュー メッセージはマウスやキーボードによるユーザー入力によって発生し、 WM_MOUSEMOVE WM_LBUTTONDOWN WM_KEYDOWN WM_CHARなどがあります。キュー メッセージには、 このほかに、 タイマ メッセージ (WM_TIMER)、 描画メッセージ (WM_PAINT)、 終了メッセージ (WM_QUIT) があります。ウィンドウ プロシージャに直接送られるこれ以外のメッセージの多くは、 「キューなしメッセージ」と呼ばれます。

キュー メッセージ

Windowsは、 一度に複数のウィンドウを表示できます。Windowsは、 マウス入力やキーボード入力を適切なウィンドウにディスパッチするため、 メッセージ キューを使います。Windowsは、 1つのシステム メッセージ キューを管理し、 各スレッドごとにスレッド メッセージ キューを管理しています。

ユーザーによるマウスの移動、 マウス ボタンのクリック、 キーボード入力などが発生すると、 マウスやキーボードのデバイス ドライバは、 入力をメッセージに変換し、 システム メッセージ キューに置きます。Windowsは、 システム メッセージ キューからメッセージを1つずつ取り出して、 メッセージを調べて送り先ウィンドウを判断し、 送り先ウィンドウを作成したスレッドのメッセージ キューにポストします。スレッドのメッセージ キューは、 スレッドが作成したウィンドウのマウス入力やキーボード入力をすべて受け取ります。スレッドは、 キューからメッセージを取り出して、 適切なウィンドウ プロシージャにそのメッセージを送るようにWindowsに指示します。スレッドについて、 詳しくはプロセスとスレッドの概要を参照してください。

Windowsは、 WM_PAINTメッセージを除いて、 メッセージ キューの最後にメッセージをポストします。このため、 ウィンドウは、 入力を正しい先入れ先出しの順番で受け取れます。しかし、 WM_PAINTメッセージはキューに残され、 キューにほかのメッセージがなくなったときだけウィンドウ プロシージャに送られます。同じウィンドウに対する複数のWM_PAINTメッセージは1つのWM_PAINTメッセージに統合され、 クライアント領域の個々の無効部分は1つの領域にまとめられます。WM_PAINTメッセージをまとめることによって、 ウィンドウがクライアント領域を描画する回数が減ります。

システムは、 MSG構造体に値を設定してメッセージ キューにコピーすることによって、 メッセージをスレッドのメッセージ キューにポストします。MSG構造体の情報には、 メッセージの送り先のウィンドウのハンドル、 メッセージ識別子、 2つのメッセージ パラメータ、 メッセージがポストされた時刻、 マウス カーソルの位置があります。スレッドが自分のメッセージ キューやほかのスレッドのメッセージ キューにメッセージをポストするには、 PostMessage関数かPostThreadMessage関数を使います。

キューからメッセージを取り出すには、 GetMessage関数を使います。メッセージをキューから取り出さずに調べるには、 PeekMessage関数を使います。この関数は、 メッセージに関する情報をMSG構造体に設定します。

キューからメッセージを取り出したら、 DispatchMessage関数を使って、 メッセージをウィンドウ プロシージャに送るようにWindowsに指示します。DispatchMessageには、 以前にGetMessage関数かPeekMessage関数を呼び出して設定したMSG構造体を指すポインタを指定します。DispatchMessageは、 ウィンドウ ハンドル、 メッセージ識別子、 2つのメッセージ パラメータをウィンドウ プロシージャに渡しますが、 時刻やマウス カーソルの位置は渡しません。この情報を取得するには、 メッセージを処理するときに、 GetMessageTime関数やGetMessagePos関数を呼び出してください。

キューなしメッセージ

キューなしメッセージは、 システム メッセージ キューやスレッド メッセージ キューを通さずに、 送り先のウィンドウ プロシージャに直接送られます。通常、 Windowsは、 ウィンドウ自体に関するイベントをウィンドウに通知するときにキューなしメッセージを送ります。たとえば、 ユーザーが新しいアプリケーション ウィンドウをアクティブ化すると、 Windowsは、 WM_ACTIVATEWM_SETFOCUSWM_SETCURSORなどの一連のメッセージをウィンドウに送ります。これらのメッセージは、 それぞれ、 ウィンドウがアクティブ化されたこと、 キー入力の対象がこのウィンドウになったこと、 マウス カーソルがウィンドウの境界内に入ったことをウィンドウに通知します。また、 キューなしメッセージは、 アプリケーションが特定のWindows関数を呼び出したときにも送られます。たとえば、 アプリケーションがSetWindowPos関数を使ってウィンドウを移動すると、 Windowsは、 WM_WINDOWPOSCHANGEDメッセージを送ります。

メッセージを送るには、 SendMessage関数、 SendNotifyMessage関数、 SendDlgItemMessage関数のいずれかを使います。メッセージの送信について詳しくは、 メッセージの送信を参照してください。

メッセージの処理

アプリケーションは、 自分のスレッドのメッセージ キューにポストされたメッセージを取り出して処理しなければなりません。通常、 単一スレッド アプリケーションは、 WinMain関数内の「メッセージ ループ」を使って、 メッセージを取り出し、 適切なウィンドウ プロシージャにメッセージを送って処理させます。ここでは、 メッセージ ループの動作と、 ウィンドウ プロシージャの役割について説明します。

メッセージ ループ

1つのメッセージ ループは、 GetMessage TranslateMessageDispatchMessageの各関数を一度ずつ呼び出すことによって構成されます。GetMessage関数は、 キューからメッセージを取得して、 MSG構造体にコピーします。WM_QUITメッセージがなければ、 GetMessageはTRUEを返します。WM_QUITメッセージがあればFALSEを返し、 ループを終了します。通常、 単一スレッド アプリケーションでは、 メッセージ ループの終了はアプリケーションの終了の第一段階です。アプリケーションが自分自身でループを終了するには、 PostQuitMessage関数を使います。通常、 この関数は、 アプリケーションのメイン ウィンドウのウィンドウ プロシージャでWM_DESTROYメッセージを処理するときに呼び出します。

GetMessageにウィンドウ ハンドルを指定すると、 指定したウィンドウに対するメッセージだけがキューから取得されます。また、 GetMessageは、 キュー内のメッセージをフィルタして、 指定した範囲のメッセージだけを取得することもできます。メッセージのフィルタ処理について詳しくは、 メッセージのフィルタ処理を参照してください。

スレッドがキーボードからの文字入力を受け付ける場合、 スレッドのメッセージ ループでは、 TranslateMessageを呼び出さなければなりません。ユーザーがキーを押すたびに、 Windowsは仮想キー メッセージ (WM_KEYDOWNWM_KEYUP) を生成します。仮想キー メッセージは、 押されたキーの文字値ではなくキーを識別する仮想キー コードを示します。文字値を取得するには、 メッセージ ループでTranslateMessageを呼び出して、 仮想キー メッセージを文字メッセージ (WM_CHAR) に変換し、 アプリケーション メッセージ キューに戻してください。そして、 メッセージ ループの後の繰り返しで文字メッセージを取り出し、 ウィンドウ プロシージャにディスパッチしてください。

DispatchMessage関数は、 MSG構造体に指定されているウィンドウ ハンドルに対応するウィンドウ プロシージャにメッセージを送ります。ウィンドウ ハンドルがHWND_TOPMOSTならば、 DispatchMessageは、 システムのすべてのトップ レベル ウィンドウのウィンドウ プロシージャにメッセージを送ります。ウィンドウ ハンドルがNULLならば、 DispatchMessageはメッセージに関しては何もしません。

アプリケーションのメイン スレッドは、 メッセージ ループを開始する前に、 アプリケーションを初期化し、 ウィンドウを少なくとも1つ作成します。メッセージ ループは、 いったん開始すると、 スレッドのメッセージ キューからメッセージを取得して適切なウィンドウにディスパッチする作業を続けます。GetMessage関数がメッセージ キューからWM_QUITメッセージを取得すると、 メッセージ ループは終了します。

アプリケーションが複数のウィンドウを持つ場合でも、 1つのメッセージ キューに必要なメッセージ ループは1つだけです。キュー内の各メッセージはメッセージが属するウィンドウのハンドルを含むMSG構造体であるため、 DispatchMessageは、 メッセージを必ず適切なウィンドウにディスパッチできます。

アプリケーションのメッセージ ループはさまざまに修正できます。たとえば、 キューから取り出したメッセージをウィンドウにディスパッチしないようにもできます。これは、 ウィンドウを指定しないメッセージをポストするアプリケーションで役立ちます (このようなメッセージのウィンドウ ハンドルはNULLであるため、 特定のウィンドウではなくアプリケーションに適用されます)。また、 キュー内の特定のメッセージだけを検索してほかのメッセージは残しておくようにGetMessageに指示することもできます。これは、 メッセージ キューの通常の先入れ先出しの順番を一時的に変更したいときに役立ちます。

アクセラレータ キーを使うアプリケーションは、 キーボード メッセージをコマンド メッセージに変換しなければなりません。キーボード メッセージをコマンド メッセージに変換するには、 メッセージ ループでTranslateAccelerator関数を呼び出してください。アクセラレータ キーについて、 詳しくはキーボード アクセラレータの概要を参照してください。

ウィンドウ プロシージャ

ウィンドウ プロシージャとは、 ウィンドウに送られたメッセージをすべて受け取って処理する関数です。どのウィンドウ クラスにもウィンドウ プロシージャが1つあり、 同じクラスで作成したウィンドウは、 すべて、 同じウィンドウ プロシージャを使ってメッセージに応答します。

システムは、 ウィンドウ プロシージャにメッセージを送るとき、 プロシージャの引数としてメッセージ データを渡します。ウィンドウ プロシージャは、 メッセージに応じた動作を実行します。ウィンドウ プロシージャは、 メッセージ識別子を調べて、 メッセージ パラメータが示す情報を使ってメッセージを処理します。

通常、 ウィンドウ プロシージャは、 メッセージを無視しません。メッセージを処理しないときは、 システムにメッセージを返して、 デフォルトの処理を行わせなければなりません。メッセージのデフォルト処理を行うには、 DefWindowProc関数を呼び出します。この関数は、 デフォルト動作を実行し、 メッセージの結果を返します。ウィンドウ プロシージャは、 この値を自分自身のメッセージの結果として返さなければなりません。通常、 ウィンドウ プロシージャは、 一部のメッセージだけを処理して、 処理しないメッセージはDefWindowProcを呼び出してシステムに渡します。

ウィンドウ プロシージャは同じクラスに属するウィンドウすべてで共有されるため、 いくつかの異なるウィンドウのメッセージを処理する場合があります。メッセージが影響する特定のウィンドウを識別するには、 メッセージで渡されたウィンドウ ハンドルを調べてください。ウィンドウ プロシージャについて、 詳しくはウィンドウ プロシージャの概要を参照してください。

メッセージのポストと送信

アプリケーションは、 メッセージをポストしたり送信できます。アプリケーションは、 システムと同様に、 メッセージをメッセージ キューにコピーすることによってメッセージをポストしウィンドウ プロシージャに引数としてメッセージ データを渡すことによってメッセージを送ることができます。メッセージをポストしたり送信するには、 PostMessage関数やSendMessage関数を使います。

通常、 アプリケーションは、 処理を実行するように特定のウィンドウに指示するためにメッセージをポストします。PostMessageは、 メッセージのMSG構造体を作成し、 メッセージをメッセージ キューにコピーします。アプリケーションのメッセージ ループは、 このメッセージを受け取って、 適切なウィンドウ プロシージャにディスパッチします。

通常、 アプリケーションは、 すぐに処理を実行するようにウィンドウ プロシージャに指示するためにメッセージを送ります。SendMessage関数は、 指定されたウィンドウに対応するウィンドウ プロシージャにメッセージを送ります。この関数は、 ウィンドウ プロシージャが処理を完了するまで待ち、 メッセージの結果を返します。親ウィンドウと子ウィンドウは、 互いにメッセージを送りあうことによって通信できます。たとえば、 エディット コントロールを子ウィンドウとして持つ親ウィンドウは、 メッセージをそのコントロールに送ることによってコントロールのテキストを設定できます。コントロールは、 親ウィンドウにメッセージを送ることによって、 ユーザーがテキストを変更したことを親ウィンドウに通知できます。

場合によっては、 システムのすべてのトップ レベル ウィンドウにメッセージを送信またはポストしなければならないことがあります。たとえば、 システム時刻を変更したら、 WM_TIMECHANGEを送って、 変更をトップ レベル ウィンドウすべてに通知しなければなりません。すべてのトップ レベル ウィンドウにメッセージを送信またはポストするには、 hwndパラメータにHWND_TOPMOSTを指定してSendMessage関数かPostMessage関数を呼び出します。

アプリケーションは、 ウィンドウを指定せずにメッセージをポストできます。PostMessageを呼び出すときにNULLのウィンドウ ハンドルを指定すると、 メッセージは、 現在のスレッドに関連付けられているキューにポストされます。ウィンドウ ハンドルが指定されていないため、 アプリケーションは、 メッセージ ループでメッセージを処理しなければなりません。特定のウィンドウではなくアプリケーション全体に適用されるメッセージを作成するには、 この方法を使います。

ウィンドウ プロシージャは、 InSendMessage関数を使うことによって、 処理しているメッセージがほかのスレッドから送られたものかどうかを判断できます。これは、 メッセージの送りもとによってメッセージの処理が異なる場合に役立ちます。

よくあるプログラミング上の間違いは、 PostMessage関数が必ずメッセージをポストすると仮定することです。この関数は、 メッセージ キューがいっぱいならばメッセージをポストしません。アプリケーションは、 PostMessage関数の戻り値を調べて、 メッセージがポストされたかどうかを判断し、 ポストされていなければ、 もう一度ポストしてください。

メッセージの種類

ここでは、 システム定義とアプリケーション定義の2種類のメッセージについて説明します。

システム定義メッセージ

システムは、 システム定義メッセージを使って、 アプリケーションの動作を制御し、 アプリケーションが処理する入力などの情報を提供します。システムは、 アプリケーションと通信するときにシステム定義メッセージを送信またはポストします。また、 アプリケーションがシステム定義メッセージを送ったりポストすることもできます。通常、 アプリケーションは、 システム定義メッセージを使って、 登録済みウィンドウ クラスで作成したコントロール ウィンドウを制御します。

各システム メッセージには一意なメッセージ識別子と、 対応するシンボル定数があります (シンボル定数はWindowsのヘッダー ファイルで定義されており、 メッセージの用途を示しています)。たとえば、 WM_PAINT定数は、 ウィンドウの内容を描画するようにウィンドウに要求します。

シンボル定数は、 システム定義メッセージが属するカテゴリを示します。定数のプリフィックスは、 そのメッセージを解釈して処理できるウィンドウの種類を示します。プリフィックスとそれに関連するメッセージ カテゴリを次に示します。

プリフィックス メッセージ カテゴリ

BM ボタン コントロール

CB コンボ ボックス コントロール

DM デフォルト プッシュ ボタン コントロール

EM エディット コントロール

LB リスト ボックス コントロール

SBM スクロール バー コントロール

WM 一般のウィンドウ

一般ウィンドウ メッセージは、 マウス入力やキーボード入力、 メニューやダイアログ ボックスの入力、 ウィンドウの作成や管理、 動的データ交換 (DDE) など、 さまざまな情報や要求に使われます。

アプリケーション定義メッセージ

アプリケーションは、 自分自身のウィンドウで使うメッセージや、 ほかのプロセスのウィンドウと通信するためのメッセージを作成できます。アプリケーションが自分自身のメッセージを作成する場合、 そのメッセージを受け取るウィンドウ プロシージャは、 メッセージを解釈して適切な処理を行わなければなりません。

Windowsは、 0x0000から0x03FF (WM_USERの値-1) までと0x8000から0xBFFFまでのシステム定義メッセージのメッセージ識別子の値を予約しています。アプリケーションは、 これらの値をプライベート メッセージに使うことはできません。

0x0400 (WM_USERの値) から0x7FFFまでの値は、 アプリケーションが自分用に定義するメッセージ識別子で利用できます。0xC000から0xFFFFまでの値は、 アプリケーションがほかのアプリケーションのウィンドウと通信するために定義するメッセージ識別子で利用できます。

アプリケーションがRegisterWindowMessage関数を呼び出してメッセージを登録すると、 Windowsは、 0xC000から0xFFFFまでのメッセージ識別子を返します。この関数が返すメッセージ識別子は、 システム全体で一意であることが保証されています。ほかのアプリケーションのウィンドウと通信するためにメッセージを作成するアプリケーションは、 RegisterWindowMessageを使ってメッセージを登録してください。この関数を使うことによって、 ほかのアプリケーションが同じメッセージ識別子を別の目的に使うといった衝突が発生しません。

メッセージのフィルタ処理

メッセージ キューから特定のメッセージだけを取り出してそれ以外のメッセージを無視するには、 メッセージ フィルタをGetMessage関数かPeekMessage関数に指定します。メッセージ フィルタは、 (最初の識別子と最後の識別子で指定する) メッセージ識別子やウィンドウ ハンドルの範囲です。GetMessagePeekMessageは、 メッセージ フィルタを使って、 キューから取得するメッセージを選択します。メッセージ フィルタは、 メッセージ キューに後から届いたメッセージを検索するときに役立ちます。

メッセージをフィルタするアプリケーションは、 メッセージ フィルタの条件を満たすメッセージがポストされるようにしなければなりません。たとえば、 キーボード入力を受け取らないウィンドウでWM_CHARメッセージをフィルタすると、 GetMessage関数は戻りません。この場合、 アプリケーションはハングしてしまいます。

キーボードやマウス、 DDEのメッセージをフィルタするには、 それぞれ、 WM_KEYFIRST定数とWM_KEYLAST定数、 WM_MOUSEFIRST定数とWM_MOUSELAST定数、 WM_DDE_FIRST定数とWM_DDE_LAST定数を使ってください。

メッセージのデッドロック

SendMessageを呼び出してほかのスレッドにメッセージを送ったスレッドの実行は、 メッセージを受け取ったウィンドウ プロシージャが戻るまで中断します。受け取り側のスレッドがメッセージの処理中に制御を譲渡すると、 送り側のスレッドはSendMessageが戻るのを待ち続けるため、 実行を継続できません。この状況は、 「デッドロック」と呼ばれます。受け取り側のスレッドは、 明示的に制御を譲渡する必要はありません。次に示す関数は、 スレッドの制御を譲渡する可能性があります。

DialogBox

DialogBoxIndirect

DialogBoxIndirectParam

DialogBoxParam

GetMessage

MessageBox

PeekMessage

ウィンドウ プロシージャが受け取ったメッセージがほかのスレッドから送られたものかどうかを判断するには、 InSendMessage関数を呼び出します。ウィンドウ プロシージャは、 メッセージの処理時に上記の関数を呼び出すとき、 まず、 InSendMessageを呼び出してください。この関数がTRUEを返したときは、 スレッドの制御を譲渡する関数を呼び出す前に、 ReplyMessage関数を呼び出してください。

メッセージとメッセージ キューの使用

次に示すトピックをクリックすると、 そのトピックの実行方法が表示されます。

メッセージ ループの作成

メッセージ キューの調査

メッセージのポスト

メッセージの送信

メッセージ ループの作成

Windowsは、 各スレッドのメッセージ キューを自動的に作成します。スレッドがウィンドウを作成するときは、 スレッドのメッセージ キューからメッセージを取得して適切なウィンドウ プロシージャにディスパッチするメッセージ ループを用意しなければなりません。

Windowsはメッセージをアプリケーションの個々のウィンドウに送るため、 スレッドは、 ウィンドウを少なくとも1つ作成してからメッセージ ループを開始しなければなりません。通常、 Windows用アプリケーションには、 ウィンドウを作成するスレッドが1つあります。通常のアプリケーションは、 メイン ウィンドウのウィンドウ クラスを登録し、 メイン ウィンドウを作成して表示してから、 メッセージ ループを開始します。この作業はすべてWinMain関数で行います。

メッセージ ループを作成するには、 GetMessage関数とDispatchMessage関数を使います。アプリケーションがユーザーからの文字入力を取得するには、 ループでTranslateMessage関数を呼び出してください。TranslateMessageは、 仮想キー メッセージを文字メッセージに変換します。次の例は、 簡単なWindows用アプリケーションのWinMain関数のメッセージ ループを示しています。

HINSTANCE hinst;
HWND hwndMain;

int APIENTRY WinMain(hInstance, hPrevInstance, lpszCmdLine,
nCmdShow)
HINSTANCE hInstance;
HINSTANCE hPrevInstance;
LPSTR lpszCmdLine;
int nCmdShow;
{
MSG msg;
WNDCLASS wc;
UNREFERENCED_PARAMETER(lpszCmdLine);

/* Register the window class for the main window. */

if (!hPrevInstance) {
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName ="MainMenu";
wc.lpszClassName = "MainWndClass";

if (!RegisterClass(&wc))
return FALSE;
}

hinst = hInstance;/* saves handle of application instance */

/* Create the main window. */

hwndMain = CreateWindow("MainWndClass", "Sample",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
(HMENU) NULL, hinst, (LPVOID) NULL);

/*
* If the main window cannot be created, terminate
* the application.
*/

if (!hwndMain)
return FALSE;

/* Show the window and paint its contents. */

ShowWindow(hwndMain, nCmdShow);
UpdateWindow(hwndMain);

/* Start the message loop. */

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

/* Return the exit code to Windows. */

return msg.wParam;
}

GetMessage関数、 TranslateMessage関数、 DispatchMessage関数は、 MSG構造体を指すポインタをパラメータに取ります。メッセージが利用可能ならば、 GetMessageは、 メッセージをMSG構造体にコピーします。メッセージが (WM_KEYDOWNWM_SYSKEYDOWNなどの) 仮想キー メッセージならば、 TranslateMessageは、 文字メッセージ (WM_CHARWM_SYSCHAR) を生成してメッセージ キューに置きます。DispatchMessageは、 ウィンドウ プロシージャの引数としてMSG構造体のメンバを使いますが、 ウィンドウ プロシージャがメッセージ処理を完了するまで戻りません。

アクセラレータをサポートするスレッドは、 メッセージ ループでTranslateAccelerator関数を呼び出さなければなりません。この関数は、 スレッドのアクセラレータ テーブルのエントリにキーの組み合わせが一致するか調べます。キーとエントリが一致する場合、 TranslateAcceleratorは、 キーの組み合わせをWM_COMMANDメッセージに変換し、 ウィンドウ プロシージャにディスパッチします。

モードレス ダイアログ ボックスを使うスレッドは、 メッセージ ループでIsDialogMessage関数を呼び出して、 ダイアログ ボックスがキーボード入力を受け取れるようにしなければなりません。

次の例は、 アクセラレータを使用し、 モードレス ダイアログ ボックスを表示するスレッドのメッセージ ループを示しています。TranslateAcceleratorIsDialogMessageTRUEを返したとき (メッセージが処理されたことを示します) は、 TranslateMessageDispatchMessageを呼び出しません。これは、 TranslateAcceleratorIsDialogMessageが、 メッセージに必要な変換とディスパッチをすべて行うためです。

HWND hwndMain;
HWND hwndDlgModeless = NULL;
MSG msg;
HACCEL haccel;
.
. /* Perform initialization and create a main window. */
.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
if (hwndDlgModeless == (HWND) NULL ||
!IsDialogMessage(hwndDlgModeless, &msg) &&
!TranslateAccelerator(hwndMain, haccel, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

メッセージ キューの調査

アプリケーションは、 スレッドのメッセージ ループの外でスレッドのメッセージ キューの内容を調べなければならない場合があります。たとえば、 アプリケーションのウィンドウ プロシージャが時間のかかる描画操作を実行する場合、 ユーザーが操作に割り込めるようにしなければなりません。時間のかかる操作中にアプリケーションがメッセージ キューのマウス メッセージやキーボード メッセージを調べなければ、 操作が完了するまでユーザーの入力に応答できなくなります。これは、 スレッドのメッセージ ループで呼び出されるDispatchMessage関数が、 ウィンドウ プロシージャがメッセージ処理を完了するまで戻らないためです。

時間のかかる操作のときにメッセージ キューを調べるには、 PeekMessage関数を使います。PeekMessageは、 GetMessage関数に似ています。どちらの関数も、 フィルタに一致するメッセージがメッセージ キューにあるか調べて、 メッセージをMSG構造体にコピーします。この2つの関数の大きな違いは、 フィルタに一致するメッセージがキューに置かれるまでGetMessageは戻らないのに対し、 PeekMessageはキューにメッセージがあるかどうかにかかわらずすぐに戻る点です。

次の例は、 時間のかかる操作で、 PeekMessageを使ってメッセージ キューにマウス クリックやキーボード入力があるか調べます。

HWND hwnd;
BOOL fDone;
MSG msg;

/*
* Begin the operation and continue until it is complete
* or until the user clicks the mouse or presses a key.
*/

fDone = FALSE;
while (!fDone) {
fDone = DoLengthyOperation(); /* application-defined */

/*
* Remove any messages that may be in the queue. If the
* queue contains any mouse or keyboard
* messages, end the operation.
*/

while (PeekMessage(&msg, hwnd,0, 0, PM_REMOVE)) {
switch(msg.message) {
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
.
. /* Perform any required cleanup. */
.
fDone = TRUE;
}
}
}

また、 GetQueueStatusGetInputStateなどの関数を使ってスレッドのメッセージ キューの内容を調べることもできます。GetQueueStatusは、 キューにあるメッセージの種類を示すフラグの配列を返します。キューにメッセージがあるか調べるには、 この方法が最も高速です。GetInputStateは、 キューにマウス メッセージかキーボード メッセージがあればTRUEを返します。この2つの関数は、 どちらも、 処理しなければならないメッセージがキューにあるか調べるのに使えます。

メッセージのポスト

メッセージ キューにメッセージをポストするには、 PostMessage関数を使います。PostMessageは、 スレッドのメッセージ キューの最後にメッセージを置いて、 そのメッセージをスレッドが処理するのを待たずに、 すぐに戻ります。関数のパラメータには、 ウィンドウ ハンドル、 メッセージ識別子、 2つのメッセージ パラメータがあります。Windowsは、 これらのパラメータをMSG構造体にコピーし、 timeメンバとptメンバに値を設定して、 構造体をメッセージ キューに置きます。

Windowsは、 PostMessageで渡されたウィンドウ ハンドルを使って、 メッセージを受け取るスレッド メッセージ キューを判断します。ハンドルがHWND_TOPMOSTならば、 Windowsは、 すべてのトップ レベル ウィンドウのスレッド メッセージ キューにメッセージをポストします。

特定のスレッド メッセージ キューにメッセージをポストするには、 PostThreadMessage関数を使います。PostThreadMessagePostMessageに似ていますが、 第1パラメータはウィンドウ ハンドルではなくスレッド識別子です。スレッド識別子を取得するには、 GetCurrentThreadId関数を呼び出してください。

メッセージ ループを終了するには、 PostQuitMessage関数を使います。PostQuitMessageは、 現在実行中のスレッドにWM_QUITメッセージをポストします。スレッドのメッセージ ループは、 WM_QUITメッセージを受け取ると、 終了して制御をWindowsに返します。通常、 アプリケーションは、 次の例に示すように、 WM_DESTROYメッセージに対してPostQuitMessageを呼び出します。

case WM_DESTROY:
.
. /* Perform cleanup tasks. */
.
PostQuitMessage(0);
break;

メッセージの送信

メッセージをウィンドウ プロシージャに直接送るには、 SendMessage関数を使います。SendMessageは、 ウィンドウ プロシージャを呼び出して、 そのプロシージャがメッセージを処理し終わるまで待ち、 結果を返します。

メッセージは、 システム内の任意のウィンドウに送ることができます。必要なのはウィンドウ ハンドルだけです。Windowsは、 ウィンドウ ハンドルを使って、 メッセージを受け取るウィンドウ プロシージャを判断します。

ほかのスレッドが送ったメッセージを処理するときにウィンドウ プロシージャが制御を譲渡すると、 メッセージのデッドロックが発生する可能性があります (メッセージのデッドロックについて詳しくは、 メッセージのデッドロックを参照してください)。ウィンドウ プロシージャは、 ほかのスレッドから送られた可能性があるメッセージを処理する前に、 InSendMessage関数を呼び出してください。この関数がTRUEを返した場合、 ウィンドウ プロシージャは、 次の例に示すように、 スレッドの制御を譲渡する関数を呼び出す前に、 ReplyMessageを呼び出してください。

case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);

DialogBox(hInst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;

ダイアログ ボックスのコントロールには、 さまざまなメッセージが送られます。コントロール メッセージは、 コントロールの外観や動作、 内容を設定したり、 コントロールに関する情報を取得します。たとえば、 CB_ADDSTRINGメッセージはコンボ ボックスに文字列を追加し、 BM_SETCHECKメッセージはチェック ボックスやオプション ボタンのチェック状態を設定します。

コントロールにメッセージを送るには、 コントロールの識別子とコントロールを含むダイアログ ボックス ウィンドウのハンドルを指定してSendDlgItemMessage関数を呼び出します。次に示すダイアログ ボックス プロシージャの一部は、 コンボ ボックスのエディット コントロールからリスト ボックスに文字列をコピーします。この例では、 SendDlgItemMessageを使って、 コンボ ボックスにCB_ADDSTRINGメッセージを送っています。

HWND hwndCombo;
int cTxtLen;
PSTR pszMem;

switch (uMsg) {

case WM_COMMAND:
switch (LOWORD(wParam)) {

case IDD_ADDCBITEM:

/*
* Get the handle of the combo box and the
* length of the string in the edit control of
* the combo box.
*/

hwndCombo = GetDlgItem(hwndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hwndCombo);

/*
* Allocate memory for the string and copy
* the string into the memory.
*/

pszMem = (PSTR) VirtualAlloc((LPVOID) NULL,
(DWORD) (cTxtLen + 1), MEM_COMMIT,
PAGE_READWRITE);
GetWindowText(hwndCombo, pszMem, cTxtLen + 1);

/*
* Add the string to the list box of the
* combo box and remove the string from the
* edit control of the combo box.
*/

if (*pszMem != NULL) {
SendDlgItemMessage(hwndDlg, IDD_COMBO,
CB_ADDSTRING, 0,
(DWORD) ((LPSTR) pszMem));
SetWindowText(hwndCombo, (LPSTR) NULL);
}

/* Free the memory and return. */

VirtualFree(pszMem, 0, MEM_RELEASE);
return TRUE;
.
. /* Process other dialog box commands. */
.

}
.
. /* Process other dialog box messages. */
.

}

メッセージとメッセージ キュー関数

メッセージとメッセージ キューに関する関数を次に示します。

DefWindowProc

DispatchMessage

GetInputState

GetMessage

GetMessageExtraInfo

GetMessagePos

GetMessageTime

GetQueueStatus

InSendMessage

PeekMessage

PostMessage

PostQuitMessage

PostThreadMessage

RegisterWindowMessage

ReplyMessage

SendAsyncProc

SendMessage

SendNotifyMessage

TranslateMessage

WaitMessage

使われなくなったか、 または削除された関数

SetMessageQueue

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします
inserted by FC2 system