コンピューターと戯れて
-- Williamの窓 --

ちょっと悪乗りしてスタイルシートで遊んでみました。






今回からいよいよ本格的にコンピューターと戯れましょう。ただ、どこまで初心を貫徹できるか少々不安です。



さて、タイトルにもあるように今回は をテーマにします。


「おぃ!色はどうした!」そんな声が聞こえてきそうですが、その前置きとして今回脱線しちゃいます。

・・・・ もっとも今回取り上げる「 」は、近年の GUI (Graphical User Interface)環境にとっては切り離すことのできない重要な役割を果たしているので、コンピューターと戯れる前にどうしても理解しておきたい登竜門だと思ってください。

ですから、窓型 I/F ( InterFace ) に興味の無い方や「今更 window なんぞ教鞭してもらいたくないや」ってな方は、次のリンクをクリックしてください。
では、ごきげんよう!


あ!そうそう、取り扱う環境ですが
悪名高い Microsoft Windows 上の で戯れるつもりですので、MacやUNIX系、BeOS等の OS をお使いのユーザーの方は残念ですがここでお別れですね。


戻る









改めてこんにちは!

これからしばらく Microsoft Windows システムの で戯れますのでお付き合いください。



では、サブタイトルを一新して


はじめての窓 -- 苦々しい想いでともに







さて、テーマである「窓」ですが、現在メジャーになっているこのシステムの歴史は古く、遡ればゼロックス社のパロアルト研究所で作られたアルトが始祖になります。
「だからどうした」、はい、その通りですね。ただ今では当たり前の I/F を最初に思いついたのがパロアルトの方々だということを強調させてください・・・ 一応、彼らに敬意をはらって

簡単な説明になりますが 「窓」 とは、高度に TimeSharing された環境下で、端末と向き合ったユーザーがもっとも効率よく、多彩且つ複数の機能を効果的に使える環境として考え出されたものです。

「??」な説明の仕方でしたが、ようはWindowsやMac、Be、XWindow、(其の他もろもろ)のような GUI 上で、ユーザーとアプリケーションが意思を疎通させるディスプレー上の領域のことです。

これ以上 GUI の歴史や思想と戯れるつもりがないので、さくさく話を進めましょう。


さて、この Window システムでどのようなコード (プログラム) を書けば、プログラマーがエンドユーザーに対して Window を提供することができるのでしょうか?

これが今回のメインテーマになります。
プログラムなんぞに興味がない方もここでお別れですね、ではまた!
最終的には Windows 環境で単純な「窓」を出力させることを目的に以後話を進めます。
戻る
あと、申し訳ないのですが、お話のベースに C 言語或いは C++ を使わせて頂きますが、C 言語の解説は致しません











さて、単純な窓を作成しましょう。

例えばUNIXのXWindowになりますが、OSFのMotifを用いたコードでは次のようなコードになります。

motifwindow.c


#include <Xm/Label.h>

main(argc, argv)
int    argc;
char **argv;
{
  Widget            topLvl,
                        lbl;
  Arg              args[10];
  int           numargs = 0;

  topLvl = XtInitialize(argv[0], "Hey", NULL, 0, &argc, argv);

  XtSetArg(args[numargs], XmNwidth, 300);   numargs++;
  XtSetArg(args[numargs], XmNheight, 100);  numargs++;

  lbl = XmCreateLabel(topLvl, "Hello", args, numargs);
  XtManageChild(lbl);

  XtRealizeWidget(tpLvl);
  XtMainLoop();
}



行き成り Motif を使ってごめんなさい。ついでに内容が「??」な方にもごめんなさい。

え〜っと、補足ですが、UNIXのXWindow上でWindowを使うアプリケーションを作成する際に、昔はよく使われていたライブラリーが Motif です。・・・ 少なくともわたしくは
もっともこの Motif、最近はあまり使われていないようですが・・・
取り敢えず、他の環境での実装の仕方ということで敢えてMotifのコードを紹介しました


さて、話をWindowsに戻します。

Windowsでもっとも簡単な窓の作り方は以下のようなコードになります。

msgwin.c

#include <windows.h>

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
  MessageBox(NULL, "Hello", "Hey", MB_OK);
  return (0);
}



上記のコードをコンパイルして起動すると
こんな感じ(←)の画面がでます。


さて、なじぇ、こんな簡単なコードで window が生成されるのか?

実は、window を作るためにメッセージボックスと呼ばれるダイアログボックスの一種を用いたためです。


では、少し用語の説明しましょう。
まず、ダイアログボックスとは window の一種で、MSDN (Microsoft Developer Network) では、状況の通知、ユーザーからの入力を受けたりする temporary window だと言っています。
つぎにメッセージボックスですが、こちらはダイアログボックスの一種で、単純な I/F で構成された状況通知専用の window になります。

話を纏めます
ダイアログボックスとは複雑な window に対して、単純な入力I/Fのみで構成された window のことを示し、メッセージボックスはダイアログボックスをさらに簡易化したものです。
つまりはもっとも簡単なメッセージボックスはもっとも簡単に作成することが可能な window となります。



では、上記のコードの説明を致しましょう。

非 Windows 出身者の方には馴染みが無い形式でプログラムが始まりますが、ここで用いられている WinMain 関数が Win32 アプリケーションのエントリーポインターとなります。
プログラミングに馴染みの無い方は、取り敢えず Win32 アプリケーションは WinMain 関数から実行されるものと解釈しておいてください。

さて、ここで行き成り話しを脱線させましょう。

エントリーポイントやロードモジュールは任意に指定 ( ユーザーがカスタマイズ ) できるのか?
答えは、「はい、できます」です。
っと、その前に、ここから話が思いっきり脱線しますので、興味のない方や先に進みたい方はこちらから跳んでください。

この辺の話って、通常はリンカーに任せるべきところなので、一般的なプログラマーは気にしないところなのですが、取り敢えずざっと、ついでにさらりと解説してみたいと思います。
ちなみにお話の対象は Microsoft Visual C++ になりますので、使われているコンパイラーとリンカーのメーカーによっては、ぜんぜん違うものになっている可能性があるのであしからず

まず、エントリーポイントについてですが、こちらは Linker に対して 『 /ENTRY 』 オプションを切ることで開始アドレス(初期実行関数)を設定することができます。ただし、あたりまえのことですが、指定される関数は必ず __stdcall で指定してください。
なじぇって?それはですね、起動モジュールが呼び出す対象がインスタンス化されていなければならいないからさぁ
ところでエントリーポイントを変更しても、守らなければいけないルールが存在します。

それは・・・・

引数と戻り値は、必ず Win32 API 内で定義されている WinMain (.EXE ファイルの場合)、または DllEntryPoint (DLL ファイルの場合) はたまた main と同じにしなければならないのです!
つまりは Windows システムのロードモジュールには、エントリーポイントを細かくカスタマイズする機能がないのです。
ですから、任意に決めた関数名をエントリーポイントにしても、引数および戻り値はロードモジュールに依存しなかればならないのです。 (つまり戻り値及び引数の構造はデフォルトのエントリーポイントと同じ型じゃなきゃいけないのよ)
っていうか、ロードモジュールの起動関数呼び出しに対してスタックのカスタマイズ機能がないのよね。

またまた話が跳びます ・・・・ __sdcall 規約について Microsoft 固有の規約があるので以下に列挙しました。
・引数の引渡し方法は、値渡しになります
・引数の引き渡し順は、右から左に順に行われる
・スタックのクリアは、呼び出し側がクリアし、呼び出された側はそのままスタックからポップする

さて、ではロードモジュール(スタートアップ)に関してです。
こちらもユーザーが、どのロードモジュールを採用するか決めることができますが、基本的にはリンカーに任せていたほうが無難です。下手にいじると実行モジュールを生成できなくなってしまうから・・・

でも、知っていても損はしないので、さらっと紹介します。
まず、WindowsOS にはいくつかのロードモジュールがありまして、具体的には GUI 用とか CUI 用、はたまた DLL 呼び出し用等に対して、各々の専用ロードモジュールが用意されております「・・・ 関数のレファレンステーブルでロードモジュールのセレクトを行っているのであって、動的にスタック管理等はやらない構造なのね」 MSDN を読んだ当初はこんな認識を持っていましたが、実際に内部のコードを見てみると、なんのことはない、マクロでコンパイル箇所が別れており、コンパイルオプションを指定することで採用されるロードモジュールのコードが選択される構造なのね、まぁ自由度よりもリスクとのトレイドオフで、この方式を採用したのだろう

さて、ロードモジュールを決めるためのオプションが 『 /SUBSYSTEM 』 になります。このオプションを切ることで起動モジュールが決められます。
オプションの説明を致しましょう。(基本的には MSDN に書かれていることなので、詳細は MSDN を参照のこと)

オプションの定義は以下のようになります。

/SUBSYSTEM:{CONSOLE|WINDOWS|NATIVE|POSIX|WINDOWSCE}[,major[.minor]]


CONSOLE を指定すると CUI ベースのロードモジュールが指定されます。
main 関数の場合にはロードモジュール mainCRTStartup が呼び出され、wmain 関数の場合には wmainCRTStartup となります。
WINDOWS の場合には GUI ベースのロードモジュールになります。
WinMain 関数には WinMainCRTStartup が対応し、wWinMain 関数には wWinMainCRTStartup が対応します。
・ちょっと特殊で、/DLL オプションが切られていると、_DllMainCRTStartup が選ばれます。
NATIVE こちらはさらに特殊で、Windows NT システムのデバイスドライバーを作成する場合に用います。
POSIX これまた特殊で、NT システム上で POSIX 準拠のアプリケーションを作成する場合に用います。
でぇ、引数の major と minor ですが、こちらには 10 進数で 0 〜 65,535 の範囲の整数値を用いて必要最低限であるバージョン番号を指定します。
デフォルトのバージョンは、CONSOLE、WINDOWS、NATIVE では 4.0、POSIX では 19.90 だそうです。


纏めましょう。
/ENTRY を指定することで起動関数(エントリーポイントを指定)を指定することが可能
/SUBSYSTEM をしてするとロードモジュールが指定できる
取り敢えず、これ以上深追いすると OS の内部構造まで説明せにゃならんので、ここら辺で止めときます。





さて、話を戻して、どんどん進めちゃいましょう。


次に起動関数(エントリーポイント)を説明します。

Win32の基本的なエントリーポイントには、通常 WinMain 関数が用いられます。
では、このWinMain 関数とは?
int WINAPI WinMain(
  HINSTANCE    hInstance,      // handle to current instance
  HINSTANCE    hPrevInstance,  // handle to previous instance
  LPSTR        lpCmdLine,      // pointer to command line
  int          nCmdShow        // show state of window
);
以下は引数に関して MSDN の内容を要約したものになります。(ようは MSDN の焼き直しです)
hInstance
アプリケーションのインスタンスハンドル
hPrevInstance
アプリケーションの前のインスタンスのハンドル
ですが、Win32 アプリケーションでは常に NULL になっています。
かつて Windows 3.x のころはアプリケーションの二重起動をチェックするのにこのパラメータを用いた時期もありましたが、現在ではこのテクニックを用いることができません。
ちなにみ二重起動防止に関しては、MSDN 上に CreateMutex 関数を使えと記入されております。
lpCmdLine
コマンドラインが格納されます。
実態はNULL で終わる文字列を参照したポインタになります。
ただしコマンドライン全体は含まれません。
GetCommandLine 関数を用いてコマンドライン全体を取得します。
nCmdShow
window の表示方法の指定が入ってきます。
内容
SW_HIDE 非表示、他の window をアクティブ
SW_MINIMIZE 最小化、システムリスト中のトップレベル
window をアクティブ
SW_RESTORE (SW_SHOWNORMAL と同じ) アクティブにして、表示
最小化または最大化されているときは、
位置とサイズを元に戻す
SW_SHOW アクティブ且つ現在の位置・サイズで表示
SW_SHOWMAXIMIZED アクティブ且つ最大化
SW_SHOWMINIMIZED アクティブ且つ最小化
SW_SHOWMINNOACTIVE 最小化状態で表示
アクティブな window はアクティブな状態を維持
SW_SHOWNA 現在の状態で表示
アクティブな window はアクティブな状態を維持
SW_SHOWNOACTIVATE 直前の位置とサイズで表示
アクティブな window はアクティブな状態を維持
SW_SHOWNORMAL (SW_RESTORE と同じ) アクティブ表示
window が最小化または最大化されているときは、
位置とサイズを元に戻す


一先ず、なんにもしないプログラムはこんな感じになります。
nowin.c

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
  return (0);
}










さて、この辺で思いっきり話を戻します。

もっとも簡単な window を表示するコードの説明途中でしたね。

msgwin.c を思い直してください。
さて、このコードの七行目、赤字で MessageBox の一行に注目してください。
実は、この一行だけで window が作成されているのです。(ただし、生成した window はメッセージボックスですけど)

では、この一行をご説明しましょう。

これは MessageBox 関数と呼ばれる関数で、メッセージボックスを作成する関数です。
たいへんにシンプルな関数であるため、メッセージボックスができることも制約されています。基本的には、タイトルおよびメッセージ、定義済みのアイコン、プッシュボタンの組み合わせを決めることしかできません。

故にメッセージボックスの役割は、主に状態通知になるのです。


では、関数の説明をしましょう。

関数定義
int MessageBox(
  HWND     hWnd,       // handle of owner window
  LPCTSTR  lpText,     // address of text in message box
  LPCTSTR  lpCaption,  // address of title of message box
  UINT     uType       // style of message box
);
戻り値:メモリ不足のため作成できなかったときは、"0" が戻ります。それ以外は以下の通り。
意味
IDABORT [中止]ボタン選択
IDCANCEL [キャンセル]ボタン選択
IDIGNORE [無視]ボタン選択
IDNO [いいえ]ボタン選択
IDOK [OK]ボタン選択
IDRETRY [再試行]ボタン選択
IDYES [はい]ボタン選択
引数は以下の通りです。
hWnd
オーナーwindowを指定
NULLを指定することでオーナーなしのメッセージボックスを作成することが可能
lpText
表示したい内容を含んだ領域のアドレスを指定
NULLで終端された文字列のポインタを渡す
lpCaption
タイトル内容
NULL 終端文字列へのポインタを指定
ただし、NULL では、タイトルに「 エラー 」が使われる
uType
メッセージボックスの構成を指定します。
指定するフラグは以下の通り
ボタンの状態を指定するフラグ
MB_ABORTRETRYIGNORE [中止]、[再試行]、[無視]
MB_OK [OK]
MB_OKCANCEL [OK]、[キャンセル]
MB_RETRYCANCEL [再試行]、[キャンセル]
MB_YESNO [はい]、[いいえ]
MB_YESNOCANCEL [はい]、[いいえ]、[キャンセル]

戻り値に関して追加:[キャンセル]ボタンがあるときに[Esc]キーが押されると、IDCANCEL 値が返る。



アイコンを指定するフラグ
MB_ICONEXCLAMATION
(MB_ICONWARNING)
感嘆符アイコン
黄色い三角形の中に感嘆符 (!)


MB_ICONINFORMATION
(MB_ICONASTERISK)
吹き出しに小文字「 i 」アイコン

MB_ICONQUESTION 疑問符 (?) アイコン


MB_ICONSTOP
(MB_ICONERROR、MB_ICONHAND)
停止アイコン
赤い円の中に白い×



デフォルトプッシュボタンを指定するフラグ
MB_DEFBUTTON1 デフォルト
デフォルトプッシュボタンを最初のボタンにする
MB_DEFBUTTON2 デフォルトプッシュボタンを 2 番目のボタンにする
MB_DEFBUTTON3 デフォルトプッシュボタンを 3 番目のボタン
MB_DEFBUTTON4 デフォルトプッシュボタンを 4 番目のボタン


その他に動作形態指定フラグなるものもあるが、敢えて説明を省略させていただきます。(太字がデフォルト)
MB_APPLMODAL, MB_SYSTEMMODAL, MB_TASKMODAL, MB_DEFAULT_DESKTOP_ONLY, MB_HELP, MB_RIGHT, MB_RTLREADING, MB_SETFOREGROUND, MB_TOPMOST, MB_SERVICE_NOTIFICATION, MB_SERVICE_NOTIFICATION_NT3X



ここまでだらだらと書いてきたが、なんのこはない、ようするに MSDN の焼き写しをだらだらと纏めただけである。

簡単に言ってしまえば、メッセージボックスなんぞ、MessageBox 関数を使ってサックっと呼び出しているだけである。
その使い方は至って簡単で、第一引数にメッセージボックスの呼び出し元を、さらに第二引数にはメッセージボックスが通知する内容を、第三引数にタイトル、最後の引数にメッセージボックスの形態を指定すれば OK!

なんでこんな簡単なことに時間を費やしていたのだろう ( ・・・ 読んでくれている人はもっと退屈したはずだ ) っていうか、ここまで我慢して読んでいないかぁ・・・・・反省


でぇ、改めて上記のコードを説明しましょう。

MessageBox(NULL, "Hello", "Hey", MB_OK);

第一引数NULL所有者が存在しないメッセージボックス
第二引数"Hello"(バッファー上には『48 65 6C 6C 6F 00』) 『Hello』が表示
第三引数"Hey"(バッファー上には『48 65 79 00』) タイトルに『Hey』が表示される
第四引数MB_OKアイコンなし、[OK]ボタンのみのメッセージボックスになる
デフォルトでMB_DEFBUTTON1とMB_APPLMODALが選択される。
[OK]にフォーカスが移り、メッセージボックスに応答しない限り
シークエンスはロックされた状態になります。


以上!
ねぇ、簡単なことでしょう ・・・・ こんな簡単なことを説明するのに、なんでこんなに書を要したのか?
取り敢えず、今回はこの辺りで止めときます・・・・つづく



参考までに以下に MSDN のメッセージボックスに関する記述を無断で抜粋しました。
・A secondary window that is displayed to inform a user about a particular condition.
・A message box is a special kind of modal dialog box that an application uses to display messages and prompt for input.
ところでなぜ文字が小さいって? ・・・ それはですね、無断転載だからさぁ ・・・ わたくし小心者ですから


目次に戻る
全文メニュー
サイトの玄関