前回までを通して Windows 環境下において を実装するための手法(具体的には二つばかしの Windows API ですが)を紹介しました。しかしこれらは基本的には単純な を実現させるための簡易版 API となります。
そこで今回はいよいよ正規の を実装します。

さて、まずは論ずるよりも先に を実装するためのコードを見てください!


WinSmp.c

 //コンパイラのバージョン定義
 //Microsoft Visual C++R 6.0 の場合は 1200 として定義
 #if _MSC_VER > 1000
 #pragma once
 #endif // _MSC_VER > 1000

 // Windows ヘッダーから殆ど使用されないスタッフを除外
 #define WIN32_LEAN_AND_MEAN

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

 // Constant Value を作っておく
 // 命名は MSDN サンプル上の名前をそのまんま使っています
 // ついでにコメント内容も・・・
 // Class name for this application's window class.
 #define WINDOW_CLASSNAME      "Windows Sample"
 // Title for the application's window.
 #define WINDOW_TITLE          "Windows Sample Title"
 // Fixed window size.
 #define WINDOW_WIDTH          (320)
 #define WINDOW_HEIGHT         (200)


 // 前宣言
 // コールバック関数
 LRESULT CALLBACK
 WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

 // グローバル変数
 // 取り敢えずフラグを宣言
 BOOL    fCommandLine = FALSE;
 // Mutex用のハンドル
 HANDLE  mx; 


 // アプリの開始
 int APIENTRY
 WinMain(HINSTANCE hInstance, 
         HINSTANCE hPrevInstance, 
         LPSTR     lpszCommandLine, 
         int       cmdShow) 
 { 
     WNDCLASS wndClass; 
     HWND     hwnd; 
     MSG      msg; 

  // MSDN 上に以下のように書かれているのでミューテックを
  // 用いて二重起動を防止している。
  // MSDN から抜粋→「他のインスタンスが存在するかどうかを
  // 調べるには、CreateMutex 関数を使って必要なコードを実装」
     mx = CreateMutex( NULL, FALSE, "2000-10-25-11-15-02-WindowsSample1" );
     if ( GetLastError() ){
         MessageBox(NULL, "二重起動不可!", "二重起動起動防止", MB_OK);
         return FALSE;
     }

     // 非常に簡単なコマンド ライン処理
     // ユーザがコマンド ラインに何かを指定した場合、
     // 取り敢えず何も考えずフラグを有効にしておく
     if (0 != *lpszCommandLine)
         fCommandLine = TRUE; 

     // ウィンドウ クラスを登録
     wndClass.style         = 0; 
     wndClass.lpfnWndProc   = WndProc; 
     wndClass.cbClsExtra    = 0; 
     wndClass.cbWndExtra    = 0; 
     wndClass.hInstance     = hInstance; 
     wndClass.hIcon         = LoadIcon(NULL, 
                                       MAKEINTRESOURCE(IDI_WINLOGO)); 
     wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW); 
     wndClass.hbrBackground = COLOR_BACKGROUND;
     wndClass.lpszMenuName  = NULL; 
     wndClass.lpszClassName = WINDOW_CLASSNAME; 

     RegisterClass(&wndClass); 

    // インスタンスのメイン ウィンドウを作成
    // アプリケーションの初期化を行います:
    hwnd = CreateWindow(WINDOW_CLASSNAME, 
                        WINDOW_TITLE, 
                        WS_OVERLAPPED | WS_SYSMENU, 
                        CW_USEDEFAULT, CW_USEDEFAULT, 
                        WINDOW_WIDTH, WINDOW_HEIGHT, 
                        NULL, 
                        NULL, 
                        hInstance, 
                        NULL); 
    if ( !hwnd ){
       ReleaseMutex( mx );
       return FALSE;
    }

    ShowWindow(hwnd, cmdShow);
    UpdateWindow(hwnd);

    // メイン メッセージのディスパッチ ループ
    while (TRUE){ 
       if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)){ 
          // メッセージが存在する
          // QUIT であれば、メッセージループを抜ける
          // それ以外の場合はメッセージを処理
          if (WM_QUIT == msg.message){
             break; 
          } else { 
             TranslateMessage(&msg); 
             DispatchMessage(&msg); 
          } 
      } 
    } 
    ReleaseMutex( mx );
    return msg.wParam; 
 }

 // コールバック
 LRESULT CALLBACK
 WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
    switch( msg ){
    case WM_DESTROY:
       PostQuitMessage( 0 );
       break;
    default:
       return DefWindowProc( hwnd, msg, wParam, lParam );
    }
    return (0L);
 }



どうでしょうか?

長い?いえいえ、これぐらいの長さで凹たれないでください。実際にまともなアプリを作成すれば、コードステップ数なんぞ1000や2000行は軽く超えますので



ひとまず何も考えず上記のコードを黙ってコンパイルしてみてください。

すると下のような 窓 が現われたはずです。

なにもしない窓

如何ですか?無事に は表示されましたか?
何はさておき、これでようやく真っ当な window の実装が実現できたはずです。



何もしない単純な ですが、前回までで紹介した MessageBox 関数、DialogBox 関数などとは違い、今回のコードはモノ本の を実装するコードになります。
余力のある方はじっくり上のコードを読んで解析してみてください。




では、ここからコードの解説を行います。

上のコードは単純に以下に示すようなパートによって構成されています。


アプリのエントランス
WinMain()
{

ウィンドウ クラスを登録
RegisterClass()


ウィンドウの表示
CreateWindow()
ShowWindow()
UpdateWindow()


メッセージループ
while (){
PeekMessage()
TranslateMessage()
DispatchMessage()
}

}



メッセージ処理
WndProc()
{

}


始めに、まずは何も考えることなく WinMain 関数からアプリケーションは開始されます。
この辺りは前回までで極めているはずなのでサックっと行きましょう。

次ぎに行う作業は、ウィンドウクラスの定義と登録になります。
具体的には、定義した WNDCLASS 構造体のフィールドにウィンドウの性質を決定する値を格納する作業を「ウィンドウクラスの定義」とここでは呼びます。
続いて RegisterClass 関数を用いて定義したウィンドウクラスを登録します。

いよいよウィンドウを作成します。
まずは CreateWindow 関数を用いて登録されたウィンドウクラスを基にウィンドウを生成します。
さて、焦らないで下さい。
ウィンドウが無事作成されたからと言って、この時点でウィンドウが表示されるわけではありません。
次ぎに続くステップで待望のウィンドウを画面上に表示させます。
API の呼び出し手順は、CreateWindow 関数で取得したハンドルを ShowWindow 関数に渡します。この時点でようやくウィンドウが画面上に表示されます。あとはおまじないとして UpdateWindow 関数を噛ましておいてください。

さて、ウィンドウが表示されたからと言って、まだまだ油断は禁物です。
行うべき作業はまだ残っています。

表示されたウィンドウ上で発生した事象(イベント / メッセージ)を受け取るべき受皿を用意します。
それが「メッセージループ」と「メッセージ処理」の箇所になります。

「メッセージループ」とは、この場合、while ステートメントで廻り続けるループ箇所を示し、具体的にはウィンドウ上で発生した作用が PeekMessage 関数によってアプリケーション側に取得され、TranslateMessage 関数で内容が解釈され、 DispatchMessage 関数を通して一旦システムにシークエンスを返し、システム側から「メッセージ処理」部分を呼び出してもらいます。
後はプログラマーが「メッセージ処理」上に具体的なコードを書き、ウィンドウ上に行われた行為に対する処理を決定していきます。

ちなみに途中で出て来たお呪いである UpdateWindow 関数を補足として説明しておきますと、この関数の役割はウィンドウにWM_PAINTメッセージを送って、そのクライアント領域を更新させます。

さて、window を実装するためのコードを紹介し終えたところで話しを終えさせていただきます。
Windows プログラミングの基本はここから始まりますので、実際には Windows アーキテクチャの真髄を極めるには、ここから深追いするべきところでしょうが、今回の目的・主眼があくまでも を実装する点にあります。だから Windows アーキテクチャを解説するような話題は敢えて避けさせていただきました。
まぁ、いずれ機会があったらご紹介するつもりですので、詳しい話しは「今回はなし」っということにしてください。



では、また
高松 恵 2000/11/03 文化の日(旗日)になにやっているんだろうな〜




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


ご意見、ご感想、ご要望等々はこちらのメールアドレス takamegu@lycos.ne.jp までご一報ください。

















































こっそり お・ま・け
今回ご紹介したコードの部分の一部を変更すると、ちょっと面白い現象を創ることができます。
変更する箇所はウィンドウクラスの定義箇所で、hbrBackground の値になります。
具体的には下のコードの抜粋をご参照してください。
     // ウィンドウ クラスを登録
     wndClass.style         = 0; 
     wndClass.lpfnWndProc   = WndProc; 
     wndClass.cbClsExtra    = 0; 
     wndClass.cbWndExtra    = 0; 
     wndClass.hInstance     = hInstance; 
     wndClass.hIcon         = LoadIcon(NULL, 
                                       MAKEINTRESOURCE(IDI_WINLOGO)); 
     wndClass.hCursor       = LoadCursor(NULL, IDC_ARROW); 
     wndClass.hbrBackground = COLOR_BACKGROUND;
     wndClass.lpszMenuName  = NULL; 
     wndClass.lpszClassName = WINDOW_CLASSNAME; 
上の赤字の箇所を次ぎのように変更してください。

wndClass.hbrBackground = COLOR_BACKGROUND;

wndClass.hbrBackground = GetStockObject(NULL_BRUSH);


結果はコンパイルしてみて確認してみてください。

おしまい