26章 VisualBASIC感覚でSDKプログラミング(その4)

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=shift_jis"></meta>
<title>26章 VisualBASIC感覚でSDKプログラミング(その4)</title>
</head>

<body bgcolor="WHITE">
<font size="5">26章 VisualBASIC感覚でSDKプログラミング(その4)</font>
<hr>
<p align="CENTER">
<p>
 今回も、23章で紹介した簡易スケジューラーの細部に渡って説明していきます。<br>
 前章と異なり、もっとも動作の本命となる箇所の説明です。<br>
 事象駆動型アプリケーションの考え方が分かっていないと、かなり戸惑うと思います。<br>
 しかし、VisualBASICを始めとするRADツールにも同様の戸惑いがあり、侮れない事が分かれば成功です。
<hr>
<p>
<b>
・WM_COMMANDメッセージの復習
</b>
<p align="CENTER">
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト26-1
<tr><td>
<pre>
WM_COMMAND
wNotifyCode = HIWORD(wParam); // notification code
wID = LOWORD(wParam); // item, control, or accelerator identifier
hwndCtl = (HWND) lParam; // handle of control
</pre>
</table>
<p>
 マニュアルによると、WM_COMMANDは、リスト26-1の意味を持ちます。<br>
 wParamのハイワードは通知メッセージ、wParamのローワードはコントロール識別子、そしてlParamはコントロールのウインドウハンドルです。lParamは、6章の様にメニューから発生したWM_COMMANDの時は、NULLを返します。
<hr>
<p>
 さて、メインウインドウとなるダイアログプロシージャを<b>分解</b>してみましょう。
<p>
 まずは、リストボックスがダブルクリックされた場合です。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト26-2
<tr><td>
<pre>
  if(wID==IDC_LIST1 &amp;&amp; wNotifyCode==LBN_DBLCLK)
   {   /*  リストボックスがダブルクリックされた場合    */
       char    buf[11];    /*  リストボックスからの文字列の取得用  */
       char*   filename;
       int     CurrentPos;
       FILE    *fp;
       int     filesize;
       char*   textbuffer;
       char*   forcombostring;

      CurrentPos=SendDlgItemMessage(hwnd,IDC_LIST1,LB_GETCURSEL,0,0);
       SendDlgItemMessage(hwnd,IDC_LIST1,LB_GETTEXT,CurrentPos,(LPARAM)buf);
       filename=ForFileType((ForDateFileType(buf)));   /*ここまででロードするファイル名を作成する*/

      /*  ロードする  */
       filesize=GetBufferSize(filename);
       textbuffer=malloc(filesize+1);
       fp=fopen(filename,"rb");
       fread(textbuffer,1,filesize,fp);
       fclose(fp);
       textbuffer[filesize]='\0';
       SendDlgItemMessage(hwnd,IDC_EDIT1,WM_SETTEXT,0,(LPARAM)textbuffer);
       free(textbuffer);

      /*  コンボボックスの設定    */
       forcombostring=strtok(buf,"/");
       SendDlgItemMessage(hwnd,IDC_COMBO1,CB_SELECTSTRING ,0,(LPARAM)forcombostring);
       forcombostring=strtok(NULL,"/");
       SendDlgItemMessage(hwnd,IDC_COMBO2,CB_SELECTSTRING ,0,(LPARAM)forcombostring);
       forcombostring=strtok(NULL,"/");
       SendDlgItemMessage(hwnd,IDC_COMBO3,CB_SELECTSTRING ,0,(LPARAM)forcombostring);
   }
</pre>
</table>
<p>
 単純ですね。ファイル名を作成して、ファイルを(バイナリーモードで)ロードして、エディットコントロールに流し込み、選択した日付をコンボボックスに設定するだけです。

<p>
 次は、3つの予定日のコンボボックスが変更された場合です。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト26-3
<tr><td>
<pre>
  else if((wID==IDC_COMBO1 | | wID==IDC_COMBO2 | | wID==IDC_COMBO3) &amp;&amp; wNotifyCode==CBN_SELCHANGE )
   {   /*  3つのどれかのコンボボックスが変更された場合    */
       /*  日付に変更があれば、元のファイルをロードする    */
       LONG    indexyear;
       LONG    indexmonth;
       LONG    indexday;
       LONG    item;
       char    year[5];
       char    month[3];
       char    day[3];

      /*  コンボボックスに空白が無いか調べる  */
       indexyear=SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETCURSEL,0,0);
       indexmonth=SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETCURSEL,0,0);
       indexday=SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETCURSEL,0,0);
       if(indexyear==CB_ERR | | indexmonth==CB_ERR | | indexday==CB_ERR)
           return  TRUE;
       
       /*  コンボボックスの文字列を取得する    */
       SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETLBTEXT,indexyear,(LPARAM)year);
       SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETLBTEXT,indexmonth,(LPARAM)month);
       SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETLBTEXT,indexday,(LPARAM)day);

      /*  リストボックスに該当するものが無いか調べる  */
       if((item=SendDlgItemMessage(hwnd,IDC_LIST1,LB_FINDSTRING,0,(LPARAM)ToListBoxStyle(year,month,day)))!=LB_ERR)
       {   /*  該当する予定日があった場合  */
           FILE    *fp;
           char*   filename;
           LONG    filesize;
           char*   textbuffer;

          /*  ロードする  */
           filename=ForFileType(ForDateFileType(ToListBoxStyle(year,month,day)));
           filesize=GetBufferSize(filename);
           textbuffer=malloc(filesize+1);
           fp=fopen(filename,"rb");
           fread(textbuffer,1,filesize,fp);
           fclose(fp);
           textbuffer[filesize]='\0';
           SendDlgItemMessage(hwnd,IDC_EDIT1,WM_SETTEXT,0,(LPARAM)textbuffer);
           free(textbuffer);
       }
       else
       {
           if(SendDlgItemMessage(hwnd,IDC_EDIT1,WM_GETTEXTLENGTH,0,0)!=0)
               if(MessageBox(hwnd,"該当する日にちは、登録されていません\r\n"
                                   "予定項目画面を消去しますか?","メッセージ",
                                   MB_YESNO|MB_ICONQUESTION)==IDYES)
                   SendDlgItemMessage(hwnd,IDC_EDIT1,WM_SETTEXT,0,(LPARAM)"");
       }
   }
</pre>
</table>

<p>
 コンボボックスを変更した結果、登録済みカレンダーであるリストボックスに該当する物があれば、リストボックスをダブルクリックしたのと同じ様に、ファイルをロードして、エディットコントロールに表示します。
<p>
 もし、リストボックスに該当する物が無ければ、エディットコントロールを消去するか尋ねてきます。何故かというと、日にちは違うが同じ内容の予定も考えられるからです。例えば、歯科などの予約です。

<p>
 今度は登録ボタンを押された場合です。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト26-4
<tr><td>
<pre>
  else if(wID==IDC_BUTTON1 &amp;&amp; wNotifyCode==BN_CLICKED)
   {   /*  登録ボタンが押された場合    */
       FILE    *fp;
       char*   filename;
       LONG    filesize;
       char*   textbuffer;
       LONG    indexyear;
       LONG    indexmonth;
       LONG    indexday;
       char    year[5];
       char    month[3];
       char    day[3];

      /*  コンボボックスに空白が無いか調べる  */
       indexyear=SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETCURSEL,0,0);
       indexmonth=SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETCURSEL,0,0);
       indexday=SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETCURSEL,0,0);
       if(indexyear==CB_ERR | | indexmonth==CB_ERR | | indexday==CB_ERR)
       {
           MessageBox(hwnd,"予定日を選択してください","注意",MB_OK|MB_ICONEXCLAMATION);
           return  TRUE;
       }
       /*  エディットコントロールから文字列を取得する  */

      <b>/*  BUG
       filesize=SendDlgItemMessage(hwnd,IDC_EDIT1,WM_GETTEXTLENGTH,0,0)+1;
       textbuffer=malloc(filesize);
       SendDlgItemMessage(hwnd,IDC_EDIT1,WM_GETTEXT,filesize,(LPARAM)textbuffer);
       */

      /*  BEGIN DEBUG */
       filesize=SendDlgItemMessage(hwnd,IDC_EDIT1,WM_GETTEXTLENGTH,0,0);
       textbuffer=malloc(filesize+1);
       SendDlgItemMessage(hwnd,IDC_EDIT1,WM_GETTEXT,filesize+1,(LPARAM)textbuffer);
       /*  END   DEBUG */</b>

      /*  ファイル名を作成する    */
       SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETLBTEXT,indexyear,(LPARAM)year);
       SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETLBTEXT,indexmonth,(LPARAM)month);
       SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETLBTEXT,indexday,(LPARAM)day);
       /*  過去は予約できない  */
       if(strcmp(ToListBoxStyle(year,month,day),NowDate2())&lt;0)
       {
           MessageBox(hwnd,"過去は予約できません","(笑)",MB_OK|MB_ICONSTOP);
           free(textbuffer);
           return  TRUE;
       }
       /*  ファイルに書き込む:ここから    */
       filename=ForFileType(ForDateFileType(ToListBoxStyle(year,month,day)));
       fp=fopen(filename,"wb");
       fwrite(textbuffer,1,filesize,fp);
       fclose(fp);
       free(textbuffer);
       /*  ファイルに書き込む:ここまで    */
       /*  リストボックスに追加する    */
       if(SendDlgItemMessage(hwnd,IDC_LIST1,LB_FINDSTRING,0,(LPARAM)ToListBoxStyle(year,month,day))==LB_ERR)
           SendDlgItemMessage(hwnd,IDC_LIST1,LB_ADDSTRING,0,(LPARAM)ToListBoxStyle(year,month,day));
       /*  予定日コンボボックスと予定エディットコントロールを初期化    */
       InitInput(hwnd);
   }
</pre>
</table>
<p>
 まさにプログラムソースのコメント通りで、コンボボックスに正規の(過去でない)項目が選択されているかを調べ、(バイナリーモードで)ファイルに書き込み、リストボックスに予定日を追加し、画面を消去します。
<p>
 最後は削除ボタンが押された場合です。
<p>
<table border="1" bgcolor="#F0F0F0">
<caption align="BOTTOM">リスト26-5
<tr><td>
<pre>
  else if(wID==IDC_BUTTON2 &amp;&amp; wNotifyCode==BN_CLICKED)
   {   /*  削除ボタンが押された場合    */
       /*  リストボックスで選択されている項目を削除する    */
       char    selecteditem[11];
       char    itemstring[11];
       char*   fullfilename;
       int     index;
       LONG    comboindex1;    /*  年  */
       LONG    comboindex2;    /*  月  */
       LONG    comboindex3;    /*  日  */

      /*  削除する項目が選ばれていない場合    */
       index=SendDlgItemMessage(hwnd,IDC_LIST1,LB_GETCURSEL,0,0);
       if(index==LB_ERR)
       {
           MessageBox(hwnd,"削除する項目を選んでください","警告",MB_OK|MB_ICONEXCLAMATION);
           return  TRUE;
       }
       /*  削除:ここから  */
       SendDlgItemMessage(hwnd,IDC_LIST1,LB_GETTEXT,index,(LPARAM)itemstring);
       fullfilename=ForFileType(ForDateFileType(itemstring));
       /*  内容を表示し、削除の確認を求める    */
       if(DialogBoxParam(hInst,MAKEINTRESOURCE(IDD_DIALOG2),hwnd,(DLGPROC)DeleteWindowProc,(LPARAM)fullfilename)==IDOK)
       {
           /*  現在登録中の項目を削除しようとした場合  */
           comboindex1=SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETCURSEL ,0,0);
           comboindex2=SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETCURSEL ,0,0);
           comboindex3=SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETCURSEL ,0,0);
           if(comboindex1!=CB_ERR &amp;&amp; comboindex2!=CB_ERR &amp;&amp; comboindex3!=CB_ERR)
           {
               char    year[5];
               char    month[3];
               char    day[3];
               char    yyyymmdd[11];
               SendDlgItemMessage(hwnd,IDC_COMBO1,CB_GETLBTEXT,comboindex1,(LPARAM)year);
               SendDlgItemMessage(hwnd,IDC_COMBO2,CB_GETLBTEXT,comboindex2,(LPARAM)month);
               SendDlgItemMessage(hwnd,IDC_COMBO3,CB_GETLBTEXT,comboindex3,(LPARAM)day);
               sprintf(yyyymmdd,"%s/%s/%s",year,month,day);
               SendDlgItemMessage(hwnd,IDC_LIST1,LB_GETTEXT,index,(LPARAM)selecteditem);
               if(!strcmp(yyyymmdd,selecteditem))
                   InitInput(hwnd);    /*  入力コントロールも初期化する    */
           }
           unlink(fullfilename);                                       /*  ファイルを削除  */
           SendDlgItemMessage(hwnd,IDC_LIST1,LB_DELETESTRING,index,0); /*  リストボックスから削除  */
           InitInput(hwnd);    /*  予定日コンボボックスと予定エディットコントロールを初期化    */
       }
       /*  削除:ここまで  */
   }
</pre>
</table>
<p>
 削除すべき項目が選択されていない場合は、メッセージボックスで警告を発して、削除を中断します。削除条件が揃えば、削除確認ウインドウを表示し、削除を確認の上、削除します。現在、登録中の場合であっても、画面を初期化し、該当するファイルも削除します。
<hr>
<p>
 さて、感想はどうでしょうか?Windowsの様にGUI環境では、常にアプリケーションの<b><i><u>状態</u></i></b>を意識しなければいけない事です。これは、自動車などに似ています。同じ1速に入れてクラッチを放すという行為をしても、エンジンがアイドリング状態か、フル回転している時かで、結果は自ずと違っています。GUI環境でのプログラミングの難しさはここにあるのです。
<p>
 いかにして起こりうる状態を網羅するか。これは、VisualBASICなどのRADツールでも何ら変りません。
<p>
 結論は、<b>GUIプログラミングは如何なるツールを用いても難しい</b>ということです。
<hr>
<p>
 次回は、ウインドウクラスを登録しなくてもよかったダイアログボックスって何?と言う事をテーマにしましょう。
<p>
 では、お楽しみに。
<br>
<p align="RIGHT">
2002年7月3日<br>
修正2002年7月9日<br>
<hr>
<p align="RIGHT">
<a href="/web/20151022183410/http://web.kyoto-inet.or.jp:80/people/ysskondo/index.html">目次</a><br>
<hr>
<p align="RIGHT">
著作権者:近藤妥

</body>
</html>

  • 最終更新:2018-03-11 05:22:59

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

認証パスワード