最後の仕上げが、プログラムの本来の機能の実装です。(逆に言えば今まではその土台を作っていたにすぎません。)
実際のプログラムの動作を規定するのがProc.hファイル(場合によってはそれを補完する追加のUser.hファイル)です。今回のHelloWorldProjectの要求仕様である「”Hello World!!"を(視覚的に)面白く表示する。」は具体化されて、「ウィンドウ上で複数の文字列表示を行う。」→「(1)文字列表示は、ウィンドウタイトル、ウィンドウクライアントエリア、ダイアログ、メッセージボックス、ステータスバーとする。」となりました。それに対応するのがメニューの
「 POPUP "ファイル(&F)"
{
MENUITEM "終了(&X)", IDM_EXIT
}
POPUP "表示(&V)"
{
MENUITEM "画面表示(&D)", IDM_DISP
MENUITEM "ウィンドウタイトルの表示(&T)", IDM_TITLE
MENUITEM "ステータスバーの表示(&S)", IDM_STATUSBAR
}
POPUP "ヘルプ(&H)"
{
MENUITEM "バージョン情報(&V)", IDM_VERSION」
であり、それぞれのメニュー項目に対応する関数が
「 //メニュー項目、ダイアログコントロール関連
bool OnExit();
bool OnDisp();
bool OnTitle();
bool OnStatusbar();
bool OnVersion();」
でした。それを踏まえて以下説明します。
ダイアログは生成された段階でWM_INITDIALOGメッセージが出され割り込み処理が行われます。それに対応するのがOnInit関数です。
bool CMyWnd::OnInit(WPARAM wParam, LPARAM lParam) {
//コモンコントロールの初期化
InitCommonControls();
//ステータスバー登録-SetHandle(hWnd))
SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));
//ステータスバー区画設定
// int sec[2] = {200, 400}; オリジナル
int sec[2] = {120, -1}; //変更
SBar.SetSection(2, sec);
//ステータスバー文字列設定
SBar.SetText(0, "HelloWorld Ver1.0");
// SBar.SetText(1, ""); オリジナル(不要)
//追加-CANVASインスタンスの対象ウィンドウをIDD_MAINのハンドルで初期化
cvs.SetCanvas(m_hWnd);
return TRUE;
}
ここでは↑のコメントのとおり、自動生成コードについてステータスバーに関する「200,400ピクセルの2区画を120ピクセルと残余の2区画に変更」し、ブランクのままにする第2区画の記述を削除しています。また、仮想ウィンドウインスタンスの対象ウィンドウをメインウィンドウ(ダイアログ)に設定しています。(BCCSkeltonのウィンドウハンドルは、CMyWndのようなメインウィンドウの中でも、VERSIONDLGのようなダイアログクラスの中でも常に"m_hWnd"です。)
次にIDM_EXITに対応する関数の定義が
bool CMyWnd::OnExit() {
//追加-典型的な終了処理(SendMsgはSendMessageのBCCSkelton版)
SendMsg(WM_CLOSE, 0, 0);
return TRUE;
}
で、コメントにある通りの"SendMessage(m_hWnd, WM_CLOSE, 0, 0);"と等価のBCCSkelton関数を記述します。
次のIDM_DISPは長いので後回しにして、ウィンドウタイトルを設定するIDM_TITLEは
bool CMyWnd::OnTitle() {
//追加-ダイアログタイトルに文字列を表示、なお"(LPARAM)"はキャスト)
SendMsg(WM_SETTEXT, 0, (LPARAM)g_lpMsg);
return TRUE;
}
単に"SendMessage(m_hWnd, WM_SETTEXT, 0, (LPARAM)g_lpMsg);"と等価のBCCSkelton関数を記述します。コメントにある通り、キャストを外すとエラ―になりますのでご注意を。
ステータスバーの第2区画に表示するIDM_STATUSBARもWin32で書くと長くなりますが、単純に
bool CMyWnd::OnStatusbar() {
//追加-第2セクション(引数は1)に文字列を表示
SBar.SetText(1, g_lpMsg);
return TRUE;
}
これだけになります。SBarはIDD_MAINダイアログのステータスバー(コントロール)に紐づけた(注)CSBARクラスのインスタンスでSetTextメンバー関数を使っています。
注:"SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));"の部分です。
さて、飛ばしていたIDM_DISPの関数は次のようになります。ここではWin32でウィンドウに文字を書くコードとBCCSkeltonのコードを比較できるようにしました。(その関係で使い勝手の悪いPrintText関数を改造したPrintTextEx関数を追加して使っています。このメンバー関数は作った後、ダウンロードファイルを更新してアップしていますので、「そんな関数がない」とエラーが出る場合は再度BCCForm and BCCSkeltonをダウンロードしてBCCSkeltonフォールダーのCANVAS.hを確認してください。)
bool CMyWnd::OnDisp() {
//追加-retuirn TRUEまですべて
/////////////////
//Win32での表示//
/////////////////
//クライアントエリアサイズを取得(RECT m_clientはCMyWndのメンバー関数)
GetClientRect(m_hWnd, &m_client);
//デバイスコンテキストの取得
HDC hDC = GetDC(m_hWnd);
//フォント変更
HFONT hFont = CreateFont(48, 0,
0, 0, FW_BOLD,
FALSE, FALSE, FALSE,
SHIFTJIS_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH,
"MS Pゴシック");
HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
// テキストカラー変更
SetTextColor(hDC, RGB(0xff, 0x00, 0x00));
//テキスト表示
DrawText(hDC, g_lpMsg, -1, &m_client, DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_NOCLIP);
//表示確認の為一旦停止
MessageBox(m_hWnd, g_lpMsg, g_lpMsg, MB_OK | MB_ICONEXCLAMATION);
SelectObject(hDC, hOldFont);
DeleteObject(hFont);
ReleaseDC(m_hWnd, hDC);
//////////////////////////////////
//仮想ウィンドウCANVASによる表示//
//////////////////////////////////
//画面のクリア
cvs.Clear();
//新しい関数PrintTextExによるテキスト表示
cvs.PrintTextEx(g_lpMsg, &m_client, RGB(0x00, 0x00, 0xff),
"Times New Roman", 48,
DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_NOCLIP);
return TRUE;
}
Win32のコードに比べ、BCCSkeltonでの表記が随分簡素になったことが分かると思います。なおヘルプに記載がないので説明するとPrintTextExの引数定義は次のようになっています。
bool CANVAS::PrintTextEx(LPCTSTR str, LPRECT lprec, //出力文字列とDrawText関数で使うRECT変数へのポインター
COLORREF rgb, //SetTextColorで使うCOLORREF変数
LPCTSTR FontName, int FontSize, //CreateFontで使うフォント名、フォントサイズ
UINT fmt = DT_LEFT | DT_NOCLIP, //DrawTextのフォーマットオプション
//ここからはデフォルト値を与えているので省略可
int nEscapement = 0, int nOrientation = 0, int nWeight = FW_NORMAL, //文字送り方向角度、X軸との角度、文字太さ
DWORD fdwItalic = FALSE, DWORD fdwUnderline = FALSE, //イタリックか否か、下線付きか否か
DWORD fdwStrikeOut = FALSE, DWORD fdwCharSet = DEFAULT_CHARSET) //打消し線付か否か、キャラクターセット
最後にバージョン表示ダイアログに関してです。IDD_VERSIONDLGダイアログについてはメインウィンドウでの呼び出しは
bool CMyWnd::OnVersion() {
//追加(m_hWndはIDD_MAINのハンドル、versiondlgProcはコールバック関数アドレス)
versiondlg.DoModal(m_hWnd, "IDD_VERSION", versiondlgProc);
return TRUE;
}
とVERSIONDLGダイアログクラスのインスタンスversiondlgのメンバー関数DoModalで、モーダルダイアログとして呼び出します。
呼び出されたダイアログでの対応は
bool VERSIONDLG::OnIdok() {
//追加(DoModalと対-クラスの関数定義なのでインスタンス名versiondlgは不要)
EndModal(TRUE);
return TRUE;
}
と「追加コメント」の通り、あっさり記述されます。(なおEndModal関数の引数はversiondlg.DoModal関数の戻り値となります。)
以上で6回に亘って定番のHelloWorldプロジェクトの説明を行いました。いかがだったでしょうか?BCCForm and BCCSkeltonはフールプルーフなWYSWYG開発環境ではなく、C++とWin32のプログラミングに一定慣れた人が開発効率を上げるためのツールであることが実感されたと思います。
しかし、単に"Hello World!!"と表示されるだけのプログラムを組んでも(自分の学習意欲を満足させる以外の実益はなく)いずれあきてしまいます。やはり「実際に使えるソフト」を作ることがプログラミングの醍醐味でしょう。しかし個人のプライベートライフで使えるプログラムは、実は、余りなく(またその多くは既に先人が素晴らしいものを作っており)、「何を作ろうか?」が大きな問題となってきます。
私の場合、もともとBCCFormやBCCSkeltonはC++によるWin32プログラミング学習のために自作したわけですが、BCCMakerやBCFEditor、TBEditorやEZImage(と対になるIconViewer)はやはり開発環境として作成し、「個人のプライベートライフで使えるプログラム」ではありませんでした。機会があればそのようなプログラム開発を行う際に、同時進行でブログで開発状況を紹介してゆきたいと考えています。