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

<html>

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

<body bgcolor="WHITE">
<p>
<font size="5">9章 Windowsアプリケーションの骨格(その6)</font>
<hr>
<p>
 長い間、更新を怠っていました。本業が忙しく、更新どころでは無かったのです。
<p>
 さて、前回は動的に生成破壊されるオーナー付きウインドウを通して、ウインドウ間のメッセージのやり取りについて勉強しました。今回から数章に渡って、機能の分割・再利用の為の子ウインドウを説明しようと思います。今回は、その第一段階です。
<p>
 子ウインドウというと、<b><i><u>MDI(マルチプル・ドキュメント・インターフェース)</u></i></b>の様にユーザーがマウスを使って自由に移動できるウインドウを連想すると思います。しかし、これは、Windowsプログラミングをしていない全くのユーザー(オペレーター)からの視点です。<b><i><u>全くのオペレーターから見れば、1つのウインドウから成る様に見えるアプリケーションも、1つの親ウインドウと1つの子ウインドウから構成される場合が多々あります。なぜなら、この様な構成にする事により、プログラムの再利用がしやすくなるからです。</u></i></b>
<p>
 では、マウスの左ボタンによりカウントアップされ、マウスの右ボタンによりカウントダウンされるアプリケーションを例に、説明を始めます。
<p>
 今回もサンプルプログラムを用意しましたので、<a href="chap9.lzh">ここ</a>を押してダウンロードしてください。
<hr>
<p>
 さて、今回のサンプルプログラムの画面を以下に示します。<br>
<p>
 素人目には全く1つだけのウインドウからなるアプリケーションに見えますね。
<p>
 しかし、ソースファイルを覗くと、<b><i><u>マウスのボタンに反応してカウントアップしたりカウントダウンしたりするウインドウをフレームとなるウインドウが所有している構造</u></i></b>に気が付くと思います。
<p>
 再利用可能なカウンター部分を提示しますと、リスト9-1とリスト9-2になります。リスト9-1がSWndProc.cで、リスト9-2がそのヘッダファイルになるのです。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト9-1(SWndProc.c)
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 9章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */

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

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

/* このファイル内でのみ用いられる関数のプロトタイプ宣言 */
static LRESULT Wm_PaintProc(HWND);
static LRESULT Wm_LButtonDown(HWND);
static LRESULT Wm_RButtonDown(HWND);

/* 子のウインドウプロシージャ */
LRESULT CALLBACK SubWindowProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)
{
  switch(message)
   {   
       case    WM_PAINT:           /*  描画    */
           return  Wm_PaintProc(hwnd);
       case    WM_LBUTTONDOWN:     /*  マウスの左ボタン押下    */
           return  Wm_LButtonDown(hwnd);
       case    WM_RBUTTONDOWN:     /*  マウスの右ボタン押下    */
           return  Wm_RButtonDown(hwnd);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

/* このファイルの中でのみ用いられる変数 */
static int Counter=0;

/* このファイルの中でのみ用いられる関数 */
static LRESULT Wm_PaintProc(HWND hwnd)
{
  PAINTSTRUCT ps;
   HDC         DC;
   RECT        ClientRect;
   TEXTMETRIC  Tm;

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

  wsprintf(buf,"%d",Counter);         /*  数値を文字列に変換する                      */
                                       /*  泥臭い方法:「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)
{
  Counter++;
   InvalidateRect(hwnd,NULL,TRUE);
   return  0;
}

/* マウスの右ボタンが押された時、カウントダウンする */
static LRESULT Wm_RButtonDown(HWND hwnd)
{
  Counter--;
   InvalidateRect(hwnd,NULL,TRUE);
   return  0;
}

/*-----------------------------------------------------------------------*/

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

  wc.style        =CS_HREDRAW|CS_VREDRAW; 
   wc.lpfnWndProc  =SubWindowProc;
   wc.cbClsExtra   =0;
   wc.cbWndExtra   =0;
   wc.hInstance    =hInstance;
   wc.hIcon        =NULL;
   wc.hCursor      =LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR1));
   wc.hbrBackground=(HBRUSH)GetStockObject(LTGRAY_BRUSH);
   wc.lpszMenuName =NULL;
   wc.lpszClassName="SubWindowClass";
   
   return  RegisterClass(&wc;);     /*  ウインドウクラス登録    */
}
</pre>
</table>

<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト9-2(SWndProc.h)
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 9章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */

extern LRESULT CALLBACK SubWindowProc(HWND,UINT,WPARAM,LPARAM);
extern ATOM RegSubWindowClass(HINSTANCE);
</pre>
</table>
<hr>
<p>
 さて、今回登場した新しいメッセージやWIN32API関数について説明を加えます。
<p>
 今回のサンプルプログラムが、あたかも1つのウインドウから成るように見えるのは、常にカウンターとして機能する子ウインドウが、フレームウインドウ(親ウインドウ)のクライアント領域のサイズと同じになるからです。これを行っているのが、リスト9-3の太文字部分です。

<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト9-3(WndProc.c)
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 9章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */
/* このファイルでは、メインウインドウのウインド*/
/*ウプロシージャが定義されている */

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

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

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

/* メインウインドウのウインドウプロシージャ */
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);
       <b>case WM_SIZE:
           return  Wm_SizeProc(hwnd,wparam,LOWORD(lparam),HIWORD(lparam));</b>
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

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

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

  /*  子ウインドウの作成  */
   GetClientRect(hwnd,&ClientRect;);
   hChildWindow=CreateWindow("SubWindowClass",
                             "",
                             WS_CHILD|WS_VISIBLE,
                             ClientRect.left,
                             ClientRect.top,
                             ClientRect.right,
                             ClientRect.bottom,
                             hwnd,
                             <u>(HMENU)1</u>,
                             cs-&gt;hInstance,
                             NULL);
   if(hChildWindow==NULL)
       return  -1; /*  子ウインドウの作成に失敗すれば-1を返す    */

  return  0;
}


static LRESULT Wm_DestroyProc(HWND hwnd)
{
  PostQuitMessage(0);
   return  0;
}
<b>
static LRESULT Wm_SizeProc(HWND hwnd,WPARAM fwSizeType,WORD nWidth,WORD nHeight)
{
  MoveWindow(<u>GetDlgItem(hwnd,1)</u>,0,0,nWidth,nHeight,TRUE);
   return  0;
}
</b>
</pre>
</table>
<p>
<b>・WM_SIZEメッセージ</b>
<p>
 WM_SIZEメッセージというのは、<b><i><u>ウインドウのサイズが変更された後</u></i></b>に送られてくるメッセージです。このメッセージは付加情報として、サイズが変更された理由と変更後のクライアント領域のサイズを持ちます。結局、このメッセージに応答して、子ウインドウの大きさをクライアントサイズに変更する事で、あたかも1つのウインドウから成るように見せるのです。
<p>
<b>・MoveWindow関数</b>
<p>
子ウインドウのサイズを変更するのに、MoveWindow関数。なんかピンときませんね。でも、サイズだけ変更するAPIが無いのですから仕方がありません。この関数を用いて、子ウインドウのサイズを変更します。子ウインドウですから、座標系は親ウインドウのクライアント座標です。<br>
 さて、ここで、GetDlgItem関数なる物が使われていますが、以下で説明いたします。
<p>
<b>・GetDlgItem関数</b>
<p>
 さて、MoveWindow関数の引数として使われていたGetDlgItem関数。これの意味が分からなければ、MoveWindow関数を理解できませんよね。<br>
 さて、リスト9-3の下線部を見てください。最初に下線部が用いられているのは、CreateWindow関数の中ですね。本来ならメニューハンドルを設定する場所にキャストをかけて1としています。<br>
 子ウインドウはメニューを持つ事が出来ませんから、この1は何なのでしょうか?これは、子ウインドウを識別する為にプログラマが定義した値です。したがって、子ウインドウのウインドウハンドルをグローバル変数にして持たなくても、子ウインドウの識別子で、操作したいウインドウを指定できるのです。<br>
 しかし、MoveWindow関数は、子ウインドウのウインドウハンドルを引数として要求しています。ここで親ウインドウのウインドウハンドルと子ウインドウの識別子から、子ウインドウのウインドウハンドルを取得するAPI関数がGetDlgItemなのです。実際、APIのリファレンスを読んでみるとダイアログボックスに特化したようなイメージを持たせるような表現が使われていますが、いたって汎用的な関数です。

<hr>
<p>
 今回は、一見、1つのウインドウだけからでも作れるアプリケーションを実際の機能を受け持つ子ウインドウとそれを所有するフレームウインドウに分ける事で、本来の機能を受け持つ部分を分割し再利用し易くなる事を示唆しました。
<p>
 次回は、せっかく機能の分割・再利用をしようと子ウインドウ化した部分にとんでもない欠点がある事を示し、これに対する対処方法を示します。
<p>
 では、お楽しみに。

<p align="RIGHT">
1999年9月18日<br>
加筆修正1999年9月19日<br>
加筆修正1999年9月29日<br>
加筆修正1999年11月10日<br>
修正2002年8月10日<br>
<hr>
<p align="RIGHT">
<a href="index.html">目次</a><br>
<a href="chap10.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 04:25:48

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

認証パスワード