[level4] [ウィンドウ] [ HWND ] [ HINSTANCE ] [ MSG構造体] [Win32 データ型] [コールバック関数]
インスタンスハンドル
"HINSTANCE" は、インスタンスハンドル型の「データ型」(型名) です。
文法上の役割からは宣言シンボル、型宣言子にもあたりますが、「データ型」 と呼んでしまうのが一般的です。
"インスタンスハンドル" は、アプリケーション自体を表している 「値」 です。
HINSTANCE hInst ; で宣言された hInst は、インスタンスハンドルを格納するための変数です。
一つのアプリケーションにとって、インスタンスハンドルの値は一つ(一意)です。
インスタンスハンドルは、アプリケーション実行中、ころころ変わることはありません。
この値を、グローバル変数としてずっと保存しておいて、アプリケーションの「柱」くらいに考えても間違いはなさそうです。でも、主に関数を使うときに関数のパラメータとして必要になるもので、われわれにとってはさしたる使い道はありません。
"インスタンス" は、アプリケーションのメモリ内での実体のことを言います。
HINSTANCE については当面 "おまじない" でも問題ないと思います。
必要なときにインスタンスハンドルが取得できれば十分です。
構成
インスタンスハンドルの実際
インスタンスハンドルと、インスタンス
インスタンスハンドルの実際
スケルトンコード 内では、アプリケーションの一番はじめ(初期化時)にあらかじめ、WinMain() 関数の第一引数として、インスタンスハンドルを受け取ることができるので 、後々の使用に備えるために、「HINSTANCE 型のグローバル変数 "hAppInst"」 に格納 (保存) しているに過ぎません。実際、スケルトンコードを見渡せば、 hAppInst が "必要" となっている場面 (使われている場面) なんかは、 "ない" ことがわかります。また、変数名 hAppInst の名前のつけかたは任意です。
[インスタンスハンドルの取得] :
取得の仕方(場面)〜3つの方法 :
インスタンスハンドルはアプリケーションの実行中、常に変わることのない一貫した値です。だから、それの"取得" というのは取りたてて大げさなことではありません。 スケルトンコードでの例のように、グローバル変数 hAppInst (名前は任意) に保存しておかなくても、プログラム中、GetWindowLong() 関数により、
HINSTANCE hInst = GetWindowLong( hWnd , GWL_HINSTANCE ) ;
のようにするか、または、
GetModuleHandle() 関数により、この関数のときは唯一の引数をNULL にして、HINSTANCE hInst = GetModuleHandle(NULL) ;
のようにすることによって、"いつでも" 同じインスタンスハンドルが取得できます。前者
GetWindowLong() 関数で得る"値"は、ウィンドウクラスの登録の際に "登録" した インスタンスハンドルの値 に対応します(つまり登録した内容から得る)。また、後者GetModuleHandle() に関しては、「なにか自分で関数をつくるときに、その関数内でインスタンスハンドルが必要だが、その値をグローバル変数に頼らないようにしたいとき、または頼れないとき」は、この関数によるほかはありません。GetModuleHandle() 関数は、使い方が簡単なので、なにかの応急的な一時しのぎにも "使えます"。はじめからグローバル変数に保存しておく方法も含め、どれを使っても"結果"は同じですが、同じインスタンスハンドルを得るのに3つも方法を知っていれば心強いです。先に、てんてこ舞いをすると言ったのは、いざ必要なときに、意味すら分からないのに、加えてこの "取得の仕方" もわからない場面のことを言ったのです。でも意味は、このページの一番上の数行くらいでいいんです。※ これら3つは「簡単に取得できる方法」で、探せばより技巧的な方法も見つかります。
[インスタンスハンドルの使用と場面] :
使用の場面(場合)〜関数は限定的 :一方、必要となる場面は、関数が、インスタンスハンドルを必要とする場合で、そのような関数や場面は経験的にはかなり限定的でした。特に"関数"が限定的なのです。ただ、そのような関数の一つに、ダイアログボックスを表示するために使う、CreateDialog() 関数があります。ダイアログボックスを作成するときは、いつも インスタンスハンドルを使うことになるから、そのためにも、グローバル変数 (例では hAppInst) に保存しておく利点はあります。プログラムを組む側 (人) からしてみれば、ウィンドウハンドル(HWND) の場合と異なり、インスタンスハンドル(HINSTANCE) を使って、アプリケーションを 「識別する」 必要に迫られることは そうはありません。ウィンドウは、「あのウィンドウとこのウィンドウと・・・」 というように区別したいことがあっても、複数のアプリケーションを区別したいことは通常あまりないのです。インスタンスハンドルは主に、使用したい"関数"自身が必要とします。
さらに、高度な関数を使い、デフォルトのアプリケーションとは "別のインスタンス" を「生成」 することができます。LoadLibrary()、CoCreateInstance() 等。前者の関数は、DLLファイル 内の関数を使用したい(呼び出す)ときは必須の関数で、呼び出すための GetProcAddress() 関数とセットで使います。でも、この後者の CoCreateInstance() 関数なんかでは、もはや HINSTANCE 型 のパラメータは表れてきません・・・。
※ 前者関数 LoadLibrary() では、"モジュール" の "ロード" が済むと "インスタンスが生成" され、そのインスタンスは HINSTANCE 型の変数で識別します。だから ロードしたモジュールの扱われ方は本体アプリケーションのインスタンスと同等です。
また、HWND の場合もそうですが、HINSTANCE hAppInst ; の hAppInst は、ほんとうは、 "インスタンスハンドルを格納するための変数" です。インスタンスハンドルは、アプリケーション自体を表す(そして識別するための) 「値」 です。だから、hAppInst に有効な "インスタンスハンドル" が格納されてないうちに、 hAppInst を インスタンスハンドルと呼ぶことは説明として(説明するわたしにとって)誤りなのです。たいてい、値が代入(格納)されている上で、変数 hAppInst をもってして インスタンスハンドル ということは多く、そのケースが通常です。前頁で説明をはしょったのは、文脈的な都合上です。
インスタンスハンドルと、インスタンス
[インスタンスは実体 : 実体ってなに]
インスタンスハンドルは、このアプリケーションそのものを表す (represent) 値です。インスタンスそれそのものは、プログラミングでのことというよりは、コンピュータ一般での概念です。インスタンスは、実行ファイルや DLL などの モジュールファイル が、メモリ上にロードされて、メモリのある領域を占領している状態での、"アプリケーションの実体" のことをいいます。
この 「実体」 のことを理解したければ、次のことを考えます。つまり、アプリケーションは、実行をする前は、.exe や .dll 形式をはじめとしたフォーマット形式で、フロッピーやハードディスクなどの中に、 "ファイル" の形でおさまっています。処理をしたいことの内容や順序が、 機械語 で記されていて、特に、"モジュール" と呼ばれます。どうにかその ファイルに書かれている内容や順序を、 CPU によって処理させたいのであれば、CPU としては、一度、 "メモリ上" にロードしてからでないと、処理をすることができないのです。CPU はそういう仕組みになっています。実行ファイルは "実行モジュール" ともいうし、DLL も "モジュール" です。
そして、インスタンスハンドルは、アプリ起動直後、ローダーによりインスタンスが確保生成されたあと (Instantiated : インスタンス化された )、そのインスタンスを表すための数値として、オペレーティングシステム(OS) から与えられた、または割り当てられた値です。プログラミング上も、また API 関数にとっても、その値でアプリケーションを識別できればよいので、とりあえずの解説は以上で完結します。
[関数アドレスの理解]
上のような、インスタンスという実体になんとなく察しがつけば、関数アドレスの理解にも役立ちます。(スタティックな)関数は、インスタンスという実体に含まれるから、関数アドレスもその "実体" の中のどこかの位置を示す同様の、"アドレス値" だと、理解できるからです。あらかじめモジュール、ひいてはインスタンスに含まれない関数を、動的にロードして使おうという場合、そのような関数は、"スタティック" に対して"ダイナミック" な関数、つまり DLL 内の "動的関数" です。
[インスタンスハンドルの正体は 32ビット値だけど、さらには "ポインタ" なのだ。]
複数のアプリケーションを同時に起動できる マルチタスクの OS であることを前提として、インスタンスの大体のイメージ(実体のありさま)を簡単に捕らえてみた上で、それぞれのアプリケーションのインスタンス を 「区別」 したいとかんがえれば、「そういうことだったらそのメモリ内の展開されたところの先頭アドレス かなんかを代表値(インスタンスを表す値)とかにして区別すればいいんじゃないの?」 という考え方が最も安直です。OS はもちろんそうしますが、でも "インスタンスハンドル" は、インスタンスを表す値と説明したところが、それとは違うものなのです。アプリケーション起動時には、プログラマーの感知する段階より前の段階で、OS (カーネル) がインスタンスを管理します。他のアプリケーションも並立して走らせなくてはいけないから、つまり マルチのタスクの並立のためには、 OS にはどうしたってその手の情報(データ)管理は避けて通れません。だから、コンピュータ (OS) は、もちろんさきの "先頭アドレス" を "含めて"、いくつかのインスタンス情報を、 OS のやりかたに従って、データとして (構造体に) とりまとめます。
そして、インスタンスハンドルは、この、"OS がとりまとめた" インスタンス情報(構造体)へのアクセスです。そしてポインタです。
インスタンスハンドルの正体は、実際には、32 ビットの値だとも言えるし、けれどもどうせそれを言うのなら、"ポインタ" と言ってしまった方が要領を得ています。ハッカーになるためでない限り、プログラミング上意識することもなければ知る必要もまたないことですが、実際、インスタンスハンドルの数値は、有効なメモリブロックを指し示す "ポインタ" であることが確認できます。だから、より細かくは、この値は、識別のために用意された値というよりは、割り当てられたその一意の値 ( OS に確保されたその構造体へのポインタ) を、識別のために用いる、というほうが正確です(ほんとはでもどっちでもいいですけど)。
[インスタンスはコンピュータの概念、対するインスタンスハンドルは、Windows API の概念 ( OS 依存)]
ここにきて、インスタンスハンドルは、インスタンスという実体 のメモリ上のアドレスを直接指すものでなくて、OS 流に インスタンス情報をまとめたもの に対するアクセスである以上、この具体的な HINSTACE 型のインスタンスハンドル は、"Windows" という OS または、Windows API の範囲だけのこと(概念)、ということになるから、コンピュータ一般 (もしくは OS) の概念である インスタンス そのものとは、くらべる土俵がすこし異なるものとなるのです。インスタンスハンドルの種類なんてものについて述べる気にはなりませんが、OS がインスタンス情報の "まとめかた" をいくつか持っていれば、その数だけ インスタンスハンドルの種類が (HINSTANCE 型以外にも) あることになります。OS が マルチタスクを成り立たせるには、少なくとも インスタンスハンドルのような "考え方自体" は、どの道必要になるのが道理(インスタンスは管理しないことにははじまらないという道理)なので、今はマルチタスクの時代だし、コンピュータ一般(OS) の概念としてとらえてもいいような気がします。
正確を期すことを意図したとは思うのですが、自分でもなんでこんな理屈をごねたのかわからなくなってしまいました。ただ、HINSTANCE に関することはこれでおおかた締めくくれるように思います。インスタンスハンドルは識別ができればそれで上等なので、深く突っ込む必要はありませんが、意味の分からないものを使っていくというのも、少し心細いものです。これくらい述べておけば、インスタンスハンドルを扱うときのいやな顔も少しは減るように思います。 [ COM オブジェクトを扱うときはインスタンスの生成をじかに処理することがあるから、インスタンスの概念を知っておくと少しは役に足ちます。ただ、そこではもはや HINSTANCE型のハンドルが表立って出てくることはあまりありません。]