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

<html>

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

<body bgcolor="WHITE">
<p>
<font size="5">8章 Windowsアプリケーションの骨格(その5)</font>
<hr>
<p>
<center>
</center>
<p>
 7章では、オーナー付きウインドウと子ウインドウの作成と特徴について学びました。
<p>
 今回は、オーナー付きウインドウを例に、メインウインドウ(アプリケーションを終了さすウインドウ)以外のウインドウの動的な生成と破壊について学びます。そして、その過程で、ウインドウ間のメッセージのやり取りの方式について学びます。
<p>
 今回もサンプルプログラムを用意しましたので、<a href="chap8.lzh">ここ</a>を押してダウンロードしてください。
<hr>
<p>
 7章のサンプルプログラムは、メニューによってオーナー付きウインドウの表示・非表示の切り替えが出来ました。つまり、オーナー付きウインドウその物は、オーナーウインドウが破壊されるまで存在し続けました。それゆえ、オーナー付きウインドウは、メモリーを使い続けていたわけです。
<p>
 今回は、オーナー付きウインドウを必要な時に作成し不必要になれば破壊できるようにします。
<p>
 実際にサンプルを動かせば分かると思いますが、今回のオーナー付きウインドウには[×]ボタンがあるのが特徴です。つまり、オーナー付きウインドウに対する操作でオーナー付きウインドウを破壊できるのです。
<p>
 今回のプログラムの仕様が、何回でもオーナー付きウインドウを生成できて良いなら、極めて簡単です。つまり、メニューの[オーナー付きウインドウ]→[生成]に対するWM_COMMANDメッセージに応答するだけでよいのです。しかし、同じウインドウが何枚も画面に表示されているのは不自然です。そこで、1個しかオーナー付きウインドウを表示できないようにしました。
<p>
 そこで、オーナー付きウインドウが破壊された事をオーナーウインドウに伝える手段としてユーザー定義のメッセージを用いるのです。
<hr>
<p>
 では、ソースファイルの重要な個所を見てみましょう。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト8-1
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 8章のサンプルプログラム */
/* 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_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl);
static LRESULT Wm_OdestroyProc(HWND);

/* メインウインドウのウインドウプロシージャ */
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_COMMAND:     /*  メニューに応答  */
           return  Wm_CommandProc(hwnd,HIWORD(wparam),LOWORD(wparam),(HWND)lparam);
       <b>case    WM_ODESTROY:</b>    /*  オーナー付きウインドウが破壊された事に対するメッセージ  */
           <b>return  Wm_OdestroyProc(hwnd)</b>;
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

/* このファイルの中でのみ用いられる変数 */
static HWND hOwnedWnd=NULL; /* オーナー付きウインドウのハンドル */
static CREATESTRUCT sCs; /* 初期化情報のコピーを格納 */

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  sCs=*cs;                            /*  初期化情報のコピー  */
   return  0;
}

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

static LRESULT Wm_CommandProc(HWND hwnd,WORD wNotifyCode,WORD wID,HWND hwndCtl)
{
  switch(wID)
   {
       case    ID_CREATEWINDOW:
       {
           hOwnedWnd=CreateWindow( "SubWindowClass",
                                   "オーナー付きウインドウ",
                                   WS_OVERLAPPEDWINDOW,
                                   CW_USEDEFAULT,
                                   CW_USEDEFAULT,
                                   CW_USEDEFAULT,
                                   CW_USEDEFAULT,
                                   hwnd,
                                   (HMENU)NULL,
                                   sCs.hInstance,
                                   0);
           ShowWindow(hOwnedWnd,SW_SHOW);
           /*  BUG 2002/09/27
           EnableMenuItem(GetMenu(hwnd),MF_BYCOMMAND|ID_CREATEWINDOW,MF_GRAYED);
           */
           EnableMenuItem(GetMenu(hwnd),ID_CREATEWINDOW,MF_BYCOMMAND|MF_GRAYED); 
           break;
       }
   }
   return  0;
}

<b>static LRESULT Wm_OdestroyProc(HWND hwnd)
{
  /*  BUG 2002/09/27
   EnableMenuItem(GetMenu(hwnd),MF_BYCOMMAND|ID_CREATEWINDOW,MF_ENABLED);
   */
   EnableMenuItem(GetMenu(hwnd),ID_CREATEWINDOW,MF_BYCOMMAND|MF_ENABLED);
   hOwnedWnd=NULL;
   return  0;
}</b>
</pre>
</table>
<p>
 リスト8-1はWNDPROC.Cです。見るべきところは、太文字にしてあります。つまり、メインウインドウのウインドウプロシージャはWM_ODESTROYなるメッセージに応答している訳です。しかし、WM_ODESTROYなるメッセージは、WIN32APIのリファレンスマニュアルを調べても見つかりません。何故でしょうか?これは、プログラマ、つまり私が定義したメッセージだからです。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト8-2
<tr><td>
<pre>
/* C言語で始めるWindowsプログラミング */
/* 8章のサンプルプログラム */
/* Programmed by Y.Kondo */
/* 注:TABサイズは4で見てください */
/* このファイルでは、メインウインドウのウインド*/
/*ウプロシージャが定義されている */

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

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

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

/* メインウインドウのウインドウプロシージャ */
LRESULT CALLBACK SubWindowProc(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);
   }
   return  DefWindowProc(hwnd,message,wparam,lparam);
}

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

/* このファイルの中でのみ用いられる変数 */
static CREATESTRUCT sCs; /* 初期化情報のコピーを格納 */

static LRESULT Wm_CreateProc(HWND hwnd,LPCREATESTRUCT cs)
{
  sCs=*cs;                            /*  初期化情報のコピー  */
   return  0;
}

<b>static LRESULT Wm_DestroyProc(HWND hwnd)
{
  /*  オーナーウインドウに自分が死んだ事を伝える  */
   SendMessage(sCs.hwndParent,WM_ODESTROY,0,0L);
   return  0;
}</b>
</pre>
</table>
<p>
 リスト8-2はSWNDPROC.Cです。これは、オーナー付きウインドウのウインドウプロシージャです。太文字の個所は、オーナー付きウインドウのウインドウプロシージャが、WM_DESTROYメッセージに応答している部分です。オーナー付きウインドウが破壊される事は、アプリケーションが終了する事とは無関係ですから、オーナーウインドウの様に、PostQuitMessage関数はありません。しかし、変わりにと言っては何ですが、SendMessage関数があります。これが、今回の主役となる関数です。
<p>
 SendMessage関数は、読んで字のごとくメッセージを送る関数です。細かな事はさて置き、この関数が、オーナーウインドウのウインドウプロシージャに対してメッセージを送るのです。従って、仮引数は、ウインドウプロシージャと同じですね。
<p>
 では、WM_ODESTROYはどこで定義しているのでしょうか?この定数は、オーナーウインドウとオーナー付きウインドウのどちらからも見える必要があるので、SWNDPROC.Hで以下のように定義しています。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
#define WM_ODESTROY WM_USER+0
</pre>
</table>
<p>
 また、WM_USERという見慣れないメッセージ(定数)が出てきましたね。これは、後程説明しましょう。
<p>
 以上、アプリケーションの設計の方針とソースの概略を説明しました。以下、今回の主役となるSendMessage関数などについて説明します。
<hr>
<p>
<b>・メッセージの伝達</b>
<p>
 今までのアプリケーションは、ウインドウプロシージャがメッセージを処理するだけでした。しかし、今回のアプリケーションは、メッセージを他のウインドウに向けて送っている事が特徴です。
<p>
 WIN32APIのリファレンスマニュアルを調べれば分かりますが、メッセージを送る側のAPI関数には大きく二つの系統がある事が分かります。1つはSend系、もう1つはPost系です。そして、Send系の中で代表的なのがSendMessage関数で、Post系の中で代表的なのがPostMessage関数です。
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
LRESULT SendMessage(

  HWND hWnd,  // handle of destination window
   UINT Msg,   // message to send
   WPARAM wParam,  // first message parameter
   LPARAM lParam   // second message parameter
  );
</pre>
</table>
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<tr><td>
<pre>
BOOL PostMessage(

  HWND hWnd,  // handle of destination window
   UINT Msg,   // message to post 
   WPARAM wParam,  // first message parameter
   LPARAM lParam   // second message parameter
  );
</pre>
</table>
<p>
 Send系のメッセージ伝達方法は、ある意味、単なるウインドウプロシージャ呼び出しと考えられます。つまり、メッセージが伝えられる方のウインドウプロシージャはDispatchMessage関数から呼ばれるのではないのです。
<p>
 これに対して、Post系の伝達方法は、メッセージをメッセージキューに置く方式をとります。つまり、メッセージはメッセージループを経由するのです。従って、Post系のメッセージは、Send系の様な一種の関数呼び出しでは無いので、メッセージの送り先からの戻り値を取得する事は出来ません。
<p>
 実際に、WIN32APIのリファレンスマニュアルを読むと、スレッドの概念が絡んできて、かなり複雑です。つまり、Send系・Post系と分けるだけでなく、同期系・非同期系という分類も考えられます。しかし、シングルスレッドで考えるなら、あまり深く考える必要はなさそうです。
<p>
 良く使うメッセージ伝達関数は、SendMessage関数、SendDlgItemMessage関数PostMessage関数、そしてPostQuitMessage関数の4つでしょう。
<hr>
<p>
<b>・WM_USER</b>
<p>
 今回用いたWM_USERについて。
<p>
 Windowsのメッセージというのは、単なる符号無し整数にすぎません。これは、ウインドウプロシージャの引数の型を見れば分かるでしょう。しかし、SDKでは、これをシンボリックな文字に置き換えています。つまり、どのメッセージが、どのような値の整数かは分からないし、調べても将来的に変わる可能性があります。
<p>
 そこで、WM_USERというのは、ここからはユーザー定義のメッセージとして使ってよいというしきい値なのです。従って、ユーザー定義のメッセージは、WM_USERからの変位をもって表現する事になります。
<hr>
<p>
 今回は、動的に生成破壊されるオーナー付きウインドウを通して、ウインドウ間のメッセージのやり取りについて勉強しました。次回は、機能の分割・再利用の為の子ウインドウを説明する予定です。お楽しみに。
<p align="RIGHT">
1999年05月13日<br>
加筆修正1999年11月08日<br>
画像追加2001年8月19日<br>
修正2002年09月27日<br>
<hr>
<p align="RIGHT">
<a href="index.html">目次</a><br>
<a href="chap9.html">次へ</a>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

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

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

認証パスワード