今回はDump改良の最終回で、DumpProc.hを「(解説:)」で解説します。が、その前に一点Dump.hファイルに追加を行います。
【Dump.h】
//////////////////////////////////////////
// Dump.h
// Copyright (c) 03/04/2021 by BCCSkelton
//////////////////////////////////////////
//BCCSkeltonのヘッダー-これに必要なヘッダーが入っている
#include "BCCSkelton.h"
<以下略>
・
・
・
/////////////////////////////////////////////////////////////////////
//CMyWndクラスをCDLGクラスから派生させ、メッセージ用の関数を宣言する
/////////////////////////////////////////////////////////////////////
class CMyWnd : public CDLG
{
public: //以下はコールバック関数マクロと関連している
//2重起動防止用のMutex用ID名称
CMyWnd(char* UName) : CDLG(UName) {}
//メンバー変数
char* m_FileName; //ファイル名
CFILE m_File; //ファイルクラス変数
CSTR m_Buff; //エディットコントロール用文字列バッファ
int m_LastPage; //最大ページ数
int m_Page; //表示ページ数
HBITMAP m_hBmp[4]; //ページ移動ボタン用ビットマップ
//メニュー項目、ダイアログコントロール関連
<以下略>
・
・
・
ページ移動用のプッシュボタンに"←"や"→"などを入れてもよいのですが、ビットマップを張ることになっていましたので、4つのビットマップハンドルをDumpのメンバー関数として持たせましょう。
ではDumpProcに進みます。
【DumpProc.h】
/////////////////////////////////////////
// DumpProc.h
// Copyright (c) 03/04/2021 by BCCSkelton
//////////////////////////////////////////
/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CMyWnd::OnInit(WPARAM wParam, LPARAM lParam) {
//コモンコントロールの初期化
InitCommonControls();
//(解説:今回使うツールヒントコントロールには不可欠ですが、SkeltonWizardが既に記述しています。)
//ステータスバー登録-SetHandle(hWnd))
SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));
//ステータスバー区画設定
int sec[1] = {-1};
SBar.SetSection(1, sec);
//ステータスバー文字列設定
SBar.SetText(0, "ファイル名-「ファイル」ボタンで選択");
//(解説:今回ステータスバーの枠は一つで、ファイルパス、名を表示します。初期値はガイダンス表示です。)
//ビットマップをボタンに張り付ける
m_hBmp[0] = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_TOP), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
SendItemMsg(IDC_TOP, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_hBmp[0]);
m_hBmp[1] = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_BEFORE), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
SendItemMsg(IDC_BEFORE, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_hBmp[1]);
m_hBmp[2] = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_NEXT), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
SendItemMsg(IDC_NEXT, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_hBmp[2]);
m_hBmp[3] = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_BOTTOM), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
SendItemMsg(IDC_BOTTOM, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_hBmp[3]);
//(解説:プッシュボタンにビットマップを貼るには、プッシュボタンのウィンドウスタイルにBS_BITMAPを加え、WM_CREATEまたは今回のようにWM_INITDIALOGでリソースのビットマップをロードし、BM_SETIMAGEとIMAGE_BITMAPで貼り付けます。アイコンを使う場合にはLoadImageとBM_SETIMAGEでIMAGE_ICONを使います。)
//IDC_TOP、IDC_BEFOREとIDC_NEXT、IDC_BOTTOMボタンを無効にする
ChangeTopStatus(FALSE);
ChangeBottomStatus(FALSE);
//(解説:Change~Status関数はIDC_TOPとIDC_BEFORE、IDC_NEXTとIDC_BOTTOMを有効、無効にするユーザー定義関数です。後で解説します。)
//IDC_PAGEの文字数を6(5桁)に設定
SendItemMsg(IDC_PAGE, EM_LIMITTEXT, 6, 0);
//(解説:ページ移動ボタンの中央にページ表示R/Oエディットボックスを配置しますが、この桁数を5文字(およびNULL終端で6)に設定しています。)
//ドラッグアンドドロップの受付開始
DragAcceptFiles(m_hWnd, TRUE);
//(解説:前にもやりましたが、ファイルのドラッグアンドドロップを開始する際の定番関数です。)
//ボタンとエディットボックスにツールチップを付ける
if(!SetToolTip(m_hWnd, IDC_FILE, "Dumpするファイルを選択します"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_FILE)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_EDIT, "Dumpデータ表示エリア"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_EDIT)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_TOP, "ファイルの先頭に行きます"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_TOP)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_BEFORE, "1ページ戻ります"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_BEFORE)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_PAGE, "中央のページです"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_PAGE)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_NEXT, "1ページ進みます"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_NEXT)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDC_BOTTOM, "ファイルの後尾に行きます"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDC_BOTTOM)", "エラー", MB_OK | MB_ICONERROR);
if(!SetToolTip(m_hWnd, IDOK, "終了します"))
MessageBox(m_hWnd, "ツールチップを付けられませんでした(IDOK)", "エラー", MB_OK | MB_ICONERROR);
//(解説:Dumpのダイアログに使った8つのコントロール全て(デモンストレーションなので-汗;)について、Dump.cppの#includeで取り入れているToolTip.hに書いたSetToolTip関数を使ってツールチップを付与しています。)
//ファイルドロップによる起動
if(g_ByFile)
OnFile();
//(解説:プログラム起動の際に引数としてファイルパスと名が与えられていれば、g_ByFileフラグがTRUEなので「ファイルを開く」のOnFile関数に飛びます。)
return TRUE;
}
bool CMyWnd::OnSize(WPARAM wParam, LPARAM lParam) {
SBar.AutoSize();
return TRUE;
}
//(解説:Dumpダイアログはサイズ変更ができないので不要のように思えますが、最初にダイアログが現れるときに呼ばれるので、その際にステータスバーを調整してクライアントエリア幅いっぱいに伸ばします。)
bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {
if(MessageBox(m_hWnd, "終了しますか", "終了確認",
MB_YESNO | MB_ICONINFORMATION) == IDYES) {
//ボタンのビットマップを解放する
DeleteObject(m_hBmp[0]);
DeleteObject(m_hBmp[1]);
DeleteObject(m_hBmp[2]);
DeleteObject(m_hBmp[3]);
//(解説:プッシュボタンに張り付けたビットマップはメモリー割り当てを行われているので、使い終わったら解放します。)
//処理をするとDestroyWindow、PostQuitMessageが呼ばれる
return TRUE;
}
else
//そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
return FALSE;
}
//ダイアログベースの場合はこれが必要
bool CMyWnd::OnDestroy(WPARAM wPram, LPARAM lParam) {
PostQuitMessage(0);
return TRUE;
}
bool CMyWnd::OnDropFiles(WPARAM wParam, LPARAM lParam) {
//典型的なドラッグアンドドロップ処理
static char lpszFn[MAX_PATH];
HDROP hDrop = (HDROP)wParam; //HDROPを取得
DragQueryFile(hDrop, 0, lpszFn, MAX_PATH); //ファイル名を取得
m_FileName = lpszFn;
g_ByFile = TRUE;
OnFile();
DragFinish(hDrop); //終了処理
return TRUE;
}
//(解説:これも前に書きましたが定番処理です。m_FileNameにファイルパス、名をセットし、g_ByFileフラグを使ってOnFile()を読んでいます。)
/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnFile() {
//ファイル選択(ドラッグアンドドロップでは省略)
if(g_ByFile)
g_ByFile = FALSE;
//(解説:g_ByFileフラグが真ならm_FileNameに既にデータがある、ということです。使い終わったら初期値のFALSEに戻します。)
else
m_FileName = cmndlg.GetFileName(m_hWnd, "すべてのファイル(*.*)\0*.*\0\0", TRUE);
//(解説:g_ByFleがFALSEなら、ファイルを開くダイアログを使ってm_FileNameにファイルパス、名をセットします。)
if(!m_FileName) {
MessageBox(m_hWnd, "ファイル選択がキャンセルされました", "キャンセル", MB_OK | MB_ICONSTOP);
return FALSE;
}
//ステータスバーにファイル名表示
SBar.SetText(0, m_FileName);
SBar.SendMsg(SB_SETTIPTEXT, 0, (LPARAM)m_FileName); //ToolTipをつける
//(解説:ステータスバーに取得したファイルパス、名を表示します。また長くてすべて表示できない場合にはツールチップが出るようにします。)
//ファイル読み込みと初期化処理
m_File.LoadFile(m_FileName); //ファイルを読み込む
m_File.GoTop(); //ファイルの先頭へ
m_Page = 0; //最初のページ
m_LastPage = m_File.m_FileSize / PAGE; //Fileバイト数をPAGE単位とする
//最初のページを表示する(解説:内部的にm_Pageは0~ページ数 - 1、IDC_PAGEの表示はこれに+1されます。)
SetPage(m_Page);
//最初のデータの表示(解説:現在のページ-m_Page-のデータが表示されます。)
ShowData(m_Page);
//最初のボタン状態の設定
ChangeTopStatus(FALSE);
ChangeBottomStatus(TRUE);
//(解説:コメントにある通りのシーケンスで処理を行います。なお、次へと最後へのボタンは有効化されますが、1ページしかない場合、押した段階で無効化されます。)
return TRUE;
}
bool CMyWnd::OnTop() {
m_Page = 0; //最初のページ
SetPage(m_Page); //ページを表示する
ShowData(m_Page); //データを表示する
//IDC_TOPとIDC_BEFOREボタンを無効にする
ChangeTopStatus(FALSE);
//IDC_NEXTとIDC_BOTTOMボタン
if(m_LastPage) //1ページだけでなければ有効にする
//IDC_NEXTとIDC_BOTTOMボタン状態を変更する
ChangeBottomStatus(TRUE);
else //1ページだけならボタンを無効にする
ChangeBottomStatus(FALSE);
//(解説:0ページ(表示は「1」)に飛ぶ処理です。現在ページの保存、その表示、当該ページのデータ表示、ボタンの有効・無効処理を行います。)
return TRUE;
}
bool CMyWnd::OnBefore() {
if(!m_Page) { //既に最初のページであれば
//IDC_TOPとIDC_BEFOREボタンを無効にする
ChangeTopStatus(FALSE);
return FALSE; //何もしないで戻る
}
else {
m_Page--; //そうでなければページを一つ戻し
SetPage(m_Page); //ページを表示する
ShowData(m_Page); //データを表示する
//IDC_NEXTとIDC_BOTTOMボタンを有効にする
ChangeBottomStatus(TRUE);
if(!m_Page) //最初のページであれば
//IDC_TOPとIDC_BEFOREボタンを無効にする
ChangeTopStatus(FALSE);
return TRUE;
}
}
//(解説:1ページ戻る処理です。現在ページの保存、その表示、当該ページのデータ表示、ボタンの有効・無効処理を行います。)
bool CMyWnd::OnNext() {
if(m_Page == m_LastPage) { //既に最後のページであれば
//IDC_NEXTとIDC_BOTTOMボタンを無効にする
ChangeBottomStatus(FALSE);
return FALSE; //何もしないで戻る
}
else {
m_Page++; //そうでなければページを一つ進め
SetPage(m_Page); //ページを表示する
ShowData(m_Page); //データを表示する
//IDC_TOPとIDC_BEFOREボタンを有効にする
ChangeTopStatus(TRUE);
if(m_Page == m_LastPage) //最後のページになれば
//IDC_NEXTとIDC_BOTTOMボタンを無効にする
ChangeBottomStatus(FALSE);
return TRUE;
}
}
//(解説:1ページ進む処理です。現在ページの保存、その表示、当該ページのデータ表示、ボタンの有効・無効処理を行います。)
bool CMyWnd::OnBottom() {
m_Page = m_LastPage; //最後のページ
SetPage(m_Page); //ページを表示する
ShowData(m_Page); //データを表示する
//IDC_NEXTとIDC_BOTTOMボタンを無効にする
ChangeBottomStatus(FALSE);
//IDC_TOPとIDC_BEFOREボタ
if(m_Page) //1ページだけでなければ有効にする
//IDC_TOPとIDC_BEFOREボタン状態を変更する
ChangeTopStatus(TRUE);
else //1ページだけならボタンを無効にする
ChangeTopStatus(FALSE);
return TRUE;
}
//(解説:最終ページに飛ぶ処理です。現在ページの保存、その表示、当該ページのデータ表示、ボタンの有効・無効処理を行います。)
bool CMyWnd::OnIdok() {
SendMsg(WM_CLOSE, 0, 0);
return TRUE;
}
//(解説:IDOK(「終了」)ボタンによる終了処理です。)
//ユーザー定義関数
bool CMyWnd::SetPage(int page) {
char num[6]; //5桁まで対応可能
wsprintf(num, "%d", page + 1); //ページ数の文字化(1 → m_LastPage + 1)
SendItemMsg(IDC_PAGE, WM_SETTEXT, 0, (LPARAM)num); //IDC_PAGEへ表示
//(解説:ページ数のメンバー変数m_Page + 1の数をIDC_PAGEエディットコントロールに表示します。)
return TRUE;
}
bool CMyWnd::ShowData(int page) {
//(解説:ページ(7168バイト)分のダンプデータを「アドレス+スペース+(2桁16進数字+スペース)x8バイト分+区切り:+(2桁16進数字+スペース)x8バイト分+区切り:+16バイトのキャラクター表示(制御文字は'.'表示)+'CRLF'」の行表示で出力します。
char str[MAX_PATH]; //一行書き込み文字列バッファ
m_Buff = ""; //初期化
for(int i = page * PAGE; i < (page + 1) * PAGE; i += 16) {
if(i >= m_File.m_FileSize) //最終ページ対応
break;
int n = 0, j = 0;
n = wsprintf(str, "%04X ", i); //アドレス
j += n;
for(int k = 0; k < 8; k++) { //8バイト16進書き込み
n = wsprintf(str + j, "%02X ", m_File.m_Ptr[i + k] & 0xFF);
j += n;
}
n = wsprintf(str + j, ":"); //セパレーター
j += n;
for(int k = 8; k < 16; k++) { //8バイト16進書き込み
n = wsprintf(str + j, "%02X ", m_File.m_Ptr[i + k] & 0xFF);
j += n;
}
n = wsprintf(str + j, ":"); //セパレーター
j += n;
for(int k = 0; k < 16; k++) { //キャラクター表示
n = wsprintf(str + j, "%c", isalnum(m_File.m_Ptr[i + k]) ? m_File.m_Ptr[i + k] : '.');
j += n;
}
n = wsprintf(str + j, "\r\n");
m_Buff = m_Buff + str;
//(解説:書き終わった行データをCSTRクラス変数のm_Buffに付け足してゆきます。)
}
SendItemMsg(IDC_EDIT, WM_SETTEXT, 0, (LPARAM)m_Buff.ToChar());
//(解説:最後に出来上がったページデータをIDC_EDITコントロールに張り付けます。)
return TRUE;
}
bool CMyWnd::ChangeTopStatus(bool stat) {
//IDC_TOPとIDC_BEFOREボタン状態を変更する
EnableWindow(GetDlgItem(m_hWnd, IDC_TOP), stat);
EnableWindow(GetDlgItem(m_hWnd, IDC_BEFORE), stat);
return TRUE;
}
//(解説:statに応じてプッシュボタンを有効、無効に変更します。)
bool CMyWnd::ChangeBottomStatus(bool stat) {
//IDC_NEXTとIDC_BOTTOMボタン状態を変更する
EnableWindow(GetDlgItem(m_hWnd, IDC_NEXT), stat);
EnableWindow(GetDlgItem(m_hWnd, IDC_BOTTOM), stat);
return TRUE;
}
//(解説:同上)
bool CMyWnd::GoMiddle(WPARAM wParam) {
//(解説:これは真ん中のIDC_PAGEコントロールにフォーカスが移った場合(EN_SETFOCUS)の処理です。エディットコントロールはWM_NOTIFYではなく、WM_COMMANDで通知が来る点に注意が必要です。通知メッセージはHIWORD(wParam)に入っています。 処理はページ移動ボタンと同様です。コメントをご覧ください。)
//ファイルが選択されていない場合
if(!m_FileName)
return FALSE;
//IDC_PAGEエディットコントロールにフォーカスがない場合
if(HIWORD(wParam) != EN_SETFOCUS)
return FALSE;
//IDC_PAGEエディットコントロールにフォーカスが移った場合
else
m_Page = m_LastPage / 2; //ページ中間へ進む
if(m_Page == m_LastPage) //m_LastPageが0なら
ChangeBottomStatus(FALSE); //IDC_NEXTとIDC_BOTTOMボタン無効化
else {
ChangeBottomStatus(TRUE); //IDC_NEXTとIDC_BOTTOMボタン有効化
if(m_Page) //最初のページでなければ
ChangeTopStatus(TRUE); //IDC_TOPとIDC_BEFOREボタン有効化
else //最初のページなら
ChangeTopStatus(FALSE); //IDC_TOPとIDC_BEFOREボタン無効化
}
SetPage(m_Page); //ページを表示する
ShowData(m_Page); //データを表示する
return TRUE;
}
出来上がったらBCC DeveloperかBCCMakerでビルドしてみましょう。以下は私の環境での動作像です。以下のようにファイル名が長すぎて全部表示されない場合、ステータスバーにツールチップが出るので確認してみてください。