昨日書けなかったBatchGood.Proc.hについて解説します。

【Proc.hBatchGood.h】
//////////////////////////////////////////
// BatchGoodProc.h
// Copyright (c) 12/24/2021 by BCCSkelton
//////////////////////////////////////////
///////////////////////////////
//ユーザーダイアログの関数定義
//BATCHGOODDLG
///////////////////////////////
bool BATCHGOODDLG::OnInit(WPARAM wParam, LPARAM lParam) {

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

    //ツールバー登録-SetHandle(hWnd))
    TBar.SetHandle(GetDlgItem(m_hWnd, IDC_TOOLBAR));
    //ツールバーボタン用カスタムビットマップ追加
    TBar.AddBmp(m_hInstance, MAKEINTRESOURCE(IDI_TOOLBAR), 7);
    //ツールバーボタン追加
    TBBUTTON tbb[11];
    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_OPEN;
    tbb[1].iBitmap = TBar.m_id + 1;
    tbb[1].fsState = TBSTATE_ENABLED;
    tbb[1].fsStyle = TBSTYLE_BUTTON;
    tbb[1].idCommand = IDM_CLOSED;
    tbb[2].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[3].iBitmap = TBar.m_id + 2;
    tbb[3].fsState = TBSTATE_ENABLED;
    tbb[3].fsStyle = TBSTYLE_BUTTON;
    tbb[3].idCommand = IDM_EXIT;
    tbb[4].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[5].iBitmap = TBar.m_id + 3;
    tbb[5].fsState = TBSTATE_ENABLED;
    tbb[5].fsStyle = TBSTYLE_BUTTON;
    tbb[5].idCommand = IDM_RUN;
    tbb[6].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[7].iBitmap = TBar.m_id + 4;
    tbb[7].fsState = TBSTATE_ENABLED;
    tbb[7].fsStyle = TBSTYLE_BUTTON;
    tbb[7].idCommand = IDM_SETTING;
    tbb[8].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[9].iBitmap = TBar.m_id + 5;
    tbb[9].fsState = TBSTATE_ENABLED;
    tbb[9].fsStyle = TBSTYLE_BUTTON;
    tbb[9].idCommand = IDM_HELP;
    tbb[10].iBitmap = TBar.m_id + 6;
    tbb[10].fsState = TBSTATE_ENABLED;
    tbb[10].fsStyle = TBSTYLE_BUTTON;
    tbb[10].idCommand = IDM_VERSION;
    TBar.AddButtons(11, tbb);

    //ステータスバー登録-SetHandle(hWnd))
    SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));
    //ステータスバー区画設定
    int sec[2] = {210, -1};
    SBar.SetSection(2, sec);
    //ステータスバー文字列設定
    SBar.SetText(0, "BatchGood Ver1.1 (c) 2021 By Ysama");
(解説:ここら辺までは殆どSkeltonWizardの作成したままです。)

    //ファイルパスの設定
    g_OwnPath = Arg.Path();
    g_IniFile = g_OwnPath + "\\BatchGood.ini";
    Ini.SetName(g_IniFile.ToChar());
    g_Bcc32 = Ini.ReadStr("System", "BCC32");
    g_Brc32 = Ini.ReadStr("System", "BRC32");
    g_Edit = Ini.ReadStr("System", "EDITOR");
    g_HelpFile = g_OwnPath + "\\BatchGoodHelp.chm";
(解説:CARGクラスのArgとCINIクラスのIniを使って各種パスを設定します。)

    //削除フラグの設定
    g_Deltds = Ini.ReadInt("System", "DELTDS");
    g_Delres = Ini.ReadInt("System", "DELRES");
(解説:「設定」ダイアログクラスで設定するtdsファイル、resファイルの削除するか否かのフラグです。)

    //ツリービューコントロールの初期化(ダイアログのコントロールに関連付ける)
    tv.m_hWnd = GetDlgItem(m_hWnd, IDC_FILETREE);
    tv.m_hInstance = m_hInstance;
    tv.SetFont(8, "MS 明朝");
    //ビットマップを登録
    tv.AddBitmap(MAKEINTRESOURCE(IDI_CLOSE), m_hInstance, RGB(255, 255, 255));
    tv.AddBitmap(MAKEINTRESOURCE(IDI_CPPBMP), m_hInstance, RGB(255, 255, 255));
    tv.AddBitmap(MAKEINTRESOURCE(IDI_RCBMP), m_hInstance, RGB(255, 255, 255));
    tv.AddBitmap(MAKEINTRESOURCE(IDI_LIBBMP), m_hInstance, RGB(255, 255, 255));
    tv.AddBitmap(MAKEINTRESOURCE(IDI_DOC), m_hInstance, RGB(255, 255, 255));
    //本来はCreate()関数で自動的に行うが、ダイアログベースなので手動
    TreeView_SetImageList(tv.m_hWnd, tv.m_hIList, TVSIL_NORMAL);
(解説:ツリービュ―の基礎情報を設定してから、閉めたフォールダー、CPPフォールダー、RCフォールダー、LIBフォールダーと文書のアイコンを設定します。)

    //コマンドエディットコントロールの初期化
    ce.m_hWnd = GetDlgItem(m_hWnd, IDC_OUTPUT);
    ce.m_hInstance = m_hInstance;
    ce.SetFont(8, "MS 明朝");
(解説:メインダイアログ下の出力用ダイアログをコマンドエディットクラスでラップし、フォントを設定します。)

    //ダイアログの初期化
    InitFileVars();        //ファイルパス、名やオプション等の初期化
    InitTreeview();        //ツリービュ―の初期化(User.h参照)
(解説:User.hの初期化関数を呼びます。)

    //クライアントエリア初期化
    RECT rec;
    GetClientRect(m_hWnd, &rec);
    m_Width = rec.right - rec.left;
    m_Height = rec.bottom - rec.top;
(解説:WM_SIZE用のメンバー変数にウィンドウのクライアントエリアの初期値を保存します。)

    //ドラッグアンドドロップの受付開始
    DragAcceptFiles(m_hWnd, TRUE);
(解説:以降ドラッグアンドドロップを受け付けます。)

    //データファイルで起動された場合、ファイルをオープンする
    if(g_ByFile) {
        int n = Arg.c();                //ファイル数を取得
        for(int i = 1; i < n; i++) {    //ファイル数だけ繰り返す
            g_ptFile = Arg.v(i);        //g_ptFileでファイルパス、名を渡す
            g_ByFile =TRUE;
            OnOpen();
        }
    }
(解説:複数の起動時引数ファイルを受け付ける場合の書き方です。定番ですので覚えてください。)

    return TRUE;
}

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

    //ツールバーからのツールチップ情報取得
    switch (((LPNMHDR)lParam)->code) {
        case TTN_NEEDTEXT:
            static LPTOOLTIPTEXT lptip;
            lptip = (LPTOOLTIPTEXT)lParam;
            switch (lptip->hdr.idFrom) {
                case IDM_OPEN:
                    lptip->lpszText = "ファイルを開く";
                    break;
                case IDM_CLOSED:
                    lptip->lpszText = "ファイルを閉じる";
                    break;
                case IDM_EXIT:
                    lptip->lpszText = "BatchGoodの終了";
                    break;
                case IDM_RUN:
                    lptip->lpszText = "バッチファイルの実行";
                    break;
                case IDM_SETTING:
                    lptip->lpszText = "設定";
                    break;
                case IDM_HELP:
                    lptip->lpszText = "BatchGoodヘルプ";
                    break;
                case IDM_VERSION:
                    lptip->lpszText = "バージョン情報";
                    break;
            }
    }
(解説:ツールバーツールチップの定番処理ですね。)
    //ポップアップメニューを読みこむ
    HMENU hMenu = LoadMenu(m_hInstance, "IDM_POPUP");
    HMENU hPopupMenu = GetSubMenu(hMenu, 0);
    if(wParam == IDC_FILETREE) {
        //選択されているアイテムを取得
        HTREEITEM hSelected = tv.GetSelection();
        //メッセージを解読し、
        TV_DISPINFO* ptv_disp;
        ptv_disp = (TV_DISPINFO*)lParam;
        //ダブルクリックだったら
        if(ptv_disp->hdr.code == NM_DBLCLK) {
            CSTR Cmd = "\"";
            Cmd = Cmd + g_Edit + "\" \"";
            char Buff[MAX_PATH];
            //選択アイテムの文字列を取得し、これでフィルターを作成してプログラムを実行
            if(hSelected) {
                tv.GetItem(hSelected, Buff, MAX_PATH);
                if(strstr(Buff, "ファイル"))                //選択アイテムがCPPかRCのファイルなら
                    SendMsg(WM_COMMAND, IDC_OPEN, NULL);    //「ファイルを開く」に移行
                else {                                        //LIB以外のファイルならエディターを起動
                    if(tv.GetParent(hSelected) != g_hTreeLIB) {
                        Cmd = Cmd + Buff;
                        Cmd = Cmd + "\"";
                        WinExec(Cmd.ToChar(), SW_SHOWNORMAL);
                    }
                }
            }
        }
(解説:左ダブルクリックだったならば、フォールダーの場合OnOpen()、ファイルの場合設定したエディターで開きます。)
        //右クリックだったらポップアップメニュー
        else if(ptv_disp->hdr.code == NM_RCLICK) {
            //ポップアップメニューの表示
            POINT pt;
            GetCursorPos(&pt);
            TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
                          pt.x, pt.y, 0, m_hWnd, NULL);
        }
    }
    //メニューリソースの開放
    DestroyMenu(hMenu);
(解説:右クリックの場合は、↑で読み込んだメニューのハンドルを使った定番のポップアップメニュー処理です。)
    return TRUE;
}

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


    //ツールバー再設定
    TBar.AutoSize();
    //ステータスバー再設定
    SBar.AutoSize();
(解説:以下でメンバー変数を使います。ウィンドウサイズを変えた場合の処理は色々なやり方がありますが、今回はBCCMakerとは異なる「差異方式」で組んでみました。)
    //コントロールの位置、サイズ変更
    RECT rec;    //矩形取得用
    POINT pt;    //スクリーン座標変換用
    int w, h;    //幅、高さ計算用
    int diffx = LOWORD(lParam) - m_Width;    //前回と今回の差分
    int diffy = HIWORD(lParam) - m_Height;    //前回と今回の差分
    m_Width = LOWORD(lParam);                //今回の幅
    m_Height = HIWORD(lParam);                //今回の高さ
    //左右移動のみ
    //IDC_DETAIL
    GetWindowRect(GetDlgItem(m_hWnd, IDC_DETAIL), &rec);    //ウィンドウ位置取得
    w = rec.right - rec.left;
    h = rec.bottom - rec.top;
    pt.x = rec.left;
    pt.y = rec.top;
    ScreenToClient(m_hWnd, &pt);
    MoveWindow(GetDlgItem(m_hWnd, IDC_DETAIL), pt.x + diffx, pt.y, w, h, TRUE);
    //幅変更のみ
    //IDC_BCC32OPTION、IDC_BRC32OPTION、IDC_INCLPATH
    for(int i = IDC_BCC32OPTION; i <= IDC_INCLPATH; i++) {
        GetWindowRect(GetDlgItem(m_hWnd, i), &rec);    //ウィンドウ位置取得
        w = rec.right - rec.left;
        h = rec.bottom - rec.top;
        pt.x = rec.left;
        pt.y = rec.top;
        ScreenToClient(m_hWnd, &pt);
        MoveWindow(GetDlgItem(m_hWnd, i), pt.x, pt.y, w + diffx, h, TRUE);
    }
    //幅高さ変更
    //IDC_OUTPUT
    GetWindowRect(GetDlgItem(m_hWnd, IDC_OUTPUT), &rec);    //ウィンドウ位置取得
    w = rec.right - rec.left;
    h = rec.bottom - rec.top;
    pt.x = rec.left;
    pt.y = rec.top;
    ScreenToClient(m_hWnd, &pt);
    MoveWindow(GetDlgItem(m_hWnd, IDC_OUTPUT), pt.x, pt.y, w + diffx, h + diffy, TRUE);
    //クライアントエリアを再描画
    InvalidateRect(m_hWnd, NULL, TRUE);
    return TRUE;
}

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

    if(MessageBox(m_hWnd, "終了しますか", "終了確認",
                    MB_YESNO | MB_ICONINFORMATION) == IDYES) {
        //Iniファイルに書き込んで終了
        Ini.WriteStr("System", "BCC32", g_Bcc32.ToChar());
        Ini.WriteStr("System", "BRC32", g_Brc32.ToChar());
        Ini.WriteStr("System", "EDITOR", g_Edit.ToChar());
        Ini.WriteInt("System", "DELTDS", g_Deltds);
        Ini.WriteInt("System", "DELRES", g_Delres);
(解説:CINIクラスはこういう処理が得意です。)
        //処理をするとDestroyWindow、PostQuitMessageが呼ばれる
        return TRUE;
    }
    else
        //そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
        return FALSE;
}

//ダイアログベースの場合はこれが必要
bool BATCHGOODDLG::OnDestroy(WPARAM wPram, LPARAM lParam) {

    PostQuitMessage(0);
    return TRUE;
}

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

    //典型的なドラッグアンドドロップ処理
    static char lpszFn[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, lpszFn, MAX_PATH);    //ファイル名を取得
        g_ptFile = lpszFn;                            //g_ptFileでファイルパス、名を渡す
        g_ByFile = TRUE;
        OnOpen();
    }
    DragFinish(hDrop);                                //終了処理
(解説:ドラッグアンドドロップ処理ですが、今回は複数ファイルに対応する場合の定番書式です。)
    return TRUE;
}

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

    //典型的なウィンドウのサイズ制限処理
    MINMAXINFO *pmmi;
    pmmi = (MINMAXINFO*)lParam;
    pmmi->ptMinTrackSize.x = 439;    //クライアントエリア423 + 16
    pmmi->ptMinTrackSize.y = 391;    //クライアントエリア330 + 61

    return FALSE;                    //処理はDefWndProcに任す
}
(解説:最小サイズ設定の定番処理でしたね。)

/////////////////////
// BATCHGOODDLG
//コントロールの関数
/////////////////////
bool BATCHGOODDLG::OnOpen() {

    //フィルター用変数
    char Buff[MAX_PATH];
    char* Filter[] = {    "cppファイル\0*.cpp\0\0",
                        "rcファイル\0*.rc\0\0",
                        "libファイル\0*.lib\0\0",
                        "cppファイル、rcファイルまたはlibファイル\0*.cpp;*.rc;*.lib\0\0"};
    int nFilter;
    //ツリービュ―用変数
    HTREEITEM hSelected, tvParent;
(解説:今回OnOpenはプッシュボタンからの起動が基本です。しかし、メニューや起動ファイル、D&Dファイルの場合も一緒に処理するので、g_ByFileフラグで仕切っています。)
    //メニューやコントロールによるものか、それ以外かによる分岐
    if(g_ByFile)        //ファイル引数付き起動、Drag and Dropまたはメニューの場合
        g_ByFile = FALSE;        //初期状態に戻す
    else {                //コントロールの場合
        //選択されているアイテムの文字列を取得し、これでフィルターを作成
        hSelected = tv.GetSelection();
        if(hSelected) {
            tv.GetItem(hSelected, Buff, MAX_PATH);
            if(!lstrcmp(Buff, "CPPファイル"))        nFilter = 0;
            else if(!lstrcmp(Buff, "RCファイル"))    nFilter = 1;
            else if(!lstrcmp(Buff, "LIBファイル"))    nFilter = 2;
            else                                     nFilter = 3;
        }
        else                                        nFilter = 3;
        //ファイル名を取得
        g_ptFile = cmndlg.GetFileName(m_hWnd, Filter[nFilter], TRUE);
        if(!g_ptFile) {
            MessageBox(m_hWnd, "ファイルが選択されませんでした", "エラー", MB_OK | MB_ICONSTOP);
            return FALSE;
        }
    }
(解説:else以下はプッシュボタン処理です。選択ツリービュ―アイテムで「ファイルを開く」ダイアログのフィルターを変えています。)
    //ファイル名からツリービュ―の親を確認
    if(strstr(g_ptFile, ".cpp") || strstr(g_ptFile, ".CPP"))
        tvParent = g_hTreeCPP;
    else if(strstr(g_ptFile, ".rc") || strstr(g_ptFile, ".RC"))
        tvParent = g_hTreeRC;
    else if(strstr(g_ptFile, ".lib") || strstr(g_ptFile, ".LIB"))
        tvParent = g_hTreeLIB;
    else {
        MessageBox(m_hWnd, "コンパイルできないファイルです", "エラー", MB_OK | MB_ICONSTOP);
        return FALSE;
    }
     //CPPまたはRCファイルで、既にファイルが存在する場合、差し替えるか否か確認
    if(tvParent != g_hTreeLIB) {
        hSelected = tv.GetChild(tvParent);    //そのカテゴリーの最初の子アイテム
        if(hSelected) {
            if(MessageBox(m_hWnd, "既にファイルがあります。\r\n差し替えますか?", "差し替え確認",
                MB_YESNO | MB_ICONQUESTION) == IDYES) {
                InitFileVars();                //バッチファイル作成用変数の初期化
                InitTreeview();                //ツリービューの初期化
            }
            else {
                return FALSE;
            }
        }
    }
(解説:ファイル名の拡張子から既にファイルが設定されているか否かを確認し、差し替えの可否をダイアログで確認後、差し替える場合初期化を行います。)
    //ファイル名からCPPファイルまたはRCファイルを記録(LIBファイルは後で一括実施)
    //また差し替えた場合を考慮 して↑で一緒にやらない
    //InitTreeView()をした場合を考え、tvParentを再設定
    if(strstr(g_ptFile, ".cpp") || strstr(g_ptFile, ".CPP")) {
        tvParent = g_hTreeCPP;
        g_CPPFile = g_ptFile;
    }
    else if(strstr(g_ptFile, ".rc") || strstr(g_ptFile, ".RC")) {
        tvParent = g_hTreeRC;
        g_RCFile = g_ptFile;
    }
    else if(strstr(g_ptFile, ".lib") || strstr(g_ptFile, ".LIB")) {
        tvParent = g_hTreeLIB;
    }
(解説:ファイル区分をstrstr関数で確認し、親玉のツリービュ―アイテムを特定に、CPPファイルとRCファイルのみファイルパス、名を専用変数に保存します。)
    //ファイル名をツリービューに表示
    tv.InsertItem(g_ptFile, TVI_LAST, tvParent, 4, 4);
    tv.Expand(tvParent, TVE_EXPAND);
(解説:後はツリービュ―に表示するだけです。なお、このExpand処理をしても展開しないので、気にしないでください。仕様のようです。)
    return TRUE;
}

bool BATCHGOODDLG::OnClosed() {

    //選択されているアイテムの文字列を取得
    char Buff[MAX_PATH];
    HTREEITEM hSelected = tv.GetSelection();
    if(hSelected)
        tv.GetItem(hSelected, Buff, MAX_PATH);
    else {
        MessageBox(m_hWnd, "ツリービュ―のファイルが選択されていません。\r\n閉じるファイルを選択してください。", "エラー", MB_OK | MB_ICONSTOP);
        return FALSE;
    }
    CSTR ItemText = Buff;
    if(!lstrcmp(Buff, "CPPファイル") || !lstrcmp(Buff, "RCファイル") || !lstrcmp(Buff, "LIBファイル")) {
        MessageBox(m_hWnd, "フォールダーは削除できません", "警  告", MB_OK | MB_ICONSTOP);
        return FALSE;
    }
    else {
        tv.DeleteItem(hSelected);        //選択ツリービューアイテム(ファイル)を削除
        if(strstr(Buff, ".cpp") || strstr(Buff, ".CPP"))    //そのファイル区分に応じ
            g_CPPFile = "";                //g_CPPFileを初期化
        else if(strstr(Buff, ".rc") || strstr(Buff, ".RC"))
            g_RCFile = "";                //g_RCFileを初期化
        //LIBファイルは後で一括実施
    }
    return TRUE;
}
(解説:ファイルが読み込まれていて、更に読み込むと自動的に差し替え、初期化を行いますが、自分で一旦選んだファイルを外す場合の処理です。)

bool BATCHGOODDLG::OnDetail() {

    //IDD_DETAILダイアログを呼び出し、オプション詳細を入力
    detaildlg.DoModal(m_hWnd, "IDD_DETAIL", detaildlgProc);
(解説:「詳細」ボタンを押すと、詳細ダイアログを呼び出します。)
    return TRUE;
}

bool BATCHGOODDLG::OnBCCOption(WPARAM wParam, LPARAM lParam) {

    //内容が更新されたらg_Coptも更新する
    char buff[MAX_PATH];
    if(HIWORD(wParam) == EN_UPDATE) {
        SendItemMsg(IDC_BCC32OPTION, WM_GETTEXT, MAX_PATH, (LPARAM)buff);
        g_Copt = buff;
        return TRUE;
    }
    else
        return FALSE;
}

bool BATCHGOODDLG::OnBRCOption(WPARAM wParam, LPARAM lParam) {

    //内容が更新されたらg_Roptも更新する
    char buff[MAX_PATH];
    if(HIWORD(wParam) == EN_UPDATE) {
        SendItemMsg(IDC_BRC32OPTION, WM_GETTEXT, MAX_PATH, (LPARAM)buff);
        g_Ropt = buff;
        return TRUE;
    }
    else
        return FALSE;
}
(解説:↑の二つの関数は、BCC32toBRC32のオプションを入力するエディットボックスが変更された場合にその内容を専用CSTR変数に記録するものです。)

//////////////////
// BATCHGOODDLG
//メニューの関数
//////////////////
bool BATCHGOODDLG::On_Open() {

    //CPPファイル、RCファイルまたはLIBファイルを選択し、g_ByFileフラグを立ててOnOpen関数を呼ぶ
    g_ptFile = cmndlg.GetFileName(m_hWnd, "cppファイル、rcファイルまたはLIBファイル\0*.cpp;*.rc;*.lib\0\0", TRUE);
    if(g_ptFile) {
        g_ByFile = TRUE;
        OnOpen();
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "ファイルが選択されませんでした", "エラー", MB_OK | MB_ICONSTOP);
        return FALSE;
    }
}
(解説:メニューの「ファイルを開く」の処理です。アンダーバーを入れているのはOnOpen関数と区別するためにです。fileパス、名を取得して、g_ByFileフラグを立ててOnOpen関数を呼びます。)

bool BATCHGOODDLG::OnExit() {

    SendMsg(WM_CLOSE, 0, 0);
    return TRUE;
}
(解説:定番処理です。)

bool BATCHGOODDLG::OnMakebatch() {

(解説:以降が本プログラムのメインイベントです。)
    //g_CPPFileの存在確認
    if(!*g_CPPFile.ToChar()) {
        MessageBox(m_hWnd, "コンパイルするCPPファイがありません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
(解説:こういうエラー処理はお早めに。)
    //処理用変数
    char Buff[MAX_PATH];                //文字列取得バッファ
    char Ext[5] = {'.', 0, 0, 0, 0};    //拡張子保存用配列
    CSTR work;                            //作業用CSTR変数
    //g_Coptの作成
    SendItemMsg(IDC_BCC32OPTION, WM_GETTEXT, MAX_PATH, (LPARAM)Buff);
    g_Copt = Buff;
    SendItemMsg(IDC_INCLPATH, WM_GETTEXT, MAX_PATH, (LPARAM)Buff);
    if(*Buff) {    //IDC_INCLPATHに文字があれば-Iオプションを追加
        g_Copt = g_Copt + " -I\"";
        g_Copt = g_Copt + Buff;
        g_Copt = g_Copt + "\"";
    }
(解説:まずg_Coptにダイアログのbcc32オプションの内容とインクルードファイルの設定があれば、その命令を追加します。)
    //g_Roptの作成
    SendItemMsg(IDC_BRC32OPTION, WM_GETTEXT, MAX_PATH, (LPARAM)Buff);
    g_Ropt = Buff;    //Buffが空なら、g_Roptも初期化される
(解説:BRC32のオプション処理です。バッチ処理の場合、通常オプションは不要です。)
    //ターゲットディレクトリーの作成
    Arg = g_CPPFile.ToChar();        //CPPファイルパス、名
    lstrcpy(Ext + 1, Arg.Ext());    //CPPファイルの拡張子を保存(cppまたはCPP)
    g_TargetDir = Arg.Path();        //パスだけ取得
    if(g_OutDir)                    //ReleaseまたはDebugを付加
        g_TargetDir = g_TargetDir + "\\Release";
    else
        g_TargetDir = g_TargetDir + "\\Debug";
    //ターゲットディレクトリーの作成
    CreateDirectory(g_TargetDir.ToChar(), NULL);
(解説:CPPファイルのパスに選ばれた"Debug"または"Release"のフォールダーをくっつけ、フォールダー(ディレクトリー)を作成します。この時cppファイルの拡張子も'.'付きでExtに記録します。)
    //バッチファイル名を作成
    g_BatchFile = g_TargetDir + "\\" + Arg.FileName();     //未だCPPファイル名
    lstrcpy(strstr(g_BatchFile.ToChar(), Ext), ".bat");    //".cpp"を".bat"と入替え
(解説:ターゲットディレクトリーに先ずCPPファイルのファイル名をくっつけ、その後Extを探して".bat"に差し替えます。Extを使うのはより正確な拡張子名を取るためです。)
    //実行可能ファイル名を作成
    g_ExeFile = g_BatchFile;
    //DLLファイルの場合の処理
    g_DLL = strstr(g_Copt.ToChar(), "-tD");    //g_DLLフラグの設定
    if(g_DLL)    //DLLファイルなら".bat"を".dll"と入替え
        lstrcpy(strstr(g_ExeFile.ToChar(), ".bat"), ".dll");
    else        //EXEファイルなら".bat"を".exe"と入替え
        lstrcpy(strstr(g_ExeFile.ToChar(), ".bat"), ".exe");
(解説:今度は作成したバッチファイルパス、名を利用して実行ファイル名を作ります。DLLの場合は専用フラグを立てて拡張子を"exe"でなく"dll"にします。)
    //ライブラリーファイル一覧を括弧("")付で作成
    HTREEITEM hChild = tv.GetChild(g_hTreeLIB);    //LIBファイルの最初の子アイテム
    tv.GetItem(hChild, Buff, MAX_PATH);
    if(*Buff) {
        g_LIBFile = "\"";
        g_LIBFile = g_LIBFile + Buff;
        g_LIBFile = g_LIBFile + "\" ";
        while((hChild = tv.GetNext(hChild))) {
            tv.GetItem(hChild, Buff, MAX_PATH);
            g_LIBFile = g_LIBFile + "\"";
            g_LIBFile = g_LIBFile + Buff;
            g_LIBFile = g_LIBFile + "\" ";
        }
    }
(解説:↑で「後で一括」と書かれていた処理です。全てのLIBファイルを括弧を付け、空白区切りで並べ、g_LIBFileに記録します。)
    //バッチファイル作成用変数をタイトルで初期化
    CSTR Bat(TITLE);
(解説:ここでUser.hで設定したTITLEが登場です。)
    //chdirでターゲットディレクトリーに移動
    Bat =Bat + "cd \"" + g_TargetDir;
(解説:ここからバッチコマンドをつなげてゆきます。)
    Bat =Bat + "\"\r\n";
    //bcc32cコマンド
    Bat = Bat + "\"" + g_Bcc32 + "\" \"";
    Bat = Bat + g_CPPFile + "\" ";
    //Libファイルがあれば付加
    if(*g_LIBFile.ToChar())
        Bat = Bat + g_LIBFile;
    Bat = Bat + g_Copt + "\r\n";
    //g_Deltds == TRUEなら、tdsファイルを削除する
    if(g_Deltds) {
        work = g_BatchFile;
        lstrcpy(strstr(work.ToChar(), ".bat"), ".tds");    //".bat"を".tds"と入替え
        Bat = Bat + "del \"" + work + "\"\r\n";            //tdsファイルを削除する
    }
(解説:tds削除フラグが立っている場合の処理も、作業用CSTRクラス変数workを使って拡張子の変更を行っています。)
    //brc32cコマンド(g_RCFileが空の場合スキップする)
    if(*g_RCFile.ToChar()) {
        Bat = Bat + "\"" + g_Brc32 + "\" \"";
        Bat = Bat + g_RCFile + "\" \"";                    //"brc32 <rc file> <exe file> opt"
        Bat = Bat + g_ExeFile + "\"";                    //というコンパイルコマンドの作成
        if(*g_Ropt.ToChar())                            //但し、optはある場合のみ
            Bat = Bat + " " + g_Ropt;
(解説:この条件文がないと、単に「""」が書き込まれます。)
        Bat = Bat + "\r\n";
        //g_Delres == TRUEなら、resファイルを削除する
        if(g_Delres) {
            work = g_RCFile + " ";                        //workにrcファイルを入れ1文字追加
            Arg = g_RCFile.ToChar();                    //Argにrcファイルパス、名を代入
            lstrcpy(Ext + 1, Arg.Ext());                //RCファイルの拡張子を保存(rcまたはRC)
            lstrcpy(strstr(work.ToChar(), Ext), ".res");//".rc "を".res"と入替え
            Bat = Bat + "del \"" + work + "\"\r\n";        //resファイルを削除する
        }
(解説:resファイルはtdsファイルと異なり、ターゲットディレクトリーではなく、ソースと同じディレクトリーにあるので、個別にtds同様の処理をします。)
    }
    if(g_DLL) {        //DLLファイルならLIBファイルを作る
        work = g_Bcc32;
        lstrcpy(strstr(work.ToChar(), "bcc32c.exe"), "implib.exe");    //共に6文字+".exe"
        g_LIBFile = g_ExeFile;
        lstrcpy(strstr(g_LIBFile.ToChar(), ".dll"), ".lib");        //".dll "を".lib"と入替え
        Bat = Bat + "\"" +  work.ToChar() + "\" \"" + g_LIBFile.ToChar() + "\" \"" + g_ExeFile.ToChar() + "\"\r\n";
        g_LIBFile = "";        //g_LIBFile転用したので再初期化しておく
    }
(解説:さぁ、DLLの場合にLIBファイルを出力する処理です。g_DLLフラグが立って居れば、bcc32cのfileパスを利用してimplibのファイルパス、名を作ります。次にDLLのファイルパス、名を使ってLIBfileパス、名を作ります。後はコマンドをバッチファイルに書き込みます。)
    Bat =Bat + "pause\r\n";                                //DOS窓を閉めないように止める
    //バッチファイルを書き出す
    Bat.ToFile(g_BatchFile.ToChar());                    //ターゲットディレクトリに出力
    //エディットコントロール(IDC_OUTPUT)にBatを出力する
    SendItemMsg(IDC_OUTPUT, WM_SETTEXT, 0, (LPARAM)Bat.ToChar());
     //バッチファイル名をステータスバーに表示する
    SBar.SetText(1, g_BatchFile.ToChar());
    SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)g_BatchFile.ToChar());    //ToolTipをつける
(解説:この段階ではバッチファイルを作成して、ターゲットディレクトリーに書き込み、エディットボックスに表示するだけです。)
    return TRUE;
}

bool BATCHGOODDLG::OnBatchedit() {

    if(!*g_BatchFile.ToChar()) {
        MessageBox(m_hWnd, "未だバッチファイルが作られていません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    CSTR cmd("\"");
    cmd = cmd + g_Edit + "\" \"" + g_BatchFile +"\"";
    WinExec(cmd.ToChar(), SW_SHOWNORMAL);
    return TRUE;
(解説:作成したバッチファイルを登録エディターで編集する処理です。)
}

bool BATCHGOODDLG::OnRun() {

    if(!OnMakebatch())                //バッチファイルを作成し、失敗すれば
        return FALSE;                //FALSEを返す
(解説:「バッチファイルの作成」を実施しないでいきなり「バッチファイルの実行」を行う人がいるので、悩んだ結果「実行」の前に「作成」も行うようにしました。)
    DeleteFile(g_ExeFile.ToChar());    //既存のexeファイル一旦は削除する
(解説:バッチファイルが失敗した場合、実行可能な昔のexeが残っていると誤解を生むので潔く昔のexeファイルは削除しましょう。)
    CSTR cmd(BAT);
    cmd = cmd + "\"" + g_BatchFile +"\"";
    //コンパイル実施
    WinExec(cmd.ToChar(), SW_SHOWNORMAL);
    //(DLLでない)exeファイル実行には完成までのタイムラグがあり、時間稼ぎする
    if(!g_DLL && MessageBox(m_hWnd, "exeファイルを実行しますか?", 
                            g_ExeFile.ToChar(),
                            MB_YESNO | MB_ICONQUESTION) == IDYES)
        WinExec(g_ExeFile.ToChar(), SW_SHOWNORMAL);
(解説:↑のダイアログ無しで作成したexeファイルを実行しようとすると「exeファイルがありません」エラーになります。exeファイルを作成する前にこの命令が先に実行されてしまうからです。その意味のタイムラグ対応です。)
    return TRUE;
}

bool BATCHGOODDLG::OnSetting() {

    setupdlg.DoModal(m_hWnd, "IDD_SETUP", setupdlgProc);
(解説:単に「設定」ダイアログを呼び出すだけです。)
    return TRUE;
}

bool BATCHGOODDLG::OnHelp() {

    CSTR cmd = "hh \"";
    cmd = cmd + g_HelpFile + "\"";
    WinExec(cmd.ToChar(), SW_SHOWNORMAL);
(解説:HTMLHelpがBatchGood.chmファイルを実行します。)
    return TRUE;
}

bool BATCHGOODDLG::OnOption() {

    helpdlg.DoModal(m_hWnd, "IDD_HELP", helpdlgProc);
(解説:BatchGoodReadMe.txtファイルの表示用ダイアログを開きます。)
    return TRUE;
}

bool BATCHGOODDLG::OnVersion() {

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

ダイアログの解説まで書いたのですが、またまた「もう一杯!」とブログエディターに叱られたので、今日はここまで。