[level4] [ウィンドウ] [ HWND ] [ HINSTANCE ] [ MSG構造体] [Win32 データ型] [コールバック関数]


ウィンドウハンドルの実際
"HWND 型" の変数

 

 "HWND" は、ウィンドウハンドル型のデータ型(型名)です。
     役割からは宣言シンボルであり、宣言子です。
 "ウィンドウハンドル" はそれぞれ1つのウィンドウを表す「値」です。

 "ウィンドウ" は・・・
[こちら]
  親ウィンドウを持たない "トップレベルのウィンドウ" 及び 、"ダイアログボックス"
   つまり、"親ウィンドウ自身" には、"コントロールID" はありません。
  コントロールID から ウィンドウハンドル を得るには
GetDlgItem() 関数を使います。

 

スケルトンコード内のウィンドウハンドル

 スケルトンコード では、現れるウィンドウは結局一つきりなので、ウィンドウハンドル一般についてここで述べるのは少し苦しいところです。

 

 スケルトンコードでウィンドウハンドルが実際に必要になっているのは、ShowWindow( hWnd, nCmdShow ) ; のあたりだけです。ウィンドウハンドルは関数にとって必要です。スケルトンコードでは、ローカル(な)変数 "hWnd" を確保して使っていますが、その部分 (スコープ※) でしか ウィンドウハンドルを "使う" 必要がないためです。つまり、関数をまたがって (スコープをまたがって) 変数を使用する場合には是非、グローバル(な)変数を用意して使います。

 この例のスケルトンコードでは、一番親(※)のウィンドウハンドルを "グローバル変数" に保存することはしていませんが、HINSTANCE hAppInst ; にならべて、 HWND hAppWnd ; のようにしてグローバル変数を確保して(ウィンドウ作成直後にはハンドル値を代入して)おくことは、とくにこれから作り込む予定のアプリケーションなら、ぜひお勧めできることです。あとあとの混乱のたねになることもないし、そうしない方がかえって面倒になることがあります。

 ※ if 文の { や、関数の始まりを表す { も含め、{ ... } で囲まれた範囲は、一つのスコープです。ただし、変数の初期化における構文での { } に関しては例外で、スコープではありません。(ローカルとグローバル)

 ※ 変数名(識別子) "hWnd" に関して。この名前のつけ方は任意です。ウィンドウの種類によって、HWND hEdit ; のようにすることもあるし、hButton や hParent 、hWndEdit ということもあります。ダイアログボックスなら hDlg とすることもあるでしょう。"ハンドル(handle)" の意味を込めて付けられる先頭の h があり、その次が大文字となっているこのような独特のネーミングの仕方を、ハンガリー表記といいます。この "hWnd" という識別子名はとくにローカル変数としては、ごく頻繁に使われます。

 ※ HWND 型というデータ型はもちろん Windows API 専用の "データ型" としてデザイン(意図)されているものです。

 ※ あるウィンドウに、子ウィンドウとして他のウィンドウを貼り付けたりします。だから、親ウィンドウ、子ウィンドウのような呼び方を使います。

 

 ウィンドウを操作するための、車で言うハンドルだと考えてもそんなに間違いはないでしょう。ただ、そのハンドルは回したりしないで、関数に渡します。

 

 


ウィンドウハンドルの実際

 

 HWND はウィンドウハンドル型のデータ型なので、それではウィンドウハンドルの説明をすることにしましょう。個人的な感覚ですが、ウィンドウハンドルは、"取得" して "使用" ができれば "実際" としてはすべてです。つまりウィンドウハンドル恐るるに足らず。少し長くなったのが自分でも納得がいっていませんが、特に太い関数に注目です。たかだか数個です。

 

 ウィンドウハンドルの取得 : 取得の仕方(局面)

 ウィンドウのなんたるかも漠然としているうちから、ウィンドウハンドルの取得もなにもあったものではありませんが、述べておきたいことがあります。特に、とにかくまずはウィンドウハンドルを使ってみるべし、という人にとっては、それではどうやって取得するかが次の関心事であるはずです。

 取得の仕方は、大げさに考えなくても、もっとも常識的には、自分でウィンドウ(コントロールやダイアログボックスを含め) をまず "作成" し、そのときにそのウィンドウのウィンドウハンドルを得る、というのが基本的です。

 

 [一番あたりまえの取得方法]
 CreateWindow() 関数や CreateDialog() 関数などの ウィンドウ作成関数 は、ウィンドウ作成及び初期化後、戻り値として ウィンドウハンドルを返すので、必要なら、その戻り値を HWND 型の変数に代入します。すなわち、 HWND hMyEdit = CreateWindow( "EDIT" ... ) ; 。また、ウィンドウ作成関数を使うときは、戻り値が ウィンドウハンドル型を表す HWND 型であることを確認しましょう。

 [取得方法というならこれも含まれる]
 また、ウィンドウプロシージャやダイアログプロシージャの中では、プロシージャ
(といっても関数ですが) の引数として、プロシージャ内で ウィンドウハンドルを受け取ることができます。ウィンドウプロシージャを複数のウィンドウから共有(共用) していなければ、受け取る(渡される)ウィンドウハンドル hWnd の値は、 "いつも" 同じそのウィンドウのハンドル値です。

 [その他いろいろの取得法]
 このほか、コントロールID(親ウィンドウのハンドルも併せて必要) を指定して、ウィンドウ(子ウィンドウ)のハンドルを取得
したり、画面上の座標を指定してその位置にあるウィンドウのハンドルを取得したり、"ウィンドウクラス名" を指定してハンドルを取得する、デスクトップや、特定のウィンドウ中のウィンドウを全て列挙して得、特別(特定)な状態にあるウィンドウのハンドルを得、また、COM オブジェクトを使って得られる場合があります。

 ※ 太字で述べた関数は "具体的" には GetDlgItem() 関数。なにかの動作をさせるというよりは、機能的な関数です。また、併せて、あるウィンドウの "親ウィンドウ" のハンドルを得る GetParent()、さらに GetDlgItem() とは 対照的なこと、つまりハンドルからコントロールID を得ることのできる、 GetDlgCtrlID() 関数。これら3つをセットくらいに考えておくと、今後 ウィンドウハンドル や コントロールIDの取得には困りません。

 残念ながら、スケルトンコードで唯一のウィンドウは、トップレベルのウィンドウなので コントロールID を持たず、この GetDlgItem() 関数をテストするための材料がスケルトンコードにはありません。

 

 文章を追うのが疲れる場合は、下の表が上述の関数をまとめたものです。

 

[表 : ウィンドウハンドルを取得できる関数 ]

 GetDlgItem() -
 WindowFromPoint() ChildWindowFromPoint() -
 FindWindow() FindWindowEx() -
 EnumWindows() EnumChildWindows() (類) GetWindow() (類) GetNextWindow()
 GetActiveWindow() GetTopWindow() GetDesktopWindow() GetFocus()
 GetParent() -

 

 後に述べる "使用" の局面にくらべると、 "得" られる局面に関しては、上の関数くらいにおおかた整理できます。これはわたくしが個人的に把握している関数です。ウィンドウ作成関数は除いてあります。

 

 

 ウィンドウハンドルの使用 : 使用の局面(仕方)

 使う場面は、一概には把握しきれません。ウィンドウハンドル(の値)は、関数を使う際に必要です。つまり関数を使うときが一つ一つの場面です。そのような関数の中で、特に SendMessage() という関数はただ一つ 個性的な関数です。

 ウィンドウ操作だけでも、ウィンドウを動かす、テキストを表示する、もろもろのコントロール(※これもウィンドウ)を操作する・・・、のような局面があります。このような ウィンドウ操作 と少し質をことにして、描画などの処理でも、描画するウィンドウを指定するために ウィンドウハンドルは必要になります。ウィンドウに直接的な動作をさせる場合も、なにかの処理で、「あるウィンドウ」 を指定する必要があるときも ウィンドウハンドルは必要だということです。

 すべては関数 (Windows API 関数) で行います。その中でも1つだけ、特別に考えたほうがいい関数があって、SendMessage() という関数です。それ以外のここでは特別でない関数の中からは一つ、SetWindowText() 関数を挙げておきます。今はこの2つに注目します。

 [普通の関数 SetWindowText() ]
 まず、
SetWindowText() 。この関数は、ウィンドウにテキストを表示する関数です。この関数を説明する目的は、種類の違うウィンドウに対してでも、統一的に (汎用的に) 使える関数があることを言うためです。

 この関数は、SetWindowText( hWnd , "おいらはタイトル。でも移動のためにつかむところだから「キャプション」っていうんだぜ!" ) ; のように簡単に使えます。スケルトンコードで確かめてみることも出来るでしょう。ウィンドウハンドルで管理されるウィンドウのほとんどは、どこかしらにテキストを表示できます。この関数は、有効なウィンドウハンドルでありさえすれば、ウィンドウの種類を問いません。トップレベルのウィンドウやダイアログボックスだったら、タイトル(キャプション)部分にテキストが表示されるし、エディットコントロールならテキスト編集部のテキストが、ボタンならボタン表示テキストが...、この関数を使って表示変更できます。ShowWindow() 関数や EnableWindow() 関数なども、どんな種類のウィンドウにも使える点で、ここでは同じなかまです。ウィンドウハンドルを指定する関数の中にはもちろん、ダイアログボックスのハンドルを使うなど、特に指定のある関数もあります。

 [コントロールにとって独特の関数 SendMessage() ]
 ところが、コントロールアイテムは、それぞれ機能が独特で特殊です。例えばリストボックスならアイテムを順にいくつかストックしたりするし、ボタンならテキストでなく「画」 を表示したいこともある。そういった、コントロールに特化した機能を発揮したい場合のために用意されているのが、
SendMessage() 関数です。たった一つの関数ですが、すべてのコントロールにたいする様々な命令をこなします。例えば、SendMessage( hButton , BM_SETCHECK , ... ) ; のようなかたちで、チェックするタイプの "ボタンコントロール" に、チェック状態を変更する命令をだします。コントロールに限らず、ウィンドウ一般に有効な、汎用な命令 ( WM_ENABLEWM_SETTEXTWM_... ) のためにも使えます。

 スケルトンコードで簡単に試せるものとして、SendMessage( hWnd , WM_SETTEXT , 0 , (LPARAM)"オレ、キャプション( Caption )。何度も言わせないでください..." ) ; (--ここまで--) の一命令を挙げておきます。但し hWnd は 有効なウィンドウハンドルです。どこに書いたらいいのかも少し迷ってみてください。でもスケルトンコードではウィンドウは一つきりです。ウィンドウプロシージャ WndProc は、メッセージのたびに繰り返し呼ばれてしまう関数だから、この関数内に書き加えるのは気持ちとしては要注意ですが、左記のテストでは書く場所を間違えても、実行時のエラーが起こることはないでしょう。

 

 BM_SETCHECK のほかにはどのようなものがあるかは、SendMessage() 関数の説明を見ても載っていません!これらの数がまた半端ではありませんが、それだけいろいろなことが出来るということです。これら BM_SETCHECK 等は、それぞれの "コントロール" に付随する説明として、機能などとともに、 "定数" として説明されるものです。

 これら BM_SETCHECK などは通常、コントロールへのメッセージ と言って呼びます。更には、コントロールメッセージ、のように呼ぶことも可能ですが、これらの言葉にたよって特定の情報を得ようとしても困難です。実際には 32 ビットの値で、マクロ定義された定数です。少し慣れればヘッダファイルの winuser.h や、commctrl.h を見れば参考にはなります( そのとき grep.exe なども使ってみましょう)。基本的にはこれら ヘッダファイル が正式な "定義" です。

 それでも、どんな "コントロール" があるかすら、そしてどんなことをするものなのか、また、作成の仕方、使うにはもっとほかに必要なことがあるのかないのか、も気になるところだと思いますが、「必要・・・」に関しては、とにかくまずは、 「コントロールを作成 : CreateWindow() 」 さえしてしまえばひと段落です。

 ・・・さて、例えば "ボタン"(コントロール) は押されたら押されたことが分からないと作った意味がありません。そういうコントロールからの報告、これも "メッセージ" ですが、それは、親ウィンドウのウィンドウプロシージャ に報告されるから、ウィンドウプロシージャに適当な処理を記述することになります( 主に、WM_COMMAND や WM_NOTIFY、WM_PARENTNOTIFY メッセージ、など。アップダウンコントロールでは WM_H(V)SCROLL を処理するなど特殊な場合も。)。

 各コントロールも内部にはプロシージャを持ちますが、勝手に処理されるので記述の必要はありません。各コントロールのオリジナルのプロシージャを、自分で記述した(する)プロシージャに置き換えることもできて、それには 「サブクラス化」という方法を使います( SetWindowLong() 関数と 定数 GWL_WNDPROC 、または SubclassWindow() マクロ ) 。

 

 ウィンドウハンドルの説明だからこのあたりが限界です。ウィンドウでも、特にコントロールアイテムの場合、コントロールID と親ウィンドウのハンドルの2つにより、GetDlgItem() 関数でそのコントロールの "ウィンドウハンドル" が得られるから、上記のような "ウィンドウハンドルの使われ方" を、わかった分だけ把握してみて、変数を用意するか コントロールID だけでやるのか適宜、決めてみてください。単純には、常にウィンドウハンドル変数なんかは用意しなくても ( CreateWindow() の戻り値をほっぽらかしておいても)、必要になったらプログラムを書き換え変数を宣言して設ければそれで十分です。

 さまざまなコントロールが使えることを楽しみにしておきましょう。

 

 ウィンドウハンドルの正体

 ウィンドウハンドルとは何ぞや?実体は 32 ビットの値、さらにポインタです。 ウィンドウ作成時にシステム(OS) が、作成されたものを管理するために確保したデータ : ウィンドウ定義構造体へのポインタです。その構造体にはもちろん、コントロールID のデータ(つまり値)も含まれているわけです。ウィンドウハンドルと コントロールID の関係はここから考えるのが一番簡単(?)です。

 見えないウィンドウらしきものがあろうとも、この構造体により管理されるものはウィンドウということが出来るでしょう。興味があれば、ATL のソースなんかを覗いてみるとさらにいいヒントが得られます。

 インスタンスハンドル の場合も結局同様の説明でした。だから、Windows API における他のハンドルの姿も多分これ(ら)と同様です。すなわち、OS の管理のもとに供される各種データ構造体へのポインタです。

 いたるところでハンドルは数値だというのがしつこくなっています。総括としては、プログラム あまり惑わず みな数字。なんちゃって。

 

 

[トップ] [戻る]