時乃工房-Windowsとアマグラマーな関係-

アクセスアップにオートリンクネット リンクが自動で増殖オートリンクの登録はこちら 節約内職情報検索局
ページランク向上リンク集
役立つリンク集 Web Links
SEO対策ディレクトリ型検索エンジン Su-Jine
メニュー
トップ
アマグラミング
C++編 目次
第5章
第7章
ソフトウェア開発製品
相互リンク

<勘違いだらけのアマグラミングな日々(C++編)>

第6章 白い窓

2006年8月23日

<何もしないWindowのサンプル>前章で、メッセージボックスを使ったWindowsアプリケーションの開発に成功した私は、更に本格的なアプリケーションを開発するべく、左のような窓枠を作ることにチャレンジしてみることにした。

因みにメッセージボックス単体でも、パラメータをいじりまわしたりして使い方を工夫することで、色々と深く興味深い実験が出来たりするわけだが、なにせ”地味”なので面白みに欠けてしまう(←失礼・・・)。

そもそもメッセージボックスだって、”窓枠”を元に作られたスイートな(?)インターフェイスなのだから、壱から作ろうと思ったらやっぱり窓枠から取り掛からないといけないはずだ。

と、毎度ながら意味の無い前置きを語らせて頂いた後で、早速ソースファイルを書いてみよう。

/*
 Title   :List.001-007
 Filename:"main_window.h"
*/


//マクロ宣言
#define WINDOW_TITLE        "役立たずなウィンドウ・・・" //タイトル
#define WINDOW_CLASS_NAME   "MainWindow"            //ウィンドウクラス名
/*
 Title   :List.001-007
 Filename:"main_window.cpp"
*/


//インクルードファイル
#include    <windows.h>
#include    "main_window.h"
#include    "main_callback.h"

//グローバル変数
char    window_title[]=      WINDOW_TITLE;       //タイトル←ウィンドウの左上に表示
char    window_class_name[]= WINDOW_CLASS_NAME;  //ウィンドウクラス名

//エントリーポイント
int WINAPI WinMain( HINSTANCE   main_window_instance,
                    HINSTANCE   hPrevinst,
                    LPSTR       lpszArgs,
                    int         nWinMode)
{
   HWND         main_window_handle;
   MSG          message;
   WNDCLASSEX   window_class;

   //ウィンドウクラス構造体を定義
   {
       //WNDCLASSEX構造体の大きさをセット
       window_class.cbSize=    sizeof( WNDCLASSEX);

       //ウィンドウのスタイルを指示
       window_class.style=     0;  //何も指示せず

       //ウィンドウプロシージャへのポインタをセット
       window_class.lpfnWndProc=   MainCallBack;

       //WNDCLASSEX構造体の後ろに割り当てる補足バイト数を指示
       window_class.cbClsExtra=    0;

       //ウィンドウインスタンスの後ろに割り当てる細くバイト数を指示
       window_class.cbWndExtra=    0;

       //OSから渡されたウィンドウのインスタンスをセット
       window_class.hInstance=     main_window_instance;

       //ラージアイコンの指定
       window_class.hIcon=         LoadIcon(   NULL,
                                                IDI_APPLICATION);

       //カーソルリソースの指定←ウィンドウ内で使うカーソルのデザイン
       window_class.hCursor=       LoadCursor( NULL,
                                                IDC_ARROW);

       //ウィンドウバックグラウンドカラー指定
       window_class.hbrBackground= ( HBRUSH) GetStockObject(   WHITE_BRUSH);

       //使用するメニューの名前を指示
       window_class.lpszMenuName=  NULL;   //指定なし

       //このウィンドウクラスの名前を指示
       window_class.lpszClassName= window_class_name;

       //スモールアイコン指定
       window_class.hIconSm=       LoadIcon( NULL,
                                              IDI_WINLOGO);
   }

   //ウィンドウクラスの登録
   if( FALSE== RegisterClassEx( &window_class))
       return FALSE;   //失敗するとここでアプリケーションを終了

   //ウィンドウの生成←成功するとハンドルを返す
   main_window_handle= CreateWindowEx( 0,
                                       //ウィンドウクラス指示
                                       window_class_name,
                                       //ウィンドウタイトル指示
                                       window_title,
                                       //ウィンドウスタイル指示
                                       WS_OVERLAPPEDWINDOW,
                                       //ウィンドウのx方向位置
                                       CW_USEDEFAULT,
                                       //ウィンドウのy方向の位置
                                       CW_USEDEFAULT,
                                       //ウィンドウの横幅
                                       CW_USEDEFAULT,
                                       //ウィンドウの縦幅
                                       CW_USEDEFAULT,
                                       //親ウィンドウのハンドル
                                       HWND_DESKTOP,
                                       NULL,
                                       main_window_instance,
                                       NULL);

   //ウィンドウを表示
   ShowWindow( main_window_handle,
               nWinMode);
   UpdateWindow(   main_window_handle);

   //メッセージループの生成
   while( GetMessage(  &message,
                        NULL,
                        0,
                        0))
   {
       TranslateMessage(   &message);
       DispatchMessage(    &message);
   }
   return ( int)message.wParam;
}
/*
 Title   :List.001-007
 Filename:"main_callback.h"
*/


//関数プロトタイプ宣言
LRESULT CALLBACK MainCallBack(  HWND    window_handle,  //ウィンドウのハンドル
                                UINT    message,         //メッセージ番号
                                WPARAM  wParam,        //受渡し用パラメータ
                                LPARAM  lParam);        //受渡し用パラメータ
/*
 Title   :List.001-007
 Filename:"main_callback.cpp"
*/


//インクルードファイル
#include    <windows.h>

//メインコールバック関数
LRESULT CALLBACK MainCallBack(  HWND    window_handle,
                                UINT    message,
                                WPARAM  wParam,
                                LPARAM  lParam)
{
   return DefWindowProc(   window_handle,  //デフォルトのメッセージ処理
                           message,
                           wParam,
                           lParam);
}
ソースのダウンロード
この本で見つけました

この手のソースは、Windowsプログラミングの入門書(APIを扱ったもの)で似たようなコードが紹介されている事が多いので、結構ポピュラーなものだと思う。

内容的には複雑なことなどしている気配も無く、そのほとんどをWindow構造体の穴埋め(それと長いコメント)が占めていることに気がつくことと思う。
因みに構造体への代入部分が"{}"で囲んであるが、これは特に必要なものではない。
見易いかなぁ、と思った私の老婆心である。
ついでにダウンロード版のソースには、おまけのコメントアウトが盛り込まれているので、気になる方は目を通してみてほしいと思う。

このソースで唯一気にかかる部分といえば、関数MainCallBack()である。
これは関数WinMain()から直接呼出されていない。
作ったWindowから呼出される関数で、ごらんのとおりポインタ渡しされているのでプロトタイプの形を崩してはいけないはず。
あ、この"MainCallBack"と云う関数名は私が勝手に付けたものである。

ともかく、コンパイルして実行してみる。
画面には中身の無い窓枠が表示され、出来ることといえば右上の三つのアイコンをクリックして、最小化したり、全画面化したり、終了させてみたり、あと大きさや位置を動かしてみたり、といったことだけ。
まことにシンプルな、しかして複雑な処理をさらっとこなす感慨深いアプリケーションだ(大袈裟)。
役に立たないけど・・・。

・・・しかし、このソースはとんでもないバグを内包しているのである。

<実行中のプロセス状態>

終了の×アイコンをクリックすると、一見アプリケーションは終了したかに見える。
その後、タスクマネージャーを使って実行中のプロセスを覗いてみる

するとそこには、終わったはずのアプリケーションの名前が、"007simple・・・"がそれ。
すでに画面上にはWindowの姿が無い為、ここでプロセスを終了させるほか手段がない。
実行してしまった方は、申し訳ないがこの方法で終了させて頂きたい。

因みに、私がこのバグに気が付いたのはリコンパイル時にでたあるエラーからである。
それは"対象の実行ファイルが実行中だから上書きできないよ"と云うコンパイラの悲痛な叫びであった。
上記のタスクマネージャはその時のもので、4つ表示されているタスクはどれも窓枠が消えているのである。(あー恥かしい)

WindowsOSはメッセージ駆動のOS
これは様々な場面で聞かれる言葉なので、私でも聴いたことがある。
そのOS上で実行されるアプリケーションも、OSからのメッセージを受取ることが出来る。
あえて"出来る"と書いたのは、別に無視しても一向に構わないからであるが、必要なメッセージまで無視すると今回のように痛い目にあってしまうわけだ。

しかし、なんともはや"役立たず"どころか"はた迷惑"なアプリケーションになってしまった。
仕方が無いので、ちゃんと終了するように修正を施すとしよう。

/*
 Title   :List.001-008
 Filename:"main_window.h"
*/




/*
 Title   :List.001-008
 Filename:"main_window.cpp"
*/




/*
 Title   :List.001-008
 Filename:"main_callback.h"
*/




/*
 Title   :List.001-008
 Filename:"main_callback.cpp"
*/


//インクルードファイル
#include    <windows.h>

//メインコールバック関数
LRESULT CALLBACK MainCallBack(  HWND     window_handle,
                                UINT     message,
                                WPARAM   wParam,
                                LPARAM   lParam)
{
       switch( message)
   {
   case    WM_CLOSE:      //これがウィンドウを閉じるメッセージ
       DestroyWindow(  window_handle);
       break;

   case    WM_DESTROY:  //終了メッセージ
       PostQuitMessage(    0);  //メッセージループを抜ける為の処理を行う
       break;

   default:
       return DefWindowProc( window_handle,   //デフォルトのメッセージ処理
                             message,
                             wParam,
                             lParam);
   }

   return 0;
}
ソースのダウンロード

あんまり長いソースを何度も載せるのはどうかと思うので、今回は変更した部分のみ書き出してみた。
ダウンロード用のファイルには全部入れてあるので、ご安心を(?)。

メッセージって云うのはある番号なので、"windows.h"内でマクロ宣言されている。
それを踏まえた上で、修正個所を判る範囲で観察してみよう.
まず、窓枠右上の×アイコンをクリックするとWM_CLOSEメッセージがOSから送られてくるものと思われる。
これを、デフォルトの処理であるところの関数DefWindowProc()に任せてしまうと、とりあえずウィンドウを画面上から破棄してくれる。
しかし、視覚的な窓枠がなくなっただけでアプリケーションはまだ終了していないので、WM_CLOSEメッセージの処理として、アプリケーションを終了する処理を追加してやらなければいけない。
具体的には、アプリケーションを終了させるメッセージWM_DESTROYを自分自身に言い聞かせ、自分でメッセージを受取って、アプリケーションの終了処理を行うこととなる。

因みに、関数DestroyWindow()に他のアプリケーションのハンドルを指示すると、きっと道連れに出来るはずだから、試してみるのも面白い。

とにかく、窓枠を作ることには成功したわけだ。
次はもう少しこの窓枠をいじって遊んでみることにする。

<前章> <目次>





<時乃工房>
Net Office Nakai
メビウスリング投稿掲示板には小説日記ゲームアニメコミック小学生中学生などの掲示板過去ログがあります。相互リンクも募集中。