とりあえず、現行の進行計画から,(以前の案から若干変更)
β版: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も同様のため)
titleがNULLの場合は、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つ用意しておく。
- 使用方法等
そのまま使用する際の使用方法を説明する。
- インスタンス化
GCanvas=new CwccCanvas() ;
によって、インスタンス化を行う。プログラム内で、必ず使うのであれば、最初に行ってしまっても良い。
- グラフィックキャンバスのオープン
GCanvas->OpenCanvas("TEST Graphics",200,200) ;
の様に(引数は例)して、グラフィックキャンバス(別Window)を開く。
- 描画
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) ;
としても良い…多分
- 画面に表示する。
仮想スクリーンに描画しただけでは、本来の画面には表示されないので、
GCanvas->Update() ;
を実施して、画面を表示する。
- 必要が有れば、グラフィックキャンバスを閉じる
GCanvas->CloseCanvas() ;
にて、Windowを閉じる。もし、プログラムの終わりまで表示を保持したければ、行わなくても良い。
- 必要が有れば、クラスの消去
delete GCanvas ;
アプリの終了時に、クラスが生成されていれば、消されるので、特別消す必要は無いが、消しても構わない。但し、この時点でグラフィックキャンバス自体も消されるので、注意!
拡張して使うには、ユーザがCwccCanvasを継承して、クラスを生成すれば良いのだが、実質、効果的に使えるのは、virtual void OnPaintGC()部分のみであると思うので、仮想スクリーンに関係なく、常に画面に表示させたいグラフィックが有れば、これを拡張して、その部分を描画させる事も可能である。
- グラフィックライブラリ
グラフィックライブラリは、基本的に、ユーザが用意するものであるので、CWCC側では、ユーザの判断に委ねるものとする。
(★失敗談)
困ったものである。最初の仕様で造り込んだら、Windowまでは開けたが、アプリ自体が止まってしまって、ぜ〜んぜん動かなかったのだ。試作までは、上手く動いていたので、かなりハマった。これでまた、一昼夜悩んでしまったのだ。結局,何をやっても復活しなかったので、試作と違う点をピックアップしたら、やっと何とか見えてきた。何故かというと、どうやら、スレッドの動作上からWindowを生成すると、ハマる様である。消去する方の操作は、スレッドからでも出来てしまうので、ちょっとスッキリしていないのだが、多分、親Windowが、子Windowが生成された事を認識出来ない為に、メッセージの流れが止まってしまったのだと思う。とりあえず、メインのWindow側に、要求を出して、同じクラスの関数を呼び出して貰う様にしたら上手く行ったので、めでたしめでたしなのである。もう一点、GCanvasをdeleteして、再度newすると、再度同じクラス名でRegisterClassしてしまうので、デストラクタにUnregisterClassを追加する。
上記中、改版点は、★マークで記述
|