Version 8.04
プロセスとインスタンスハンドル
「前回は」
『っつーか、ここ最近インスタンスハンドルばっかり』
「インスタンスハンドルというよりは、メモリ関係の話ってところかな。こ
の辺は C++ 言語では切っても切れない話だから」
『うー』
「さて、まずは前回見たようなプログラムから」
#include <Windows.h>
#include <stdio.h>
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// インスタンスハンドル( p_hPrevInstance )
// を文字列化します。
char pchMsg[1024];
sprintf( pchMsg, "HINSTANCE(PREV) : %x", p_hPrevInstance );
// それを表示。
MessageBox( NULL, pchMsg, "WinMain()", MB_OK );
return 0;
}
『ホント、似てるね。んーっと、 p_hInstance が p_hPrevInstance になっ
てるだけだ』
「そう、今回は p_hPrevInstance 、つまり WinMain() の第2引数に何が渡
されてるかを見ていきます」
『そういえば第1引数も第2引数もインスタンスハンドルだよね。ちょっと
謎』
「ではその謎な値を見てみましょう」
『ビルドして実行! ……あれ? 0 だって』
「そう、ゼロ。 WinMain() のリファレンスにも書いてあるけど、この第2
引数には必ず 0 が渡されるんです」
『リファレンスには〈 NULL が渡される〉って書いてあるけど』
「同じことだよ。 Version 5.11 ( No.076 ) 参照」
『あー、そっか。ハンドルもポインタだしね』
「そうそう。で、これがなぜゼロなのか」
『そうよ、必ず 0 なら、こんな引数要らないじゃない』
「そう、今はね」
『今は? これから必要になるの?』
「これからじゃなくて、昔必要だったんです。実は、昔はインスタンスハン
ドルは一定じゃなかったから」
『へ? だって、 0x00400000 で全部同じだったじゃない。昔はそうじゃな
かったの?』
「正確に言えば、 Windows 3.1 以前の OS はそうだったんです。この前、
メモリとかアドレスとかは仮想のものだって言ったよね」
『うん、ハードウェアのメモリのアドレスじゃなくて、それとは違うアドレ
スなんだよね』
「そうそう。で、その仮想アドレス、実は Windows 3.1 とか MS-DOS の時
代にはなかったんです」
『なかった?』
「その頃は、ハードウェアのメモリに直に実行ファイルや変数が置かれてた
んです」
『ってことは、アドレスもハードウェアのメモリ上のそのもの?』
「そのもの。で、実はそれはまずいんです」
『まずい?』
「今の仮想メモリの場合……そうだね、前回みたいに何回も起動したとしま
す」
『ダブルクリックしまくってね』
「その中で、一番最初に実行したのから、2番目に実行したのの変数を見た
り書き換えたりするのってどうすればいいと思う?」
『どうすれば……って、それ無理なんじゃない!?』
「そう、無理なんです。実行したものそれぞれに仮想メモリが存在するわけ
だから」
『他の仮想メモリを見たくても、結局自分の仮想メモリを見ちゃうわけね』
「これはとても安全なんです。他から邪魔されないから」
『なるほど、ハードウェアのメモリに全部置かれてると、他のアプリの邪魔
が出来ちゃうんだ』
「わざとするのももちろん、プログラムミスで」
『げ!』
「ま、逆にだから速い処理もできたりするんだけどね」
『速い処理?』
「……まぁそれは置いといて、歴史的には」
MS-DOS や Windows 3.1 : ハードウェアのメモリのアドレス : 危険
Windows95 以降 : 仮想メモリ、仮想アドレス : 安全
「っていう経緯があるわけです」
『安全な方に移ったわけね』
「で、昔の危険な頃は、ハードウェアのメモリ上にアプリが散在してまし
た。なので、その場所を知る必要があったんです。それがインスタンスハ
ンドル」
『おお!』
「この頃は、インスタンスハンドルはまさにアプリのハンドル。ひとつ実行
すると、ひとつメモリに置かれて、その先頭のアドレスがインスタンスハン
ドルとして渡される」
『実行されるごとの、まさに背番号ね』
「と、この〈実行される単位〉を【プロセス】って言います」
『プロセス?』
「 proccess は〈進行〉とか〈過程〉とか言うんだけど」
『結果じゃなくプロセスが大事、とか言うもんね』
「まぁあんまりニュアンスが分かりづらいから〈処理単位〉くらいに訳した
方がいいかも」
『テキトーねー』
「ま、それはともかく。アプリを実行する度に、仮想メモリがひとつ作られ
て、その中に実行ファイルとか変数が置かれます。このかたまりがプロセス
になります」
『実行する度に作られるものなのね』
「そうそう。 VC に付いてくる Process Viewer を使うと今作られてるプロ
セス全部を見ることが出来るから、一度見ておいて」
『はーい。で、話を戻して、もうひとつの p_hPrevInstance はなんなの?』
「これは previous なインスタンスハンドル。 previous は〈前〉って意
味」
『プレビューとか言うもんね。でもひとつ前のインスタンスハンドルっ
て?』
「そのまま。実行ファイルを数回起動してその都度プロセスが作られた時
に、ひとつ前に作られたプロセスのインスタンスハンドルがそう」
『??』
「あ、これは昔のイメージで考えないと分からないよ。たとえば」
昔のメモリ
[アドレス]
1000 : ひとつめのプロセスの置かれた場所(=インスタンスハンドル)。
1001
...
3000 : ふたつめのプロセスが置かれた場所。
3001
...
5000 : みっつめのプロセスが置かれた場所。
5001
「って場合に、みっつめのプロセスでは p_hPrevInstance には 3000 が渡
されることになります」
『あ、そういう〈ひとつ前〉ね』
「そゆこと。で、これが今は 0 なのは分かるよね」
『もちろん。だって、仮想メモリだと全部同じなんだもの。ひとつまえのイ
ンスタンスハンドルは自分と同じだし、ハードウェアのはもらっても仕方な
いし』
「そういうこと。こういう経緯で、昔は必要だったんだけど、今は必要な
いっていうかどうしようもないから 0 が渡されるんです」
『なるほどねー。でも、必要って何に使うの?』
「たとえば、複数起動を防ぐ場合。アプリの中には〈ひとつだけしか実行さ
せたくない〉って場合あるでしょ」
『ユーティリティとかそういうの多いよね』
「昔は、それを実現するために p_hPrevInstance を使ってたんです。
p_hPrevInstance には直前のプロセスのインスタンスハンドルが入ってるわ
けだけど、もし直前のがない、つまり最初に実行された時には 0 が入って
るんです」
『ってことは p_hPrevInstance に 0 が入ってたらそのまま実行、それ以外
のが入ってたら何もせずに終了』
「っていうことをしてたんです。たとえばこんな感じ」
#include <Windows.h>
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// NULL じゃないということはすでに実行されているので
// 何もしません。
if( p_hPrevInstance != NULL )
{
return 0;
}
// あとは普通に。
MessageBox( NULL, "テスト", "WinMain()", MB_OK );
return 0;
}
『なるほどねー』
「重要なのは、このプログラム、機能はしないけど、エラーにもならないっ
てこと」
『そりゃそうよね、 p_hPrevInstance は 0 に決まってるんだから』
「わざとそうしてるってことも言えるかな。もしこれが -1 を返すように
なってたら」
『このプログラム、使えないね』
「つまり、 Windows3.1 のプログラムを Windows98 に使えるようにするの
に修正しなきゃいけない」
『げげ』
「これは WinMain() もそう。もし p_hPrevInstance が必要ないってことで
WinMain() の引数が3個になったら」
『それも修正しなきゃいけない! めんどくさー』
「今の時代は必要ないものが残ってるのは、そういう理由があるんです」
『でもそれなら〈いらない〉って一言でいいんじゃ』
「一言でいいの?」
『うーん……』
/*
Preview Next Story!
*/
『インスタンスハンドル、なんかポインタ編みたいな感じ』
「実際、このバージョン8はそういう話多いかも」
『む、結構手強いかも』
「本当はねー、こういうの知らなくてもプログラム組めるんだけど」
『ってゆーかずっと知らなかったし』
「でも一歩上を目指すには必要だから」
『ホントにぃ?』
「というわけで次回」
< Version 8.05 コマンドラインと表示方法 >
『につづく!』
「頂点を目指すために!」
『ってゆーか目指す必要あるの?』