13章 Windowsアプリケーションの骨格(その8)

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>13章 Windowsアプリケーションの骨格(その8)</title>
</head>

<body bgcolor="WHITE">
<p>
<font size="5">13章 Windowsアプリケーションの骨格(その8)</font>
<hr>
<p>
 10章で、機能の再利用の為の子ウインドウを学び、11章と12章でDLLの基礎を学びました。今回は、DLLの知識を元に、機能の再利用のための子ウインドウをDLL化し、発展させてみましょう。
<p>
 今回は、今までの総まとめという意味も兼ねて、ちょっとアプリケーションらしいプログラムになるまで凝ってみました。まずは、ダウンロードして、動作を確認してみましょう。
<p align="CENTER">
<img src="/web/20160504210545im_/http://web.kyoto-inet.or.jp:80/people/ysskondo/img13.gif"><br>
<a href="c13main.lzh">アプリケーション(含DLL)</a>
<a href="c13dll.lzh">DLLのソース一式(含ドキュメント:RTF形式)</a>
<hr>
<p>
<b>・DLLの主要なソースコードを見てみよう</b>
<p>
 DLLの主要なソースコードを見てみましょう。重要な個所は太文字にしてあります。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト13-1(c13dll.dll)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   13章のサンプルプログラム
                   Programmed by Y.Kondo

/


#ifndef ___YK_COUNTER_BUTTON_CLASS___
#define ___YK_COUNTER_BUTTON_CLASS___

/* ウインドウクラス名:出来るだけ衝突の無い名前にしておく */
#define <b>YK_COUNTER_BUTTON</b> "@@6523174887854@@@"

/* カウンターからカウント数を取得する為のメッセージ */
#define <b>YKCB_GETCOUNT</b> <b>(WM_USER)</b>

/* カウンターに数値をセットする為のメッセージ */
/* 数値は、WPARAMとして渡す。LPARAM値は任意*/
#define <b>YKCB_SETCOUNT</b> <b>(WM_USER+1)</b>

/* ボタンが押された事を親に通知する為の通知メッセージ */
#define <b>YKCB_NOTIFY_UP</b> 2
#define <b>YKCB_NOTIFY_DOWN</b> 3

#endif /* ___YK_COUNTER_BUTTON_CLASS___ */
</pre>
</table>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト13-2(main.c)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   13章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include "resource.h"

#include "c13dll.h"


/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static LRESULT Wm_CreateProc(HWND,LPCREATESTRUCT);
static LRESULT Wm_PaintProc(HWND);
static LRESULT Wm_LButtonDown(HWND);
static LRESULT Wm_RButtonDown(HWND);
static LRESULT Ykcb_GetCountProc(HWND);
static LRESULT Ykcb_SetCountProc(HWND,WPARAM);
static ATOM RegSubWindowClass(HINSTANCE);
static LRESULT CALLBACK SubWindowProc(HWND,UINT,WPARAM,LPARAM);

/* DLLエントリーポイント */
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
  switch(fdwReason)
   {
       case    DLL_PROCESS_ATTACH: /*  プロセスに接続されたとき    */
           if(RegSubWindowClass(hinstDLL))
               return  TRUE;   /*  ウインドウクラスの登録に成功した場合    */
           else
               return  FALSE;  /*  失敗した場合    */

       case    DLL_PROCESS_DETACH: /*  プロセスから切り離されたとき    */
           UnregisterClass(YK_COUNTER_BUTTON,hinstDLL);
           break;
   }
   return  TRUE;
}

/* 子ウインドウのウインドウクラスを登録する関数 */
static ATOM RegSubWindowClass(HINSTANCE hInstance)
{
  WNDCLASS        wc;             /*  ウインドウクラス登録用の構造体          */

  wc.style        =CS_HREDRAW|CS_VREDRAW|<b>CS_GLOBALCLASS</b>; 
   wc.lpfnWndProc  =SubWindowProc;
   wc.cbClsExtra   =0;
   wc.cbWndExtra   =<b>8</b>;             /*  <b>ウインドウ毎に8バイトのメモリーを確保する</b>   */
   wc.hInstance    =hInstance;
   wc.hIcon        =NULL;
   wc.hCursor      =LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR1));
   wc.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
   wc.lpszMenuName =NULL;
   wc.lpszClassName=YK_COUNTER_BUTTON;
   
   return  RegisterClass(&wc;);     /*  ウインドウクラス登録    */
}

/* 子のウインドウプロシージャ */
static LRESULT CALLBACK SubWindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {   
       case    WM_CREATE:
           return  Wm_CreateProc(hwnd,(LPCREATESTRUCT)lparam);
       case    WM_PAINT:           /*  描画    */
           return  Wm_PaintProc(hwnd);
       case    WM_LBUTTONDOWN:     /*  マウスの左ボタン押下    */
           return  Wm_LButtonDown(hwnd);
       case    WM_RBUTTONDOWN:     /*  マウスの右ボタン押下    */
           return  Wm_RButtonDown(hwnd);
       case    <b>YKCB_GETCOUNT</b>:      /*  現在のカウント値を返す  */
           return  Ykcb_GetCountProc(hwnd);
       case    <b>YKCB_SETCOUNT</b>:      /*  カウント値のセット      */
           return  Ykcb_SetCountProc(hwnd,wparam);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

/* このファイルの中でのみ用いられる関数 */
/* ウインドウプロシージャ初期化 */
static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  SetWindowLong(hwnd,0,0);
   <b>SetWindowLong(hwnd,4,(LONG)cs-&gt;hMenu)</b>;
   return  0;
}

/* カウント値描画関数 */
static LRESULT Wm_PaintProc(HWND hwnd)
{
  PAINTSTRUCT ps;
   HDC         DC;
   RECT        ClientRect;
   TEXTMETRIC  Tm;

  char        buf[20];
   
   GetClientRect(hwnd,&ClientRect;);    /*  クライアントウインドウのサイズを取得する    */

  wsprintf(buf,"%d",GetWindowLong(hwnd,0));       /*  数値を文字列に変換する                      */
                                       /*  泥臭い方法:「C言語の定石」を参照せよ      */

  DC=BeginPaint(hwnd,&ps;);
       SetTextAlign(DC,TA_CENTER);
       GetTextMetrics(DC,&Tm;);
       TextOut(DC,ClientRect.right/2,ClientRect.bottom/2-Tm.tmHeight/2,buf,strlen(buf));
   EndPaint(hwnd,&ps;);
   
   return  0;
}

/* マウスの左ボタンが押された時、カウントアップする */
static LRESULT Wm_LButtonDown(HWND hwnd)
{
  SetWindowLong(hwnd,0,GetWindowLong(hwnd,0)+1);  /*  カウントアップ  */
   InvalidateRect(hwnd,NULL,TRUE);                 /*  画面の無効化    */

  /*  ボタンが押された事を親に通知する    */
   <b>SendMessage(GetParent(hwnd),WM_COMMAND,
                  MAKEWPARAM((WORD)GetWindowLong(hwnd,4),YKCB_NOTIFY_UP),(LPARAM)hwnd)</b>;

  return  0;
}

/* マウスの右ボタンが押された時、カウントダウンする */
static LRESULT Wm_RButtonDown(HWND hwnd)
{
  SetWindowLong(hwnd,0,GetWindowLong(hwnd,0)-1);  /*  カウントダウン  */
   InvalidateRect(hwnd,NULL,TRUE);                 /*  画面の無効化    */

  /*  ボタンが押された事を親に通知する    */
   <b>SendMessage(GetParent(hwnd),WM_COMMAND,
                  MAKEWPARAM((WORD)GetWindowLong(hwnd,4),YKCB_NOTIFY_DOWN),(LPARAM)hwnd)</b>;

  return  0;
}

/* 現在のカウント値を返す関数 */
static LRESULT Ykcb_GetCountProc(HWND hwnd)
{
  return  GetWindowLong(hwnd,0);
}

/* カウント値を設定する関数 */
static LRESULT Ykcb_SetCountProc(HWND hwnd,WPARAM wparam)
{
  SetWindowLong(hwnd,0,wparam);
   InvalidateRect(hwnd,NULL,TRUE);
   return  0;
}
</pre>
</table>
<p>
<b>・このDLLソースコードの特徴は?</b>
<p>
 エキスポートしている関数は一つもありません。従って、コンパイル&リンクをしてもインポートライブラリーは出来ません。従って、使う時には、明示的にリンクする必要があります。
<p>
<b>・このDLLは何をしているのか?</b>
<p>
 全体から細部へと見ていきましょうか。
<p>
 このDLLは、DLLエントリーポイント(DllMain関数)内で、プロセスに接続された時に、クラス名YK_COUNTER_BUTTONのウインドウクラスを登録し、プロセスから切り離される時に、このクラスの登録を取り消しています。
<p>
 ウインドウクラスを登録する関数(RegSubWindowClass関数)で特に重要な事は、<b><i><u>WndClass構造体のstyleメンバ変数にCS_GLOBALCLASSが加わっている事です</u></i></b>。これは、クラスを登録する時とクラスを用いてウインドウを作成する時でインスタンスハンドルが異なる事に対処するものです。また、後程説明する理由から、cbWndExtraメンバ変数に8を指定しています。これで32ビット長の変数を2つ確保した事になります。
<p>
 登録されるウインドウクラスに属するウインドウプロシージャは、4つのWindows標準のメッセージと2つのプログラマー定義のメッセージに応答しています。
<p>
 WM_CREATEメッセージに対して、カウンタ本体として利用しているウインドウ毎のメモリ領域のオフセット0からの32ビットを0に代入しています。今回は、カウンタ値以外に、もう1つウインドウ毎の変数が必要になります。つまり、<b><i><u>ウインドウ毎の子ウインドウ識別子(後から説明しますが、正式にはコントロールID)を格納しておく変数です</u></i></b>。これは、このウインドウクラスを用いるウインドウで事象が発生した場合、これを親ウインドウに通知する時の標準プロトコルとして必要なのです。これに関しては、このDLLを利用する時に説明します。
<p>
 WM_PAINTメッセージに対しては、10章と全く同じなので、説明は不要でしょう。
<p>
 WM_LBUTTONDOWNメッセージに対しては、カウント値のカウントアップを行っています。そして、自らのウインドウを無効化して再描画を促しています。ここまでは、冗長さを省いた以外、10章の例と同じです。<b><i><u>この後、マウスのボタンによりカウントアップされた事をSendMessage関数を使って親ウインドウに通知しています</u></i></b>。通知内容としてYKCB_NOTIFY_UPをWM_COMMANDメッセージに付加してSENDしています。
<p>
 WM_RBUTTONDOWNメッセージに対しては、カウント値のカウントダウンを行っています。これ以外は、通知されるメッセージの内容が異なるだけで、WM_LBUTTONDOWNメッセージに対するのと同じです。
<p>
 YKCB_GETCOUNTメッセージ。これは、プログラマー定義のメッセージです。WM_COMMANDメッセージに付加されて親ウインドウに通知されるメッセージと異なり、WM_USERからの変位をもって定義されなければいけません。このメッセージは、プログラム的にカウンタ値が必要な時に、SENDされます。これに対して、ウインドウプロシージャの戻り値としてカウント値を返しています。
<p>
 YKCB_SETCOUTメッセージ。これも、プログラマー定義のメッセージです。カウント値を設定したい場合にSENDされます。WPARAM型仮引数の値が、設定されるカウント値です。戻り値は、返しますが特に意味はありません。
<p>
 では、実際にこの子ウインドウを使ってみましょう。
<hr>
<p>
<b>・C13Dll.Dllを用いるサンプルアプリケーションの主要なソースコードを見てみよう</b>
<p>
 C13Dll.Dllを用いるサンプルアプリケーションの主要なソースコードを見てみましょう。重要な個所は太文字にしてあります。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト13-3(resource.rcより抜粋)
<tr><td>
<pre>

IDI_ICON1 ICON DISCARDABLE "icon1.ico"


IDR_MENU1 MENU DISCARDABLE
BEGIN
  POPUP "カウンタのリセット"
   BEGIN
       MENUITEM "左側のカウンタ",              ID_RESET_LEFT
       MENUITEM "右側のカウンタ",              ID_RESET_RIGHT
       MENUITEM "両方のカウンタ",              ID_BOTH
   END
   POPUP "ボタン押下通知"
   BEGIN
       MENUITEM "左側のカウンタ",              ID_NOTIFY_LEFT
       MENUITEM "右側のカウンタ",              ID_NOTIFY_RIGHT
   END
   POPUP "カウンタ値の取得"
   BEGIN
       MENUITEM "カウンタ値の取得",            ID_GETCOUTER
   END
   POPUP "このプログラムについて"
   BEGIN
       MENUITEM "このプログラムについて",      ID_ABOUT
   END
END
</pre>
</table>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト13-4(WndProc.c)
<tr><td>
<pre>
/*
  『C言語で始めるWindowsプログラミング』
   13章のサンプルプログラム
                   Programmed by Y.Kondo

/


#define STRICT
#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;
#include "wndproc.h"
#include "resource.h"
#include "c13dll.h"


/*===============================================================================*/

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static LRESULT Wm_CreateProc(HWND,LPCREATESTRUCT);
static LRESULT Wm_DestroyProc(HWND);
static LRESULT Wm_SizeProc(HWND,WORD,WORD);
static LRESULT Wm_CommandProc(HWND,WORD,WORD,HWND);

/* このファイル内でのみ用いられる変数 */
static HINSTANCE hdllInstance;

/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK WindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {
       case    WM_CREATE:      /*  初期化処理      */
           return  Wm_CreateProc(hwnd,(LPCREATESTRUCT)lparam);
       case    WM_DESTROY:     /*  終了処理        */  
           return  Wm_DestroyProc(hwnd);
       case    WM_SIZE:        /*  ウインドウサイズをクライアントサイズを元に変更する  */
           return  Wm_SizeProc(hwnd,LOWORD(lparam),HIWORD(lparam));
       case    <b>WM_COMMAND</b>:     /*  メニューと子ウインドウからのメッセージを受け取る    */
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

/*===============================================================================*/

/* このファイルの中でのみ用いられる関数 */

/* ウインドウ初期化処理を行う */
static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  HWND    hChildWindow;   /*  子ウインドウのウインドウハンドル    */

  /*  子ウインドウの作成  */
   hChildWindow=CreateWindow(YK_COUNTER_BUTTON,
                             "",
                             WS_CHILD|WS_VISIBLE|WS_BORDER,
                             10,10,
                             100,100,
                             hwnd,
                             <b>(HMENU)1</b>,
                             cs-&gt;hInstance,
                             NULL);
   if(hChildWindow==NULL)
       return  -1; /*  子ウインドウの作成に失敗すれば-1を返す    */
   hChildWindow=CreateWindow(YK_COUNTER_BUTTON,
                             "",
                             WS_CHILD|WS_VISIBLE|WS_BORDER,
                             120,10,
                             100,100,
                             hwnd,
                             <b>(HMENU)2</b>,
                             cs-&gt;hInstance,
                             NULL);
   if(hChildWindow==NULL)
       return  -1; /*  子ウインドウの作成に失敗すれば-1を返す    */

  return  0;
}

static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}

static LRESULT Wm_SizeProc(HWND hwnd, /* ウインドウハンドル */
                          WORD width,     /*  クライアント領域の横幅  */
                           WORD height)    /*  クライアント領域の高さ  */
{
  RECT    WindowRect;     /*  非クライアント領域をも含めたウインドウ領域のサイズ  */
   int     delta_width;    /*  ウインドウ領域とクライアント領域の横幅の差          */
   int     delta_height;   /*  ウインドウ領域とクライアント領域の高さの差          */

  /*  フレームウインドウのサイズの調整    */
   GetWindowRect(hwnd,&WindowRect;);
   delta_width=(WindowRect.right-WindowRect.left)-width;
   delta_height=(WindowRect.bottom-WindowRect.top)-height;
   MoveWindow(hwnd,WindowRect.left,WindowRect.top,230+delta_width,120+delta_height,TRUE);

  return  0;
}

static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  <b>if(hwndCtl==NULL)</b>
   {       /*  メニューからの場合  */
       switch(wID)
       {
           case    ID_RESET_LEFT:      /*  左側のカウンタをリセット    */
               SendDlgItemMessage(hwnd,1,YKCB_SETCOUNT,0,0);
               return  0;
           case    ID_RESET_RIGHT:     /*  右側のカウンタをリセット    */
               SendDlgItemMessage(hwnd,2,YKCB_SETCOUNT,0,0);
               return  0;
           case    ID_BOTH:            /*  両方のカウンタをリセット    */
               SendDlgItemMessage(hwnd,1,YKCB_SETCOUNT,0,0);
               SendDlgItemMessage(hwnd,2,YKCB_SETCOUNT,0,0);
               return  0;
           case    ID_NOTIFY_LEFT:     /*  左側のカウンタが押された事を通知するかどうか?  */
               if(GetMenuState(GetMenu(hwnd),ID_NOTIFY_LEFT,MF_BYCOMMAND)&MF;_CHECKED)
                   CheckMenuItem(GetMenu(hwnd),ID_NOTIFY_LEFT,MF_BYCOMMAND|MF_UNCHECKED);
               else
                   CheckMenuItem(GetMenu(hwnd),ID_NOTIFY_LEFT,MF_BYCOMMAND|MF_CHECKED);
               return  0;
           case    ID_NOTIFY_RIGHT:    /*  右側のカウンタが押された事を通知するかどうか?  */
               if(GetMenuState(GetMenu(hwnd),ID_NOTIFY_RIGHT,MF_BYCOMMAND)&MF;_CHECKED)
                   CheckMenuItem(GetMenu(hwnd),ID_NOTIFY_RIGHT,MF_BYCOMMAND|MF_UNCHECKED);
               else
                   CheckMenuItem(GetMenu(hwnd),ID_NOTIFY_RIGHT,MF_BYCOMMAND|MF_CHECKED);
               return  0;
           case    ID_GETCOUTER:       /*  カウンターの値を取得する    */
               {
                   char    stringbuf[200];
                   sprintf(stringbuf,
                           "Left =%6d\r\nRight=%6d",
                           SendDlgItemMessage(hwnd,1,YKCB_GETCOUNT,0,0),
                           SendDlgItemMessage(hwnd,2,YKCB_GETCOUNT,0,0));
                   MessageBox(hwnd,stringbuf,"カウンタ値",MB_OK|MB_ICONINFORMATION);
                   return  0;
               }
           case    ID_ABOUT:       /*  このプログラムについて  */
               MessageBox(hwnd,"『C言語で始めるWindowsプログラミング』\r\n"
                               "13章のサンプルプログラム\r\n"
                               "       Programmed by Y.Kondo",
                               "このプログラムについて",MB_OK|MB_ICONINFORMATION);
               return  0;
       }
   }
   else
   {   /* 子ウインドウからのメッセージの場合  */
       if(wID==1 &amp;&amp; (GetMenuState(GetMenu(hwnd),ID_NOTIFY_LEFT,MF_BYCOMMAND)&MF;_CHECKED))
       {
           if(wNotifyCode==YKCB_NOTIFY_UP)
               MessageBox(hwnd,"左側がカウントアップされました","通知",MB_OK|MB_ICONINFORMATION);
           else
               MessageBox(hwnd,"左側がカウントダウンされました","通知",MB_OK|MB_ICONINFORMATION);
           return  0;
       }
       else if(wID==2 &amp;&amp; (GetMenuState(GetMenu(hwnd),ID_NOTIFY_RIGHT,MF_BYCOMMAND)&MF;_CHECKED))
       {
           if(wNotifyCode==YKCB_NOTIFY_UP)
               MessageBox(hwnd,"右側がカウントアップされました","通知",MB_OK|MB_ICONINFORMATION);
           else
               MessageBox(hwnd,"右側がカウントダウンされました","通知",MB_OK|MB_ICONINFORMATION);
           return  0;
       }
   }
   return  0;
}
</pre>
</table>
<p>
 DLLと異なり、DLLを用いるサンプルアプリケーションは、目立って新しい所は無いでしょう。ただ、API関数がどっか~んと出てきた印象を受ける方は居るかもしれませんね。したがって、ここでは、主要なAPI関数やメッセージについて説明しましょう。
<p>
<b>・CreateWindow関数</b>
<p>
 CreateWindow関数の9番目の引数が重要です。本来、ここは、メニューハンドルを入れる場所です。しかし、子ウインドウを作成する場合、子ウインドウはメニューを持てないという性質から、別の意味になります。つまり、子ウインドウの識別子(コントロールID)になるのです。
<p>
 子ウインドウと言うのは、単にクライント領域内に表示されるウインドウというより、メニューに近い情報源なのです。従って、子ウインドウで発生した事象を区別する為に、識別子が必要なのです。
<p>
<b>WM_COMMANDメッセージの付加情報について</b>
<p>
 WM_COMMANDメッセージは、3つの付加情報を持っています。
 つまり、
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
・通知コード(通知メッセージ)              =HIWORD(wparam)<br>
・メニューアイテム、子ウインドウ識別子(コントロールID)=LOWORD(wparam)<br>
・子ウインドウ(コントロール)のウインドウハンドル    =(HWND)lparam<br>
</table>
<p>
 通知コード(通知メッセージ)は、メニューから送られた場合は、0です。アクセラレータ(オペレータからはショートカットキー:いずれ説明します)から送られた場合は、1です。通知コードは、主に子ウインドウで有意な事象が起こった事を伝えるのに利用されます。
<p>
 子ウインドウ(コントロール)のウインドウハンドルは、子ウインドウ以外からWM_COMMANDメッセージが送られた場合はNULLです。
<p>
<b>GetMenuState関数</b>
<p>
 メニューの状態を取得するAPI関数です。第一引数は、メニューハンドルです。今回の場合は、すべてGetMenu関数でウインドウハンドルから求めています。第二引数は、知りたいメニューアイテム。これの判定の方法は第三引数で変ります。第三引数は、第二引数を、メニュー識別子と判断するかメニューアイテムの0ベースのオフセットと判断するかを決定します。今回は、MF_BYCOMMAND。つまり、メニュー識別子でもってメニューアイテムを識別しています。
<p>
 戻り値は、UINTで、マスクをかける事で、メニューの状態を知る事が出来ます。今回の場合、MF_CHECKEDビットが立っているかどうかでメニューの状態を判断しています。
<p>
<b>・CheckMenuItem関数</b>
<p>
 メニュー項目の横にチェックマークを付けたり消したりするAPI関数です。第一引数は、メニューハンドルです。今回の場合は、すべてGetMenu関数でウインドウハンドルから求めています。第二引数は、チェックマークを付けたり消したりしたいメニューアイテム。これの判定の方法は第三引数で変ります。第三引数は、第二引数を、メニュー識別子と判断するかメニューアイテムの0ベースのオフセットと判断するかを決定します。また、そのメニューアイテムをチェックするかどうかは、MF_CHECKEDやMF_UNCHECKEDと論理和で結合する事で行います。
<p>
<b>・MessageBox関数</b>
<p>
 OS定義の簡易ウインドウです。ちょっとした情報をオペレータに伝えたり、オペレーターの判断を仰いだりするのに使います。
<p>
 第一引数は、親ウインドウのウインドウハンドル。第二引数は、クライアント領域に表示される文字列へのポインタ。第三引数は、タイトルバーに表示される文字列へのポインタ。第四引数は、ウインドウのボタンやアイコンの表示を決定するフラグで、論理和でビットを結合して用います。今回の場合は全て、MB_OKとMB_ICONINFORMATIONを用いています。つまり、OKボタンと情報のiマークが表示されています。
<p>
<b>・ウインドウクラスを定義しているDLLを明示的にロードするタイミング</b>
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト13-5(WinMain.cより抜粋)
<tr><td>
<pre>
/* アプリケーションエントリーポイント */
int WINAPI WinMain(HINSTANCE hInstance,
                 HINSTANCE    hPrevInstance,
                  LPSTR        CmdLine,
                  int          CmdShow)
{
  HWND            hwnd;       /*  メインウインドウのウインドウハンドル    */
   MSG             msg;        /*  メッセージキューから取得したメッセージ  */
   HINSTANCE       hInstDll;   /*  DLLのインスタンスハンドル            */

  /*  メインウインドウのウインドウクラスを登録    */
   if(RegMainWindowClass(hInstance)==0)
       return  0;

  /*  DLLのロード  */
   <b>hInstDll=LoadLibrary("c13dll.dll");</b>
   if(hInstDll==NULL)
       return  0;

  hwnd=CreateWindow(  "MainWindowClass",  /*  ウインドウ作成          */  
                       "13章のサンプルプログラム",
                       WS_OVERLAPPED|WS_SYSMENU|WS_BORDER,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       0,  /*  サイズは後から決めるので    */
                       0,  /*  サイズは後から決めるので    */
                       NULL,
                       LoadMenu(hInstance,MAKEINTRESOURCE(IDR_MENU1)),
                       hInstance,
                       0);
   if(hwnd==NULL)
       return  0;

  ShowWindow(hwnd,CmdShow);           /*  ウインドウの表示        */
   UpdateWindow(hwnd);                 /*  ウインドウの最初の更新  */

  while(GetMessage(&msg;,NULL,0,0))    /*  メッセージループ        */
   {
       TranslateMessage(&msg;);
       DispatchMessage(&msg;);
   }

  /*  DLLの開放    */
   <b>FreeLibrary(hInstDll);</b>

  return  msg.wParam;
}
</pre>
</table>
<p>
 子ウインドウのウインドウクラスを定義しているDLLをロードするタイミングは、アプリケーションエントリーポイントが安全です。
<hr>
<p>
<b>・Windowsプログラミングをするとは</b>
<p>
 この講座はまだまだ続きますが、ここまでのまとめをしてみましょう。
<p>
 最初は、見なれないWinMain関数やウインドウクラスの登録などの作業に翻弄され、挙げ句の果てには、DOSなどOSレベルで事象駆動を要求しない環境でアプリケーションを組んできたプログラマからは、Windowsプログラミングというのは窮屈さを覚えるだけだったと思います。
<p>
 しかし、Windowsプログラミングをここまで学んできて、理解して欲しいのは、<b><i><u>Windowsプログラミングをする事はオブジェクト指向プログラミング(OOP)をする事なんだ</u></i></b>という事です。C++とMFCでWindowsのアプリケーションを開発しているなら、誰でもOOPを行っていると認知してくれるでしょう。しかし、C言語でWindowsプログラミングをしていても、そうとは全員が認知してくれないでしょう。しかし、現実は、C言語でも、<b><i><u>構築素材として与えられたAPI関数群を用いてOOPする事がWindowsプログラミングなんです</u></i></b>。
<p>
 まず、ウインドウクラスを作成します。これは、いわば型ですね。そして、CreateWindow関数でそのインスタンスを作成します。そして、そのインスタンスにメッセージを送って、インスタンスを動かす。また、ここのインスタンスは状態を持ちます。同じメッセージを与えても、その状態によって、その挙動は違ってきます。また、ウインドウクラスはDLLとしてコンポーネント化できます。
<p>
 今回作成した様な機能を持った子ウインドウの事を、Windowsプログラミング用語では、<b><i><u>コントロール</u></i></b>と呼びます。実際は、今回のように、1からコントロールを作成するのは希です。なぜなら、Windowsが有益なコントロールを数多く用意してくれているからです。そして、この事により、プログラマは煩雑なコーディングから開放され、オペレーターは異なるアプリケーションをよく似た操作で使えるという恩恵が与えられるのです。よほど既存のコントロールに不満を覚えない限り、1からコントロールを作成する事はOOPの考え方からも避けるべきと思います。もし、どうしても不満が生じた場合、<b><i><u>サブクラス化</u></i></b>という手法で、<b><i><u>差分プログラミング</u></i></b>を考慮に入れるべきです。これは、いずれこの講座で説明いたします。
<hr>
<p>
 やっと、Windowsプログラミングの感触がつかめてきたのではないでしょうか?
<p>
 次回は、一休みという事で、今まで説明したかったが出来なかった事を説明しようと思います。
<p>
 ではお楽しみに。
<p align="RIGHT">
1999年11月05日<br>
加筆修正1999年11月13日<br>
修正1999年11月29日<br>
<hr>
<p>
<b>・1999年11月13日の加筆修正について</b>
<p>
 すみません、またバグを出してしまいました。
<p>
 WM_DESTROYメッセージに応答する形で、子ウインドウクラスを定義しているDLLを開放してはいけませんでした。なぜなら、<b><i><u>親ウインドウにWM_DESTROYメッセージが呼ばれたときは、まだその子ウインドウは存在するからです</u></i></b>。
<p align="RIGHT">
1999年11月13日<br>
<hr>
<p align="RIGHT">
<a href="index.html">目次</a><br>
<a href="chap14.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 04:42:15

このWIKIを編集するにはパスワード入力が必要です

認証パスワード