[[BORLAND C++ CWCC]]
RETURN
CWCC#07 CWCC Beta Edition
とりあえず、現行の進行計画から,(以前の案から若干変更)
β版:Version 0.5.x グラフィックウインドウ+α
β1版:Version 0.6.x とりあえず、計画に有った全機能の取り込み
β2版:β版までの問題点を改善
正式版: Version 1.0.0 β版のバグFIX版

さて、少しはクラスライブラリらしくなってきたのだろうか?取って付けた様な仕様も多いが、とりあえずはファイルやシリアルも通せる様になったので、実験用のツールのプラットホームとしては、強力な設計環境として動作している。あと、大きな変更点としては、グラフィックウインドウだろうか?これを実装して、とりあえずはβ版としたい。
更に、考えなければならない点が幾つか存在しているので、この機能の追加後に考えてみたい。一つは、エディットコントロールのドロップアウト,他にも、CwccGetchの不自然さを解決する方策の検討等、本編の方では既に書いた内容も有るが、CWCCへの入れ込みが、おざなりになっている項目の検討もしていきたい。

β版ライブラリのダウンロード
ライブラリの版数は、0.5.27
※サンプル入(ソースは、暗号化されているので、メールにて、デコーダを入手して下さい)

  • ちと、バグ修正
    メインウインドウと、エディットコントロールのクラスのデストラクタで、Windowの消去を行っているのだが、行っている場所が、既にメッセージループを抜けた後になっているので、この時点は即ち、親Windowに対して、WM_CLOSEが投げられ、子Windowである筈の、エディットにも投げられている筈なのだ。という事は、既に無いWindowを消そうとしている事になるのだ…。幸い、大きな障害には至っていない。無いWindowを消そうとしたらどうなるかは不明なのだが、多分、Windows内部で、行き先不明のメッセージとして、消滅しているだけだと思うが、ちょっとやばいやばいなのである。これは、速攻消去した。

  • グラフィック画面
    元々、テキストで書ける情報には限界が有るから、グラフィック画面は重要なのだ。でも、自分,Windowsでグラフィックなんて考えた事が無かったので、今回は一苦労したのだ。DOSの頃はどうしてたっけ?PC98シリーズの場合は、グラフLioやらGDC?なんてライブラリが有ったから、簡単に描画出来たし、VRAMがメモリの延長線上に有ったから、簡単にコントロール出来たのだった。実は、テキスト表示だけのツールならば、グラフィック画面を非表示にすると、表/裏合わせて、広大なメモリエリアに変身してくれるので、時々メモりとしても使ってたっけ…。って、昔話をしていても仕方がない。DOSの頃のグラフィック画面は、テキスト画面とは別に存在していたな〜。という訳で、専用の画面を1枚(本編16参照)開いて、そこに描画する(本編17参照)って仕様で行ってみたい。しかも、クラスで提供するのだ。

  • class CwccCanvasの設計
    簡単に言うけど、これは結構な作業量なのだ。根本的に、Windowsのグラフィックについての基礎知識は、な〜〜んにも無い身なので、デザインは、非常に苦労しそうなのである。しかしながら、こういうところが、腕の見せ所でもあるのは事実だし、出来る限り勉強してみようと思っている。で、基本コンセプトは以下の通り,

    • グラフィックライブラリでは無くて、キャンバスを提供するのだ。
      これが基本思想なのである。LineやらRectangle等を描ける様にしても良いが、これをやり始めると、ペンがどうの…ビットマップがどうのと…キリがないし、1年後も同じ様な事を設計していそうなのである。

    • ユーザに提供するのは、メモリDCのみ
      即ち、専用のWindowを1枚開いて、仮想画面(ビットマップのメモリDC)を提供するので、それに対してユーザが自由に描画出来る様にするという事である。グラフィックライブラリ化をすると、クラスが異様にでかくなると思うし、一生使わない様な、必要の無いコマンドも色々有ると思うので、CWCCとして提供するのは、必要最低限の情報に留めておきたいのである。

    • グラフィックウインドウ
      これを書くと、『2枚,3枚造れ!』と言いそうだが、とりあえずは、絶対的な1枚…尚、リサイズは禁止とする。

    さて、具体的な設計である。何か仕様書風になっちまったが、この辺カチッとやらないと、きっと後で泣きを見るので…。と言ったら本当に泣きを見た…(★参照) 尚、このクラスは、CWCC側で用意した、
    CwccCanvas *GCanvas ;
    を利用しなければならないものとする(とりあえず、1枚だけ許容なので)

    • クラスの外部制御変数
      不正アクセスによる問題を防ぐ為、外部制御変数
      BOOL CwccCanvasControlを用意し、WinMainの先頭でFALSEをセットする。この変数は、ユーザプログラムでのタイマインスタンス消し忘れにも、柔軟に対応出来る仕組みである。(注意:Windowは、ユーザの指定が無い限り消さない予定)

    • メンバ
      • BOOL CreateGC ;
        内部管理用:キャンバスが作成されている場合はTRUE,作成されていなければFALSE
      • HINSTANCE hInstance ;
        アプリのインスタンスを保存する。
      • HWND pMainWin,GChandle ;
        親ウインドウのハンドル及びGCanvasのハンドルを保存する。
      • HBITMAP VBitMap ;
        仮想スクリーン用ビットマップハンドル
      • HDC Gdc,Mdc ;
        グラフィックDCと、仮想スクリーン用メモリDCを保存する。
      • int WidthX,HightY ;
        Windowを生成した時点で、クライアントの寸法をGetClientRectで取得し、幅と高さを記録しておく
      • char GCTitle[256] ;
        グラフィックキャンバスの表示文字保存用
      • int TmpX,TmpY ;
        キャンバスの生成サイズ記憶用


    • メンバ関数
      • コンストラクタ CwccCanvas() ;
        基本的に、ユーザプログラム内で、newされる事によって呼ばれる
        CwccCanvasControl=TRUEの場合は、FALSEとして、何もせずに終わる。
        →2重定義を禁止(将来的には複数枚の起動を可能としたいが、とりあえず1本)
        CwccCanvasControl=TRUEとする。
        この時点で、CWCCのメインウインドウはアクティブである筈なので、親Windowのハンドルと、インスタンスは、取得出来る筈,メニューは無し、クラス名は、"GrpCanvas",として、WindowProcは、GrpWndProc()←検討中,アイコン名は決定しているので、ここでRegisterClassまで行ってしまう。
        GChandle=NULLとする。
        CreateGC=FALSEとする。

      • デストラクタ virtual ~CwccCanvas() ;
        基本的にユーザプログラム内で、deleteされる事によって呼ばれるが、キャンバスウインドウの消去は行わない。キャンバスウインドウが有れば、消去を行う。(※)
        CreateGCがアクティブな場合は、PostMessage([WM_CLOSE])で、クローズする。 (→この機能は、親Windowが消されたら、必ず消える筈なので無しとする)
        CreateGCが、TRUEの場合は、VBitMapと、Mdcを削除する。
        CreateGC=FALSEとする。
        GChandle=NULLとする。
        CwccCanvasControl=FALSEとする。これをすると、以降,OnPaintGCが呼ばれなくなるが…クラス自体が無くなってしまうので、仕方がない…。やっぱり、デストラクタでWindow自体も消す様にしようか??難しい選択なのだ。(検討中)
        UnregisterClass("GrpCanvas",hInstance) ;を実施して、クラス登録を抹消する。
        ※多分,プログラムが終わった後も、完成した図形を見たいという要望が有ると思うので、ユーザが直接呼ぶ必要は無いものとする。何れにしても、アプリ終了前に、CwccCanvasControl==TRUEならば、CwccCanvasControl=FALSEとしてから、deleteを実施する。


        仕様が二転三転したが、この仕様でほぼFIXとする。但し、後始末のタイミングについては考慮が必要→多分、親WindowのWM_CLOSEイベントにて、子Windowに対してWM_CLOSEが投げられ、続いて、自分に対してWM_DESTROYが投げられていると思われるので、このメッセージをフックして、先回りして子Windowを消す様に設計する。★終了直前に、CwccCanvasControl==TRUEの場合、CwccCanvasControl==FALSEとしてからdeleteを実施する仕様にてFIX


      • キャンバスウィンドウの生成
         BOOL OpenCanvas(char *title,int wid,int hit) ;
        CwccCanvasControl==TRUEの場合のみ有効
        CreateGC==TRUEならば、FALSEを返す。
        wid,hitは、幅と高さ,titleは、キャプション文字である。
        開始座標は、ユーザには指定不可とする。(親Windowも同様のため)
        titleNULLの場合は、CWCC Canvasを表示する。
        ★メインウインドウに、CWCC_MAKE_GCVメッセージを送信し、CreateGCV()をコールさせる。CwccThreadクラスのコントローラで、登録が終わるまで、一時停止とする事。メッセージと座標時については、クラス内に保存しておく。

      • キャンバスウインドウの生成実行★
         HWND CreateGCV(void) ;
        メッセージCWCC_MAKE_GCVが発生した場合に呼ばれる。(どうしてこんな面倒くさい事をするのかについては、脚注参照)
        CreateWindowを実施して、結果をGChandleに取得する。
        Windowの属性は、WS_OVERLAPPED|WS_VISIBLEとする。
        同時に、WidthX,HightYを取得し、仮想スクリーンを取得して、クリアしておく。
        CreateGC=TRUE

      • キャンパスウインドウの終了 BOOL CloseCanvas(void) ;
        CwccCanvasControl==TRUEの場合のみ有効:無効ならばFALSEを返す。
        CreateGC==FALSEならば、FALSEを返す。
        PostMessage([WM_CLOSE])で、キャンパスを消す
        仮想スクリーン(VBitMapと、Mdc)を削除する。
        CreateGC=FALSE

      • 描画表示コマンド BOOL Update(void) ;
        CwccCanvasControl==TRUEの場合のみ有効
        CreateGC==FALSEならば、FALSEを返す。
        仮想スクリーンに描いていた画像をスクリーンに表示する。
        →これをしないと、描いた絵は、グラフィックキャンバスにWM_PAINTのイベントが発生しない限り表示されない。
        ※一時的にグラフィックキャンバスのDCを取得し、BitBltにてコピーを実施

      • ペイントハンドラ virtual void OnPaintGC(HDC hdc) ;
        グラフィックキャンバスのプロシージャに、WM_PAINTのイベントが上がった場合に呼ばれる。ユーザが拡張する事も可能であるが、そのまま使わないと、仮想スクリーンの画像は反映されないので、拡張した場合には、派生クラスのメンバ関数の最初に、CwccCanvas::OnPaintGC(hdc) ;の様に、呼び出しておくのが正当な使い方になるだろう。当然,WM_PAINTイベントの中で、呼び出す前に、CwccCanvasControlのチェックは行われるものとする。
        CreateGCは、関数内でチェックする事!

      • 仮想スクリーンクリアコマンド BOOL ClearScreen(void) ;
        CwccCanvasControl==TRUEの場合のみ有効
        CreateGC==FALSEならば、NULLを返す。
        仮想画面のWHITENESSを行う

      • メモリDC取得コマンド HDC GetGCDC(void) ;
        CwccCanvasControl==TRUEの場合のみ有効
        CreateGC==FALSEならば、NULLを返す。
        仮想スクリーンのDCを返す。ユーザは、このDCを使って仮想スクリーンに描画を行い、Update()によって、画面に反映させれば良い。

      • 試験コマンド void DrawLine(int sx,int sy,int ex,int ey) ;
        ライブラリには、直接関係無いが、描画試験用に、直線を描くメンバ関数を1つ用意しておく。

  • 使用方法等
    そのまま使用する際の使用方法を説明する。
    1. インスタンス化
      GCanvas=new CwccCanvas() ;
      によって、インスタンス化を行う。プログラム内で、必ず使うのであれば、最初に行ってしまっても良い。

    2. グラフィックキャンバスのオープン
      GCanvas->OpenCanvas("TEST Graphics",200,200) ;
      の様に(引数は例)して、グラフィックキャンバス(別Window)を開く。

    3. 描画
      GetGCDC()
      により、仮想スクリーンのDCを取得して、Win32APIのコマンドを使用して描画を行う。例えば,
      MoveToEx(GCanvas->GetGCDC(),10,10,NULL) ;
      LineTo(GCanvas->GetGCDC(),80,60) ;
      の様にするか、若しくは最初に、DCを取得しておき、
      HDC VDC=GCanvas->GetGCDC() ;
      MoveToEx(VDC,10,10,NULL) ;
      LineTo(VDC,80,60) ;
      としても良い…多分

    4. 画面に表示する。
      仮想スクリーンに描画しただけでは、本来の画面には表示されないので、
      GCanvas->Update() ;
      を実施して、画面を表示する。

    5. 必要が有れば、グラフィックキャンバスを閉じる
      GCanvas->CloseCanvas() ;
      にて、Windowを閉じる。もし、プログラムの終わりまで表示を保持したければ、行わなくても良い。

    6. 必要が有れば、クラスの消去
      delete GCanvas ;
      アプリの終了時に、クラスが生成されていれば、消されるので、特別消す必要は無いが、消しても構わない。但し、この時点でグラフィックキャンバス自体も消されるので、注意!


    拡張して使うには、ユーザがCwccCanvasを継承して、クラスを生成すれば良いのだが、実質、効果的に使えるのは、virtual void OnPaintGC()部分のみであると思うので、仮想スクリーンに関係なく、常に画面に表示させたいグラフィックが有れば、これを拡張して、その部分を描画させる事も可能である。

  • グラフィックライブラリ
    グラフィックライブラリは、基本的に、ユーザが用意するものであるので、CWCC側では、ユーザの判断に委ねるものとする。

(★失敗談)
困ったものである。最初の仕様で造り込んだら、Windowまでは開けたが、アプリ自体が止まってしまって、ぜ〜んぜん動かなかったのだ。試作までは、上手く動いていたので、かなりハマった。これでまた、一昼夜悩んでしまったのだ。結局,何をやっても復活しなかったので、試作と違う点をピックアップしたら、やっと何とか見えてきた。何故かというと、どうやら、スレッドの動作上からWindowを生成すると、ハマる様である。消去する方の操作は、スレッドからでも出来てしまうので、ちょっとスッキリしていないのだが、多分、親Windowが、子Windowが生成された事を認識出来ない為に、メッセージの流れが止まってしまったのだと思う。とりあえず、メインのWindow側に、要求を出して、同じクラスの関数を呼び出して貰う様にしたら上手く行ったので、めでたしめでたしなのである。もう一点、GCanvasをdeleteして、再度newすると、再度同じクラス名でRegisterClassしてしまうので、デストラクタにUnregisterClassを追加する。
上記中、改版点は、★マークで記述
2002/05/13
HomeSweetHome2
Ozzy's Software