最後のファイルです。ポイントを解説します。(なお、以下の処理で頻繁にif(g_Album.GetCount())という条件節が出てきます。これはオブジェクト生成前にGDI+メソッドを行わせようとするとプログラムが即落ちするので、その防止策です。)

【AlbumProc.h】
//////////////////////////////////////////
// AlbumProc.h
// Copyright (c) 02/28/2022 by BCCSkelton
//////////////////////////////////////////

/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CMyWnd::OnCreate(WPARAM wParam, LPARAM lParam) {

    //コモンコントロールの初期化
    InitCommonControls();

    //ツールバー登録-Init(hWnd, hIinstance, ID, (以下省略可)Style)
    TBar.Init(m_hWnd, m_hInstance, TOOLBAR);
    //ツールバーボタン用カスタムビットマップ追加
    TBar.AddBmp(m_hInstance, MAKEINTRESOURCE(IDI_TOOLBAR), 13);
    //ツールバーボタン追加
    TBBUTTON tbb[18];
    ZeroMemory(tbb, sizeof(tbb));
    tbb[0].iBitmap = TBar.m_id + 0;
    tbb[0].fsState = TBSTATE_ENABLED;
    tbb[0].fsStyle = TBSTYLE_BUTTON;
    tbb[0].idCommand = IDM_NEW;
    tbb[1].iBitmap = TBar.m_id + 1;
    tbb[1].fsState = TBSTATE_ENABLED;
    tbb[1].fsStyle = TBSTYLE_BUTTON;
    tbb[1].idCommand = IDM_OPEN;
    tbb[2].iBitmap = TBar.m_id + 2;
    tbb[2].fsStyle = TBSTYLE_BUTTON;
    tbb[2].idCommand = IDM_SAVE;
    tbb[3].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[4].iBitmap = TBar.m_id + 3;
    tbb[4].fsState = TBSTATE_ENABLED;
    tbb[4].fsStyle = TBSTYLE_BUTTON;
    tbb[4].idCommand = IDM_EXIT;
    tbb[5].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[6].iBitmap = TBar.m_id + 4;
    tbb[6].fsState = TBSTATE_ENABLED;
    tbb[6].fsStyle = TBSTYLE_BUTTON;
    tbb[6].idCommand = IDM_ADD;
    tbb[7].iBitmap = TBar.m_id + 5;
    tbb[7].fsStyle = TBSTYLE_BUTTON;
    tbb[7].idCommand = IDM_DEL;
    tbb[8].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[9].iBitmap = TBar.m_id + 6;
    tbb[9].fsState = TBSTATE_CHECKED;    //TBSTATE_ENABLED;
    tbb[9].fsStyle = TBSTYLE_BUTTON;
    tbb[9].idCommand = IDM_THUMNAIL;
    tbb[10].iBitmap = TBar.m_id + 7;
    tbb[10].fsStyle = TBSTYLE_BUTTON;
    tbb[10].idCommand = IDM_PREV;
    tbb[11].iBitmap = TBar.m_id + 8;
    tbb[11].fsStyle = TBSTYLE_BUTTON;
    tbb[11].idCommand = IDM_NEXT;
    tbb[12].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[13].iBitmap = TBar.m_id + 9;
    tbb[13].fsState = TBSTATE_ENABLED;
    tbb[13].fsStyle = TBSTYLE_BUTTON;
    tbb[13].idCommand = IDM_NORM;
    tbb[14].iBitmap = TBar.m_id + 10;
    tbb[14].fsStyle = TBSTYLE_BUTTON;
    tbb[14].idCommand = IDM_EDIT;
    tbb[15].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[16].iBitmap = TBar.m_id + 11;
    tbb[16].fsState = TBSTATE_ENABLED;
    tbb[16].fsStyle = TBSTYLE_BUTTON;
    tbb[16].idCommand = IDM_HELP;
    tbb[17].iBitmap = TBar.m_id + 12;
    tbb[17].fsState = TBSTATE_ENABLED;
    tbb[17].fsStyle = TBSTYLE_BUTTON;
    tbb[17].idCommand = IDM_VERSION;
    TBar.AddButtons(18, tbb);

    //ステータスバー登録-Init(hWnd, hIinstance, ID, (以下省略可)Style)
    SBar.Init(m_hWnd, m_hInstance, STATUSBAR);
    //ステータスバー区画設定
    int sec[3] = {100, 200, -1};
    SBar.SetSection(3, sec);
    //ステータスバー文字列設定
    SBar.SetText(0, "Album Version 1.0");
    SBar.SetText(1, "回転表示状態");
    SBar.SetText(2, "アルバムファイル(*.alb)名");
//(解説:ほぼSkeltonWizard通りです。ステータスバー区画は、後で回転状態を示そうと思ったので一つ増やし、久々に3分割になりましたね。)

    //CALBUMインスタンスの初期化
    g_Album.Init(m_hWnd);
//(解説:これは出力先ウィンドウに一回だけ行います。)

    //再生用スライダーコントロールの作成
    g_Slider.Create(TBar.GetHandle(), IDC_SLIDER, 340, 0, 300, 24);
    ShowWindow(g_Slider.GetHandle(), SW_HIDE);    //最初はスライダーを隠しておく
    //スライダーの親であるツールバーのWM_HSCROLL処理を行う為のサブクラス化
    g_TBProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(m_hWnd, TOOLBAR), GWLP_WNDPROC);
    SetWindowLongPtr(GetDlgItem(m_hWnd, TOOLBAR), GWLP_WNDPROC, (LONG_PTR)TOOLBARProc);
    g_Slider.SetRange(0, g_Album.m_NoI);    //スライダーの範囲を0~イメージ数に設定
    g_Slider.SetTic(1);                        //スライダーメモリ設定
//(解説:ここの処理はDirectShowの時と同じで、再利用しています。)

    //AlbumHelpへのパス設定
    g_Help = g_Arg.Path();
    g_Help = g_Help + "\\AlbumHelp.chm";
//(解説:CARGクラスのg_Argを使ってヘルプファイルを捕まえておきます。)

    //AlbumListへのパス設定とディレクトリーの作成
    g_Path = g_Arg.Path();
    g_Path = g_Path + "\\AlbumList";
    CreateDirectory(g_Path.ToChar(), NULL);
//(解説:対象となる写真画像はPC内に散在していても大丈夫ですが、アルバムファイルは散逸させないようにこのフォールダーだけにとどめたいと思いました。)

    //ドラッグアンドドロップの受付開始
    DragAcceptFiles(m_hWnd, TRUE);
//(解説:写真画像ファイル追加用です。)

    /////////////////////////////////////////////////////////////
    //OnCreateでファイル起動対応を行おうとすると、OnSize前なので
    //g_Album.m_Widthとm_Heightが未だ0(ゼロ)のままとなり、
    //ShowAlbum関数のところで"Division by zero"エラーとなる。
    //この為ファイル起動はg_ByFileフラグを立ててOnSizeに置く。
    //また、何を投入ファイルについては、
    //(1) *.albは専用フォールダー、パスを設けて集約しており、
    //(2) PCに散在するイメージファイルを対象にした。
    /////////////////////////////////////////////////////////////

//(解説:ファイル起動を行おうとすると"Division by zero"エラーで落ちることから苦肉の策です。)

    return TRUE;
}

bool CMyWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam) {

    if(!g_ShowMode) {    //サムネイル表示モードなら
        //マウスクリック時のx, y座標を取得する
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);
        //マウス座標から写真番号を算出して選択する
        g_Album.Select(g_Album.GetWhichThumb(g_ThumbSize, x, y));
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在の選択写真のあるページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
    }
//(解説:サムネイルモードで写真選択処理(サーモンピンクの矩形枠を付ける)を行います。)
    return TRUE;
}

bool CMyWnd::OnRButtonDown(WPARAM wParam, LPARAM lParam) {

    if(g_Album.GetCount()) {
        HMENU hMenu;
        if(g_ShowMode) {    //単一表示の場合
            hMenu = GetSubMenu(GetSubMenu(GetMenu(m_hWnd), 2), 1);
        }
        else {                //サムネイル表示の場合
            hMenu = GetSubMenu(GetSubMenu(GetMenu(m_hWnd), 2), 0);
        }
        //ウインドウの位置情報を取得
        RECT rec;
        GetWindowRect(m_hWnd, &rec);
        //ポップアップメニューの表示
        POINT pt;
        GetCursorPos(&pt);
        TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
            pt.x, pt.y, 0, m_hWnd, &rec);
        return TRUE;
    }
    else
        return FALSE;
}
//(解説:右マウスボタンクリックはポップアップメニュー処理です。モード別になっています。)

bool CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam) {

    //ツールバーツールチップ
    if(((LPNMHDR)lParam)->code == TTN_NEEDTEXT) {    //TTN_GETDISPINFOと同じ
        ((LPTOOLTIPTEXT)lParam)->hinst = m_hInstance;
        char* tag;
        switch(((LPTOOLTIPTEXT)lParam)->hdr.idFrom) {
        case IDM_NEW:
            tag = "アルバムの作成、編集";
            break;
        case IDM_OPEN:
            tag = "アルバムを開く";
            break;
        case IDM_SAVE:
            tag = "アルバムの保存";
            break;
        case IDM_EXIT:
            tag = "終了";
            break;
        case IDM_ADD:
            tag = "写真の追加";
            break;
        case IDM_DEL:
            tag = "写真の削除";
            break;
        case IDM_THUMNAIL:
            tag = "サムネイル表示";
            break;
        case IDM_PREV:
            tag = "前へ";
            break;
        case IDM_NEXT:
            tag = "次へ";
            break;
        case IDM_NORM:
            tag = "標準表示";
            break;
        case IDM_EDIT:
            tag = "写真の裏書編集";
            break;
        case IDM_HELP:
            tag = "Albumについて";
            break;
        case IDM_VERSION:
            tag = "バージョン情報";
            break;
        }
        ((LPTOOLTIPTEXT)lParam)->lpszText = tag;
        return TRUE;
    }
    return FALSE;
//(解説:定番のツールバーツールチップ処理です。)
}

bool CMyWnd::OnSize(WPARAM wParam, LPARAM lParam) {

    TBar.AutoSize();
    SBar.AutoSize();
    g_Slider.AutoSize(1, 0, 0);    //右上角に離さずに表示
    //メインウィンドウの有効クライアントエリアを取得
    g_Album.GetSize(lParam, TBar.GetHandle(), SBar.GetHandle());
//(解説:ここにGetSizeメソッドを置くことで常に新しいウィンドウサイズを監視できます。)
    //ファイル起動対応(g_ByFileが真の時、1回のみ)
    if(g_ByFile) {
        for(int i = 1; i < g_Arg.c(); i++) {
            if(g_ExtChk.CheckExt(g_Arg.v(i), IMGFILTER))    //対象ファイルか否かチェック
                g_Album.Add(g_Arg.v(i));                    //Image配列への追加
            else
                MessageBox(m_hWnd, g_Arg.v(i), "再生対象外のファイル", MB_OK | MB_ICONERROR);
        }
        //アルバムの表示(新しいアルバム)
        ShowAlbum(TRUE);
        g_ByFile = FALSE;
    }
//(解説:↑で述べた苦肉の策の中身です。)
    return TRUE;
}

bool CMyWnd::OnPaint(WPARAM wParam, LPARAM lParam) {

    PAINTSTRUCT paint;
    //仮想ウィンドウをウィンドウへ張り付ける
    g_Album.OnPaint(BeginPaint(m_hWnd, &paint));
//(解説:WWM_PAINTメッセージで処理する仮想ウィンドウ(ウィンドウとコンパチのビットマップ)を張り付ける処理です。)
    EndPaint(m_hWnd, &paint);
    return TRUE;
}

bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {

    if(MessageBox(m_hWnd, "終了しますか", "終了確認",
                    MB_YESNO | MB_ICONINFORMATION) == IDYES)
        //処理をするとDestroyWindow、PostQuitMessageが呼ばれる
        return TRUE;
    else
        //そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
        return FALSE;
}

bool CMyWnd::OnDropFiles(WPARAM wParam, LPARAM lParam) {

    //ドラッグアンドドロップされたファイル名取得と処理
    static char FileName[MAX_PATH];
    int n;                                    //ファイル数
    HDROP hDrop = (HDROP)wParam;             //HDROPを取得
    n = DragQueryFile(hDrop, -1, NULL, 0);    //ファイル数を取得
    for(int i = 0; i < n; i++) {
        DragQueryFile(hDrop, i, FileName, MAX_PATH);    //ファイル名を取得
        if(g_ExtChk.CheckExt(FileName, IMGFILTER)) {    //対象ファイルか否かチェック
            g_Album.Add(FileName);    //Image配列への追加
        }
        else
            MessageBox(m_hWnd, FileName, "再生対象外のファイル", MB_OK | MB_ICONERROR);
    }
    //ドラッグアンドドロップの終了
    DragFinish((HDROP)wParam);
    //アルバムの表示(写真の追加)
    ShowAlbum(FALSE);
    return TRUE;
}
//(解説:ドラッグアンドドロップの対象ファイルはフォールダーを定めた*.albではなく、散在する写真画像ファイルにしています。)

bool CMyWnd::OnMinMax(WPARAM wParam, LPARAM lParam) {

    //典型的なウィンドウのサイズ制限処理
    MINMAXINFO *pmmi;
    pmmi = (MINMAXINFO*)lParam;
    pmmi->ptMinTrackSize.x = MINW + 16;    //クライアントエリア + 16
    pmmi->ptMinTrackSize.y = MINH + 61;    //クライアントエリア + 61
    return FALSE;                            //処理はDefWndProcに任す
}

/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnNew() {

    //ファイルリストダイアログで*.albファイルを作成、編集
    char* cp = GetFileList("アルバムファイル(*.alb)\0*.alb\0\0",
                            IMGFILTER, g_Path.ToChar(), "alb");
//(解説:DLLのFileListを呼び出しています。)
    //作成できたなら一旦保存してOnOpenで読み込む
    if(*cp) {
        g_FileName = cp;
        //アルバムファイルを読む
        ReadAlbum();
        //アルバムの表示(新しいアルバム)
        ShowAlbum(TRUE);
//(解説:「アルバムを読んで、表示する」という基本動作です。)
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnOpen() {

    //「ファイルを開く」ダイアログを使う
    char* cp = g_Cmndlg.GetFileName(m_hWnd, FILEFLT, TRUE, "alb", g_Path.ToChar());
    if(cp) {
        g_FileName = cp;    //ファイルを開くダイアログでファイルを選択
        //アルバムファイルを読む
        ReadAlbum();
        //アルバムの表示(新しいアルバム)
        ShowAlbum(TRUE);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
//(解説:FileListダイアログか、「ファイルを開く」ダイアログか、の違いだけですね。)
}

bool CMyWnd::OnSave() {

    //現在のファイル名が無ければ「名前を付けて保存」へ移行
    if(!*g_FileName.ToChar())
        return OnSaveas();
    //現在のファイル名で保存
    else {
        //現在の選択ファイルを保存
        int sel = g_Album.GetSelected();
        //g_Fileにファイルデータを入れ、保存する
        g_File = "";
        for(int i = 0; i < g_Album.GetCount(); i++) {
            g_Album.Select(i);
            g_File = g_File + "\"";
            g_File = g_File + g_Album.GetFileName();
            g_File = g_File + "\",\"";
            g_File = g_File + g_Album.GetComment();
            g_File = g_File + "\"\n";
        }
        g_File.ToFile(g_FileName.ToChar());
        //現在の選択ファイルに戻す
        g_Album.Select(sel);
//(解説:ちょうどsel変数がスタックの役割をしてくれています。)
    }
    return TRUE;
}

bool CMyWnd::OnSaveas() {

    //「ファイルを保存」ダイアログでパス、ファイル名を指定
    char* cp = g_Cmndlg.GetFileName(m_hWnd, FILEFLT, FALSE, "alb", g_Path.ToChar());
    if(cp)
        g_FileName = cp;    //ファイルを保存ダイアログでファイルを選択
    else {
        MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //現在の選択ファイルを保存
    int sel = g_Album.GetSelected();
    //g_Fileにファイルデータを入れ、保存する
    g_File = "";
    for(int i = 0; i < g_Album.GetCount(); i++) {
        g_Album.Select(i);
        g_File = g_File + "\"";
        g_File = g_File + g_Album.GetFileName();
        g_File = g_File + "\",\"";
        g_File = g_File + g_Album.GetComment();
        g_File = g_File + "\"\n";
    }
    g_File.ToFile(g_FileName.ToChar());
    //現在の選択ファイルに戻す
    g_Album.Select(sel);
    //読み込んだアルバムファイル名をステータスバーに表示
    SBar.SetText(2, g_FileName.ToChar());
    return TRUE;
}
//(解説:ほぼSaveと同じなので、セーブ部分を下位関数にしてもよいですね。)

bool CMyWnd::OnExit() {

    SendMsg(WM_CLOSE, 0, 0);
    return TRUE;
}

bool CMyWnd::OnAdd() {

    //「ファイルを開く」ダイアログで選択、追加
    if(!g_Album.Add("")) {
        MessageBox(m_hWnd, "写真を追加できませんでした", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //スライダーの範囲を0~イメージ数 - 1に設定
    g_Slider.SetRange(0, g_Album.GetCount() - 1);
    //スライダーの位置をイメージ数 - 1に設定
    g_Slider.SetPosition(g_Album.GetCount() - 1);
    //アルバムに追加された写真を選択
    g_Album.Select(g_Album.GetCount() - 1);
    //画面を消去
    g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
    g_Album.Cls();                        //画面クリアー
    if(g_ShowMode) {    //単一表示の場合
        //画面中央にサイズ調整して表示
        g_Album.ShowImg(FALSE, FALSE);
    }
    else {                //サムネイル表示の場合
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在の選択写真のあるページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
    }
//(解説:この一連の動作は分かりやすいと思います。)
    //メニュー、ボタンを初期状態から変える
    ChangeState(TRUE);
    return TRUE;
}

bool CMyWnd::OnDel() {

    //最終確認
    if(MessageBox(m_hWnd, "選択された写真をアルバムから削除しますか?", "確認",
                    MB_YESNO | MB_ICONQUESTION) == IDNO) {
        return FALSE;
    }
    g_Album.Delete(g_Album.GetSelected());
    //スライダーの範囲を0~イメージ数 - 1に設定
    g_Slider.SetRange(0, g_Album.GetCount() - 1);
    g_Slider.SetPosition(g_Album.GetCount() - 1);
    //画面を消去
    g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
    g_Album.Cls();                        //画面クリアー
    if(g_ShowMode) {    //単一表示の場合
        //現在の選択写真を中央にサイズ調整して表示
        g_Album.ShowImg(FALSE, FALSE);
    }
    else {                //サムネイル表示の場合
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在の選択写真のあるページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
    }
//(解説:ちょうど追加の反対の処理を行っているのが分かります。最初は必ずしも同じ書き方をしていませんでしたが、分かりやすくするために処理の順も併せました。)
    //写真がなくなったらメニュー、ボタンを初期状態にする
    if(!g_Album.GetCount())
        ChangeState(FALSE);
    return TRUE;
}

bool CMyWnd::OnThumnail() {

    //表示モードを変更
    g_ShowMode = FALSE;
    //メニューチェックを変更し、ツールバーボタンを表示
    ChangeMode(g_ShowMode);
    //スライダーを隠す
    ShowWindow(g_Slider.GetHandle(), SW_HIDE);
    if(g_Album.GetCount()) {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在の選択写真のあるページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
        return TRUE;
    }
//(解説:既に写真画像の追加、削除で見てきた処理です。)
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnPrevious() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //現在位置の取得
    int pos = g_Album.GetSelected();
    //最終位置の取得
    int max = g_Album.GetCount() - 1;
    //メニューとボタンの状態変更
    HMENU hMenu = GetSubMenu(GetMenu(m_hWnd), 2);    //メニューハンドルの取得
    if(!pos) {                //先頭にいるなら「前へ」を無効にして戻る
        //メニュー
        EnableMenuItem(hMenu, IDM_PREV, MF_BYCOMMAND | MF_GRAYED);
        //ツールバーボタン
        SendMessage(TBar.GetHandle(), TB_SETSTATE, IDM_PREV, MAKELONG(0, 0));
        //警告音
        MessageBeep(MB_ICONHAND);
        return TRUE;
    }
    else if(pos == max){    //末尾にいるなら「次へ」を有効にする
        //メニュー
        EnableMenuItem(hMenu, IDM_NEXT, MF_BYCOMMAND | MF_ENABLED);
        //ツールバーボタン
        SendMessage(TBar.GetHandle(), TB_SETSTATE, IDM_NEXT, MAKELONG(TBSTATE_ENABLED, 0));
    }
//(解説:現在の位置と処理でメニューとボタンの状態を変化させる処理です。端にいて動かない時の警告音はMessageBeepを使いました。)
    //表示変更
    if(g_ShowMode) {    //単一表示の場合
        //写真を一つ戻す
        g_Album.Select(pos - 1);
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //画面中央サイズ調整で表示
        g_Album.ShowImg(FALSE, FALSE);
        //スライダーを一つ戻す
        g_Slider.SetPosition(g_Slider.GetPosition() - 1);
    }
    else {                //サムネイル表示の場合
        //選択写真を1ページ分戻す(1ページ分なければ先頭写真を選択)
        g_Album.Select(pos - g_Album.HowManyThumb(g_ThumbSize));
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在のページのひとつ前を表示
        g_Album.ShowThumbnail(g_ThumbSize);
    }
    return TRUE;
}

bool CMyWnd::OnNext() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //現在位置の取得
    int pos = g_Album.GetSelected();
    //最終位置の取得
    int max = g_Album.GetCount() - 1;
    //メニューとボタンの状態変更
    HMENU hMenu = GetSubMenu(GetMenu(m_hWnd), 2);    //メニューハンドルの取得
    if(pos == max) {    //末尾にいるなら「次へ」を無効にして戻る
        //メニュー
        EnableMenuItem(hMenu, IDM_NEXT, MF_BYCOMMAND | MF_GRAYED);
        //ツールバーボタン
        SendMessage(TBar.GetHandle(), TB_SETSTATE, IDM_NEXT, MAKELONG(0, 0));
        //警告音
        MessageBeep(MB_ICONHAND);
        return TRUE;
    }
    else if(!pos) {        //先頭にいるなら「前へ」を有効にする
        //メニュー
        EnableMenuItem(hMenu, IDM_PREV, MF_BYCOMMAND | MF_ENABLED);
        //ツールバーボタン
        SendMessage(TBar.GetHandle(), TB_SETSTATE, IDM_PREV, MAKELONG(TBSTATE_ENABLED, 0));
    }
    //表示変更
    if(g_ShowMode) {    //単一表示の場合
        //写真を一つ進める
        g_Album.Select(pos + 1);
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //画面中央サイズ調整で表示
        g_Album.ShowImg(FALSE, FALSE);
        //スライダーを一つ進める
        g_Slider.SetPosition(g_Slider.GetPosition() + 1);
    }
    else {                //サムネイル表示の場合
        //選択写真を1ページ分進める(1ページ分なければ最終写真を選択)
        g_Album.Select(pos + g_Album.HowManyThumb(g_ThumbSize));
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで現在の選択のページの次を表示
        g_Album.ShowThumbnail(g_ThumbSize);
    }
//(解説:既に説明している内容です。)
    return TRUE;
}

bool CMyWnd::OnLarge() {

    g_ThumbSize = LARGE;
    if(g_Album.GetCount()) {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで選択写真を含むページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnSmall() {

    g_ThumbSize = SMALL;
    if(g_Album.GetCount()) {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //サムネイル上の選択写真枠設定色とペン
        g_Album.SetCol(128, 255, 64, 64);
        g_Album.SetPen(8);
        //サムネイルで選択写真を含むページを表示
        g_Album.ShowThumbnail(g_ThumbSize);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}
//(解説:サムネイルの大、小変更処理です。内容は同じですね。サイズの違うMEDIUMを入れて同じ処理をすれば「中サイズ」処理ができます。)

bool CMyWnd::OnNorm() {

    //表示モードを変更
    g_ShowMode = TRUE;
    //メニューチェックを変更し、ツールバーボタンを非表示
    ChangeMode(g_ShowMode);
    //スライダーを現在選択されている写真で表示する
    ShowWindow(g_Slider.GetHandle(), SW_SHOW);
    g_Slider.SetPosition(g_Album.GetSelected());
    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    else {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //選択写真を画面中央サイズ調整で表示
        g_Album.ShowImg(FALSE, FALSE);
        return TRUE;
    }
}
//(解説:単一画像表示処理です。画像の回転やサイズ変更とその変更の保存はこのモードで行うようにしています。)

bool CMyWnd::OnOrigin() {

    if(g_Album.GetCount()) {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //選択写真を左上に原寸で表示
        g_Album.ShowImg(TRUE, TRUE);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnAdjust() {

    if(g_Album.GetCount()) {
        //画面を消去
        g_Album.SetCol(255, 255, 255, 255);    //Alph = 255、白色で
        g_Album.Cls();                        //画面クリアー
        //選択写真を画面中央サイズ調整で表示
        g_Album.ShowImg(FALSE, FALSE);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}
//(解説:原寸表示とサイズ変更中央表示です。g_Album.ShowImgの引数の違いに注意してください。)

bool CMyWnd::OnRotate0() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    g_Album.SetRotateFlip(RotateNoneFlipNone);
    OnNorm();
    ChangeRotateFlip(0);
    return TRUE;
}

bool CMyWnd::OnRotate90() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    g_Album.SetRotateFlip(Rotate90FlipNone);
    OnNorm();
    ChangeRotateFlip(1);
    return TRUE;
}

bool CMyWnd::OnRotate180() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    g_Album.SetRotateFlip(Rotate180FlipNone);
    OnNorm();
    ChangeRotateFlip(2);
    return TRUE;
}

bool CMyWnd::OnRotate270() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    g_Album.SetRotateFlip(Rotate270FlipNone);
    OnNorm();
    ChangeRotateFlip(3);
    return TRUE;
}
//(解説:↑の4つは全て同じ処理でenum型のRotateFlipType引数だけ違います。この引数はCALBUM.hに説明を入れていますが、Microsoft Docを確認してみてください。)

bool CMyWnd::OnResize() {

    if(g_Album.GetCount()) {
        resizedlg.DoModal(m_hWnd, "IDD_RESIZE", resizedlgProc, m_hInstance);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}
//(解説:IDD_RESIZEダイアログを呼び出します。フォールダー内の対象写真画像一括処理を行うDLLのResizerとは異なり、単一写真画像処理です。

bool CMyWnd::OnEdit() {

    if(g_Album.GetCount()) {
        //写真の裏書ダイアログを開く
        editdlg.DoModal(m_hWnd, "IDD_EDIT", editdlgProc, m_hInstance);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}
//(解説:写真の裏書処理です。)

bool CMyWnd::OnDetail() {

    if(g_Album.GetCount()) {
        //写真のプロパティダイアログを開く
        SHELLEXECUTEINFO ShInfo;
        ZeroMemory(&ShInfo, sizeof(SHELLEXECUTEINFO));
        ShInfo.cbSize    = sizeof(SHELLEXECUTEINFO);
        ShInfo.hwnd        = m_hWnd;
        ShInfo.lpVerb    = "properties";
        ShInfo.lpFile    = g_Album.GetFileName();
        ShInfo.fMask    = SEE_MASK_INVOKEIDLIST;
        ShInfo.nShow    = SW_SHOWNORMAL;
        ShellExecuteEx(&ShInfo);
//(解説:今回私も初めて使いましたが、OSサービスのファイルの「プロパティダイアログ」を表示する処理です。)
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnGather() {

    if(!g_Album.GetCount()) {
        MessageBox(m_hWnd, "写真がありません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //アルバム写真を纏めるフォールダーを取得
    char* cp = g_Cmndlg.GetPath(m_hWnd, "アルバム写真を纏めるフォールダーの指定");
    if(!cp) {
        MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    //ファイルパスを'\'付きで記録
    CSTR path = cp;
    path = path + "\\";
    //fnに纏めフォールダーのパスを代入
    CSTR fn = path;
    //選択写真の記録
    int sel = g_Album.GetSelected();
    //写真数を取得
    int lmt = g_Album.GetCount();
    for(int i = 0; i < lmt; i++) {
        //写真を選択
        g_Album.Select(i);
        //選択写真のフルパスファイル名を取得
        g_Arg = cp = g_Album.GetFileName();
        //fnにファイル名のみ付加
        fn = fn + g_Arg.FileName();
        //ファイルを上書きコピー
        CopyFile(cp, fn.ToChar(), FALSE);
        //fnに纏めフォールダーのパスを代入
        fn = path;
    }
    //選択写真を再選択
    g_Album.Select(sel);
//(解説:実は実際にアルバムを使ってみて、「これは欲しいな」ということで付け足した処理です。アルバムファイルはPC内に散在するファイルを対象にしますが、それを人に渡す等の場合、「一つのフォールダーに(サイズをそろえて)集めたいな」と思うことがあるでしょう。その為の処理です。)
    return TRUE;
}

bool CMyWnd::OnResizer() {

    ResizeImages();
//(解説:あっさりとしていますが、DLLのResizerという1フォールダー内のGDI+が対象とするイメージファイルを大きさの条件を付けて合致するものを縮小してコピー、または上書きするツールです。別途シリーズで解説します。)
    return TRUE;
}

bool CMyWnd::OnHelp() {

    CSTR cmd = "hh \"";
    cmd = cmd + g_Help + "\"";
    WinExec(cmd.ToChar(), SW_SHOWNORMAL);
    return TRUE;
//(解説:ヘルプファイルを開きます。)
}

bool CMyWnd::OnVersion() {

    versiondlg.DoModal(m_hWnd, "IDD_VERSION", versiondlgProc, m_hInstance);
    return TRUE;
}