前回のCommand.rc、ResCommand.hとTooltip.hについて掲載しただけでしたので、Command.rcについて補足します。

 

メインダイアログ (IDD_MAIN)は小さなダイアログフレームのダイアログにプッシュボタンが6つついただけなので特にコメントはありません。しかし、「コマンドの実行」ボタンで開くダイアログ (IDD_EXE)は仕様変更を経て今の形になりました。元々は「コマンドの実行」ボタンを押すと「ファイルを開く」ダイアログが開き実行ファイルを選択し、その後このダイアログ(当時はIDD_ARG)を開いて引数を設定し、引数が決定するとこのダイアログが閉じられてファイルが実行される、というものでした。しかし、実際に使ってみるとなかなか思うような動作はせず、毎回「ボタンを押して実行ファイルを選びなおし、引数を入力し、実行する」というシーケンスが煩わしくなり、「このダイアログで外部コマンド(実行ファイル)の納得いくまで繰り返して実行する」という仕様に変更しました。また、実行後の外部コマンド(実行ファイル)の出力内容をエディットボックスに流すのですが、ダイアログ枠だと見難いと感じ、サイズ変更枠( WS_THICKFRAME)にして、サイズに合わせて(BCCMakerやEZImageのように)コントロールが移動する形にしました。またこれに合わせて最小サイズも導入しました。

 

【Command.cpp】

//////////////////////////////////////////
// Command.cpp
//Copyright (c) 11/15/2021 by BCCSkelton
//////////////////////////////////////////
#include    "Command.h"
#include    "CommandProc.h"

////////////////
// WinMain関数
////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow) {

    //モードレスダイアログを作成Create(hParent, DlgName, DlgProc);
    if(!Command.Create(NULL, hInstance, "IDD_MAIN", ModelessProc))
        return 0L;

    //メッセージループに入る
    return Command.Loop();
}
二重起動禁止のオードを外したので大分すっきりしています。IDD_MAINはモードレスダイアログにしています。

 

【Command.h】

//////////////////////////////////////////
// Command.h
// Copyright (c) 11/15/2021 by BCCSkelton
//////////////////////////////////////////
//BCCSkeltonのヘッダー-これに必要なヘッダーが入っている
#include    "BCCSkelton.h"
//リソースIDのヘッダー
#include    "ResCommand.h"
//ツールヒントコントロールヘッダー(解説:ここに入れています。)
#include    "ToolTip.h"

/////////////////////////////////////////////////////////////////////
//CMyWndクラスをCDLGクラスから派生させ、メッセージ用の関数を宣言する
/////////////////////////////////////////////////////////////////////
class CMyWnd : public CDLG
{
public:    //以下はコールバック関数マクロと関連している
    //2重起動防止用のMutex用ID名称
    CMyWnd(char* UName) : CDLG(UName) {}
    //メンバー変数
    char m_CPath[MAX_PATH];
    CSTR m_Cmd;
    CSTR m_Log;

(解説:順にカレントパス記録用文字列メンバー変数、外部コマンド実行に関わる命令一式用CSTRメンバー変数、終了時にログを記録する為のCSTRメンバー変数です。)
    //メニュー項目、ダイアログコントロール関連
    bool OnCd();
    bool OnExe();
    bool OnDir();
    bool OnCmd();
    bool OnPs();
    bool OnIdok();
    bool OnCancel();
    //ウィンドウメッセージ関連
    bool OnInit(WPARAM, LPARAM);
    bool OnClose(WPARAM, LPARAM);
    bool OnDestroy(WPARAM, LPARAM);
};

////////////////////////////////////////////////////////////////////////
//派生させたCMyWndクラスのインスタンスとコールバック関数(マクロ)の作成
//主ウィンドウはダイアログと違い、コールバック関数は一つしか作れない
////////////////////////////////////////////////////////////////////////
CMyWnd Command("Command");    //ウィンドウクラスインスタンスの生成

BEGIN_MODELESSDLGMSG(ModelessProc, Command)    //コールバック関数名は主ウィンドウの場合ModelessProcにしている
    //メニュー項目、ダイアログコントロール関連
    ON_COMMAND(Command, IDC_CD, OnCd())
    ON_COMMAND(Command, IDC_DIR, OnDir())
    ON_COMMAND(Command, IDC_EXE, OnExe())
    ON_COMMAND(Command, IDC_CMD, OnCmd())
    ON_COMMAND(Command, IDC_PS, OnPs())
    ON_COMMAND(Command, IDOK, OnIdok())
    ON_COMMAND(Command, IDCANCEL, OnCancel())
    //ウィンドウメッセージ関連
    ON_DESTROY(Command)
END_DLGMSG

////////////////////////
//コモンダイアログの作成
////////////////////////
CMNDLG cmndlg;

///////////////////////////////////////////
// CDLGクラスからEXEDLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class EXEDLG : public CDLG {
public:
    //メンバー変数(ダイアログの幅と高さ
    int m_Width;        //クライアントエリアの幅記録用
    int m_Height;        //クライアントエリアの高さ記録用
    CSTR m_FileName;    //実行ファイルのパス、名

(解説:サイズ変更に合わせてコントロールを移動する為のクライアントエリアの幅と高さを記録するメンバー変数を付加。また実行ファイルのパス、名を記録するCSTRメンバー変数を付加。)
    //ウィンドウコントロール関連
    bool OnFile1();    //(解説:実行ファイル選択用のボタン「1.ファイル」)
    bool OnFile2();    //(解説:引数用対象ファイル選択用のボタン)
    bool OnFile3();    //(解説:引数用対象ファイル選択用のボタン)
    bool OnFile4();    //(解説:引数用対象ファイル選択用のボタン)
    bool OnDone();    //(解説:エディットボックスにコマンド文字列を格納するボタン「2.引数完了」)
    bool OnIdok();    //(解説:コマンド文字列を実行するボタン「3.実行」)
    bool OnCancel();    //(解説:「コマンドの実行」ダイアログを終了するボタン)
    //ウィンドウメッセージ関連
    bool OnInit(WPARAM, LPARAM);
    bool OnSize(WPARAM, LPARAM);
    bool OnMinMax(WPARAM, LPARAM);    //(解説:最小サイズ設定用)
};

////////////////////////////////////////////////////////////////////////////
// EXEDLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
EXEDLG exedlg;

BEGIN_MODALDLGMSG(exedlgProc, exedlg)    //第1引数がコールバック関数の名前
    ON_COMMAND(exedlg, IDC_FILE1, OnFile1())
    ON_COMMAND(exedlg, IDC_FILE2, OnFile2())
    ON_COMMAND(exedlg, IDC_FILE3, OnFile3())
    ON_COMMAND(exedlg, IDC_FILE4, OnFile4())
    ON_COMMAND(exedlg, IDC_DONE, OnDone())
    ON_COMMAND(exedlg, IDOK, OnIdok())
    ON_COMMAND(exedlg, IDCANCEL, OnCancel())
    ON_SIZE(exedlg)
    ON_(exedlg, WM_GETMINMAXINFO, OnMinMax(wParam, lParam))    //(解説:最小サイズ設定用)
END_DLGMSG

///////////////////////
//ステータスバーの作成
///////////////////////
CSBAR SBar;

///////////////////////
//CCMDEDITクラスの作成
///////////////////////
CCMDEDIT g_CE(32);            //エディットコントロール用に32Kバイトのバッファーを確保

 

最後のCCMDEDIT(コマンドラインを実行するためのエディットボックスコントロールクラス)についてはヘルプファイルとCCMDEDIT.hをご覧ください。最初の想定と仕様はこのクラスでコントロールを作成することでした(Create()メンバー関数参照)が、実際にはダイアログコントロールの既成のエディットボックスに被せて使っています。(次回CommandProc.hファイルのOnInit()関数の「//コマンドエディットコントロールの初期化」で解説します。

 

いつもながらのリソース関係ファイルです。またメインダイアログはヘルプの代わりにツールヒントを出しますので、前にやった外部関数を再掲します。

 

【Command.rc】

//-----------------------------------------
//             BCCForm Ver 2.41
//    An Easy Resource Editor for BCC
//  Copyright (c) February 2002 by ysama
//-----------------------------------------
#include    "ResCommand.h"
#define        DS_SETFOREGROUND    0x200
#define        SBT_TOOLTIPS        0x0800    //スティタスバー用スタイル(解説:これを忘れないでね。)

//----------------------------------
// ダイアログ (IDD_MAIN)
//----------------------------------
IDD_MAIN DIALOG DISCARDABLE 0, 0, 123, 135
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_DLGFRAME| WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT | DS_CENTER
CAPTION "Command"
FONT 8, "MS 明朝"
{
 CONTROL "ディレクトリ変更", IDC_CD, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 9, 6, 105, 18
 CONTROL "Explorer", IDC_DIR, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 27, 105, 18
 CONTROL "コマンドの実行", IDC_EXE, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 48, 105, 18
 CONTROL "コマンドプロンプト", IDC_CMD, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 69, 105, 18
 CONTROL "パワーシェル", IDC_PS, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 90, 105, 18
 CONTROL "終了", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 111, 105, 18
}

//----------------------------------
// ダイアログ (IDD_EXE)
//----------------------------------
IDD_EXE DIALOG DISCARDABLE 0, 0, 270, 270
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT | DS_CENTER
CAPTION "コマンドの実行"
FONT 8, "MS 明朝"
{
 CONTROL "実行ファイル(ステータスバー表示)の選択", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE, 9, 6, 200, 12
 CONTROL "1.ファイル", IDC_FILE1, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 201, 3, 60, 12
 CONTROL "第1引数", IDC_LABEL2, "STATIC", WS_CHILD | WS_VISIBLE, 9, 24, 36, 12
 CONTROL "ファイル", IDC_FILE2, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 51, 21, 45, 12
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT, 9, 36, 252, 12, WS_EX_CLIENTEDGE
 CONTROL "第2引数", IDC_LABEL3, "STATIC", WS_CHILD | WS_VISIBLE, 9, 54, 36, 12
 CONTROL "ファイル", IDC_FILE3, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 51, 51, 45, 12
 CONTROL "", IDC_EDIT2, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP  | ES_AUTOHSCROLL | ES_LEFT, 9, 66, 252, 12, WS_EX_CLIENTEDGE
 CONTROL "第3引数", IDC_LABEL4, "STATIC", WS_CHILD | WS_VISIBLE, 9, 84, 36, 12
 CONTROL "ファイル", IDC_FILE4, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 51, 81, 45, 12
 CONTROL "", IDC_EDIT3, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP  | ES_AUTOHSCROLL | ES_LEFT, 9, 96, 252, 12, WS_EX_CLIENTEDGE
 CONTROL "2.引数完了", IDC_DONE, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 201, 111, 60, 15
 CONTROL "", IDC_EXEDIT, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL | ES_LEFT, 9, 130, 252, 106, WS_EX_CLIENTEDGE
 CONTROL "3.実行", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 201, 240, 60, 15
 CONTROL "終了", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 9, 240, 45, 15
 CONTROL "", IDC_STATUSBAR, "MSCTLS_STATUSBAR32", WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | CCS_TOP | CCS_NOMOVEY, 0, 0, 0, 0
}

 //--------------------------
// イメージ(IDI_ICON)
//--------------------------
IDI_ICON    ICON    DISCARDABLE    "C:\Users\(パス)\Command\Icon.ico"

 

【ResCommand.h】

//-----------------------------------------
//             BCCForm Ver 2.41
//   Header File for Resource Script File
//   Copyright (c) February 2002 by ysama
//-----------------------------------------
//---------------------
//  ダイアログリソース
//---------------------
// ダイアログ IDD_MAIN
#define    IDC_CD            100
#define    IDC_DIR        101
#define    IDC_EXE        102
#define    IDC_CMD        103
#define    IDC_PS            104
// ダイアログ IDD_EXE
#define    IDC_LABEL1        200
#define    IDC_LABEL2        201
#define    IDC_LABEL3        202
#define    IDC_LABEL4        203
#define    IDC_FILE1        204
#define    IDC_FILE2        205
#define    IDC_FILE3        206
#define    IDC_FILE4        207
#define    IDC_DONE        208
#define    IDC_EDIT1        209
#define    IDC_EDIT2        210
#define    IDC_EDIT3        211
#define    IDC_EXEDIT        212
#define    IDC_STATUSBAR        213

//---------------------
//  メニューリソース
//---------------------

//---------------------
//  イメージリソース
//---------------------
#define    IDI_ICON        300

//---------------------
//  ストリングテーブル
//---------------------

//--------------------
//  アクセラレーター
//--------------------

//------------------
//  ヴァージョン情報
//------------------
 

【ToolTip.h】

//////////////////////////////////////////////////////////////////////////
//ツールヒントコントロール(ツールチップ)付与関数
//【注意】
//アプリケーショの冒頭でInitcommoncontrols()関数を実行してください
//【概要】
//ツールチップをダイアログのコントロールに付ける
//【引数】
//hWnd        ダイアログ(ウィンドウでもよいが)のウィンドウハンドル
//CtrlID    コントロールID
//pszText    表示文字列
//【戻り値】
//ツールチップのウィンドウハンドル(作りっぱなしでよいみたいです)
//【参考】
//https://docs.microsoft.com/ja-jp/windows/win32/controls/tooltip-controls
//なお、TTF_IDISHWNDを外した、IDだけでは表示されなかった
//////////////////////////////////////////////////////////////////////////
HWND SetToolTip(HWND hWnd, int CtrlID, PTSTR pszText) {

    if(!CtrlID || !hWnd || !pszText) {
        return (HWND)NULL;
    }
    //対象コントロールのハンドルを取得する
    HWND hCtrl = GetDlgItem(hWnd, CtrlID);
    //文字列リソースのある親ウィンドウ(ダイアログ)のインスタンス
    HINSTANCE hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE);
    //ツールチップの作成
    HWND hTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
                    WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    hWnd, NULL, 
                    hInst, NULL);
    if(!hCtrl || !hTip) {
        return (HWND)NULL;
    }
    //ツールチップをコントロールに関連付ける
    TOOLINFO toolInfo = { 0 };    //ゼロクリアー
    toolInfo.cbSize = sizeof(toolInfo);
    toolInfo.hwnd = hWnd;
    toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    toolInfo.uId = (UINT_PTR)hCtrl;
    toolInfo.hinst = hInst;
    toolInfo.lpszText = pszText;
    SendMessage(hTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
    SetWindowPos(hTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

    return hTip;
}
 

さて、BCCForm & BCCSkeltonのBCC102(Embarcadero C++ 7.3:bcc32c.exe)対応について報告いたしましたが、この対応を大なうに当り、一つ簡単なツールを作りましたので紹介いたします。

 

【このツールの必要性】

ウィンドウズアプリケーションだとExplorerからダブルクリックしたり、データファイルをドラッグアンドドロップしたりして簡単に起動できますが、ECC102のようなCUIツールだと、Explorerからダブルクリックしても瞬時にDOS窓が開いて閉まり、全く訳が分かりません。また、CUIツールは起動する際の引数で動作をコントロールするものが多く、多様な引数オプションに対応しなければなりません。従って基本的にcmd.exeでDOS窓を開けて昔のようにMS-DOSコマンドをキーボード入力するのですが、かなり離れたフォールダー(MS-DOSではディレクトリーといいましたね)に移動する場合は大変です。直接ファイルパスを入力するのが近道ですが、パスにスペース等が入っているいる場合などは""で囲む必要があるのでタイプミスするとやり直しになります。これが嫌で私は一つづつ"cd .."で上って一つずつ"cd (directory)"で降りてゆくのですが、これがまたキー入力量が半端ないです。また(cmd.exeは前の入力内容をコピーしたりできますが)うろ覚えで、コマンド入力やオプションを間違えたりすると、またまたやり直しになります。

と、いうことで、「私的に使いやすいツールを作ろう!」ということで始めました。

 

【仕様】

基本的なDOSコマンドをダイアログツールで、最小限の手間で行えるようにします。具体的には、

(1)"cd"またはchdir (change directory)"コマンドはフォールダー選択ダイアログで離れていても一発選択可能とし、基本的にパスは""で囲みます。(これは"cd (ファイルパス)"として記録に残したいです。)

(2)"dir"コマンドは見やすさからExplorerで見るようにしましょう。(これはログを取るほどではないですね。)

(3)外部コマンド(実行型のcomファイルやexeファイルを昔はこう呼んでましたね)は、

  ①まず「ファイルを開くダイアログで絶対ファイルパスを""で囲んで取得

  ②オプションは一挙に書いてもよいのですが、オプション+ファイルパスのようなものがあるので、3つに分けて入力可能とします。

   (例: -I:"C:\Borland\C++ Builder\bin\bcc32c.exe")

  ③オプションや処理の対象となるファイルのファイルパス、名も""で囲むようにします。

  ④(最初は入力したらすぐに実行してダイアログを消していたのですが考えを変えて)コマンドラインに打ち込む外部コマンドや対象ファイルパス、オプションは一旦エディットボックスに出力して最終の手編集を可能とし、OKであれば実行できるとともに、実行ファイルからの出力はそのエディットボックスにフィードバックできるようにする。

  ⑤気が済むまでファイル実行を繰り返し、満足が行けばダイアログを閉じてメインダイアログに移るようにする。またその際処理ログを記録しておく。

(4)後、気が向いたら(3)のダイアログのコマンドをコピーしておいてDOS窓で実行できるようにcmd.exeも(1)のカレントディレクトリーで起動できるようにする。

(5)更に新しい人はMS-DOSではなくPowerShellを使うと思うので、powershell.exeもカレントディレクトリーで起動できるようにする。

(6)すべて終わったら終了するのですが、その際に上記(1)と(3)はログを取り、よくできたときにはそのログを編集して、バッチファイルに転用できるようにする。

という仕様にしました。なお、いつもは(プロジェクト名).cppにSkeltonWizardが出力してくれる2重起動防止処理は今回外し、複数のCommand.exeが使えるようにしています。


(画像はbcc32c.exeでコンパイルしたもの。)

前回20年間のブランクを経て、サイズ21倍(bcc32 826KB→bcc32c 17,438KB)に発展したClang対応コンパイラーにが見逃してくれるようにBCCSkeltonを修正し、何とか大丈夫そうなところにこぎつけて「これでやっと20年ブランクの肩の荷が下りた気分です」と書きましたが、実はもっと気が重い問題があります。それは20年前はASCIIコード主体で日本語もJISマルチバイトで済んでいたのが、現在はUnicode対応が主流となったことです。勿論今でもbcc32cではcharやchar*と書いてもエラーは出ませんが、(TCHARを事実上の標準としたマイクロソフト帝国の)Visual Studioでは「エラー C2440 '=': 'char *' から 'LPCWSTR' に変換できません。」などというエラーは日常茶飯事です。実はこのUnicode対応が20年前のレガシーソフトにとって最大の障壁となっています。

 

次にUnicodeへの対応(wchar_t(WCHAR))、または以降の為の便宜措置(TCHAR: #ifdef _UNICODE typedef wchar_t TCHAR; #else typedef char TCHAR; #endif)やこれらに関連した種々のマクロ(_T()(または_t())、_TEXT()、、TEXT()、L"")があり、関数もワイド文字対応のものとASCII対応のものとが分かれ、UNICODE条件で使い分けられています。(例: MessageBox→MessageBoxW、MessageBoxA)

 

で、BCCSkelton(BCCSkelton.hをはじめとする34本のヘッダーファイル)をどうするか、という問題になるわけですが、そのことを考えていたら、このUnicodeまたはワイド文字対応のわかりやすい解説(メモ帳: TCHAR型)と方向性(TCHAR はもう使うな)についての記事がありました。BCCSkeltonをワイド文字対応とするなら、

(1)charはTCHARへ、

(2)文字列はL"(文字列)"へ、

(3)関数は_t関数(...)(例:int WINAPI _tWinMain(...))へ

語変換するツールを作って、34本のヘッダーファイルにかけ、最後に手作業で仕上げることになるかと思いますが、もはやASCIIコード、C++はおろかPCが時代遅れとなっている現在、膨大な労力をかけて修正するに値するソフトウェアではない、また向後数年の間しか使うことができない前期高齢者の私にはできない、という結論を得ました。

 

従って、新しいウィンドウズの機能を実現する為にbcc32c対応は必然でしたが、Unicode(ワイド文字)対応は未だOSが助けてくれることもあり、行わない、ということにします。利用者の方のご利益を考えこの点を明確にしておきます。

 

昨日の今日で恐縮ですが、唯一コンパイル不可であったFileHandlerのビルドが成功しましたので報告いたします。

 

元々FileHandlerはFindFirstFile()、FindNextFile()、FindClose()を使ったツリービュ―にディスクの内容を表示するもの(現在はサンプルのTradFilerとして公開)でしたが、webサイトで見たshell serviceを利用してExplorerっぽくしたものです。この開発にあたり、元々のVisual Studioベースのものは、

#define        WM_SHELLCHANGE    WM_APP
#include <shlobj.h>
#include <shlwapi.h>
#pragma comment (lib, "shell32.lib")

となっていたのですが、Embargadero C++ではうまくコンパイルできず、試行錯誤のうちに、

#define        WM_SHELLCHANGE    WM_APP
#include    <Winapi.ShlObj.hpp>
#include    <Winapi.ShLwApi.hpp>
#pragma comment (lib, "shlwapi.lib")

でコンパイルできたのでこのようにした、という経緯がありました。

 

今回bcc32cでコンパイルした際に発出したエラーが、

Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Fatal: Unable to open file 'RTLE.LIB'
bcc32c.exe: error: linker command failed with exit code 2 (use -Xdriver -v to see invocation)

であったことから、「これはどうもC++ Builder用のライブラリーじゃないか?」ということで、もう一度bcc102\lib\release\psdkの内容を調べ、shell service関係のlib(特にshlwapi.lib)を確認して、オリジナル(上記↑のベースのもの)に戻してコンパイルを行なってみました。

その結果、リンカ―の「"RTLE.lib"ファイルが開けない」というエラーはなくなりましたが、今度は、

Turbo Incremental Link 6.90 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Error: Unresolved external 'ILFindLastID' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'ILCombine' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'SHBindToParent' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'SHChangeNotifyDeregister' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'SHChangeNotification_Lock' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'ILIsParent' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'ILFree' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'ILIsEqual' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'ILClone' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'SHChangeNotification_Unlock' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unresolved external 'SHChangeNotifyRegister' referenced from C:\USERS\YSAMA\APPDATA\LOCAL\TEMP\FILEHANDLER-557043.O
Error: Unable to perform link

という「DLLに関数が無いよ」という大量のエラーの合唱が発生しました。

 

しかし、これは逆にどこかのshell関係のライブラリーにこれらの関数のヘッダー情報がある、という確信となり、普遍的な名前でサイズの大きめのshell32.libに当りを付けて#pragma commentでshlwapi.libと共に明示的にリンク先指定を行いました。

#define        WM_SHELLCHANGE    WM_APP
#include <shlobj.h>
#include <shlwapi.h>
#pragma comment (lib, "shell32.lib")    //これを追加した
#pragma comment (lib, "shlwapi.lib")

その結果(まぁ、試行錯誤だけなんですけど)、目出度くきれいにビルドされ、正常稼働させることができました。

 

これでやっとbcc32c.exeによるコンパイリングの本来の目的である新しめのshell サービス(FileHandler)やCOMサービス(TextToSpeech)を使ったプログラム開発ができることが証明され、「打率100%」に満足しています。

 

新しいFileHandlerのファイルも本日差し替えましたので、ダウンロードしていただければ本日からビルド可能になります。これでやっと20年ブランクの肩の荷が下りた気分です。

 

ps. おそらくFileHandlerにライブラリーのコードを取り込まず、dllで処理しているのか、bcc32によるファイルサイズが186KBだったのに対し、bcc32cによるファイルサイズは116KBと初めて小さくなりました。

さて2回に亘り新しいEmbarcadero C++コンパイラーのコマンドラインツールであるbcc32c.exe対応経過を書いてきましたが、現在の結果を報告します。

 

BCCForm and BCCSkeltonパッケージに入った25の公開サンプルプログラムを含む、BCCForm and BCCSkeltonプラットフォームで作成した計34プログラムに対して次の手順でbcc32c.exeによるコンパイルを実施しました。

【手順】

(1)(プロジェクト名)フォールダー(例:"BCCMaker")の中に"bcc32c_ver"というフォールダーを作り、

(2)次のバッチファイルを放り込みます。

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\BCCMaker\BCCMaker.cpp" -tW -w -I"c:\Borland\BCCForm"
"C:\Borland\BCC102\bin\brc32.exe" "C:\Users\(パス)\BCCMaker\BCCMaker.rc" "C:\Users\(パス)\BCCMaker\bcc32c_ver\BCCMaker.exe"
del "C:\Users\(パス)\BCCMaker\BCCMaker.res"
pause

一行目はbcc32cによるコンパイル指令です。オプションはウィンドウプログラム(-tW)、警告を出し(-w)、BCCSkelton.hのあるフォールダーにリンクパスを貼る(-l)、という内容です。

二行目はbrc32によりrcファイルをコンパイルして、exeファイルに突っ込む指示です。

三行目は"C:\Users\(パス)\BCCMaker\BCCMaker.rc"をコンパイルしてできたリソースのオブジェクトファイル(*.res)を削除するという指示です。

(3)コンパイル結果がコンソール出力されますので、これを基に①成功②失敗(修復対応可能)③失敗(原因調査を要するもの)に分けてみます。

 

結果的に2割に相当する7プログラムがエラーを出しましたが、以下3つの公開サンプルプログラムは容易に修復できました。
また未だ不具合が解決していない4プログラムのうち3つは非公開のプログラムで、コンパイルができないのではなく、正常にコンパイルはされましたが、動作が正常ではない状態です。これらは起動時に特殊処理(独自パスワード・暗号化処理)を行っていることから別途分析対応を予定しています。(bcc32(5.5)では正常動作していますので、不自由はしていませんが。汗;)

 

【エラーを出して修復できない公開サンプル】

唯一公開サンプルでコンパイルできなかったのはWindowsシェル(COMのShell Service)を使ったFileHandlerのみでした。その不具合内容はWinapi.ShlObj.hppとWinapi.ShLwApi.hppに関わるランタイムライブラリー('RTLE.LIB')を読み込めない、

Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Fatal: Unable to open file 'RTLE.LIB'
bcc32c.exe: error: linker command failed with exit code 2 (use -Xdriver -v to see invocation)

というものですが、そのようなライブラリーファイルは元々無い為、今後さらに調査予定です。(注)
注:これらhppヘッダーファイルは共にEmbardacero C++コマンドラインツールのBCC102\include\windows\rtlに収容されていますが、対応すると思われるRTLE.LIBファイルはEmbardacero C++(bcc102)のlibフォールダーにもなく、謎となっています。
いずれにしてもFileHandlerはbcc(5.5)ではもともとコンパイルできないので仕方がないかもしれませんね。

【エラーを出したが解決した公開サンプル】
BCFEditor - 以下のエラーメッセージが出たが、これはCSTRクラス変数m_InstNameにToChar()関数をつけ忘れたプログラムミスだった。(解決済)
C:\Users\ysama\Programing\Borland C++\BCFEditor/BCFEditorProc.h:1026:41: error: cannot pass object of non-trivial type
      'CSTR' through variadic function; call will abort at runtime [-Wnon-pod-varargs]
               wsprintf(buff, "\t%s(%s)\n", msg[0], m_InstName);→wsprintf(buff, "\t%s(%s)\n", msg[0], m_InstName.ToChar())
                                                             ^
C:\Users\ysama\Programing\Borland C++\BCFEditor/BCFEditorProc.h:1046:54: error: cannot pass object of non-trivial type
      'CSTR' through variadic function; call will abort at runtime [-Wnon-pod-varargs]
               wsprintf(buff, "\tON_COMMAND(%s, %s, On%s())\n", m_InstName, msg[2], msg[1]);→wsprintf(buff, \tON_COMMAND(%s, %s, On%s())\n", m_InstName.ToChar(), msg[2], msg[1])

SplitterTest - 今までエラーが出てこなかったが、CHSPLITクラスとCVSPLITクラスのコンストラクターに既定値を設定していたために二つに分けて書き直した。(解決済)

TextToSpeech - "35 warnings and 6 errors generated"と表示されたが、6つのエラーのうち4つはCVOICEのメンバー関数Pause()とResume()の型未指定が原因であった。他の2つはデストラクターの「例外指定が一致しない」(抑々throw、catchなどやっていない)というものであるが、Pause()とResume()の型指定を行ったらエラーは消失してコンパイルされた。(解決済)

In file included from C:\Users\ysama\Programing\Borland C++\TextToSpeech\TextToSpeech.cpp:6:
C:\Users\ysama\Programing\Borland C++\TextToSpeech/CVOICE.h:41:2: error: C++ requires a type specifier for all
      declarations
        Pause();                                                                //<U+8AAD><U+307F><U+4E0A>...
        ^~~~~→voidとした。
C:\Users\ysama\Programing\Borland C++\TextToSpeech/CVOICE.h:42:2: error: C++ requires a type specifier for all
      declarations
        Resume();                                                               //<U+8AAD><U+307F><U+4E0A>...
        ^~~~~~→voidとした。
C:\Users\ysama\Programing\Borland C++\TextToSpeech/CVOICE.h:66:9: warning: exception specification in declaration does
      not match previous declaration
CVOICE::~CVOICE() {
        ^→v意味不明。しかし、上記型指定後消失。
C:\Users\ysama\Programing\Borland C++\TextToSpeech/CVOICE.h:32:2: note: previous declaration is here
        ~CVOICE();                                                              //<U+30C7><U+30B9><U+30C8>...
        ^→v意味不明。しかし、上記型指定後消失。
 

今回のClang対応の最も大きな目的は、20年前のbcc32(5.5)ではヘッダーやライブラリーが無くてビルド出来ない、FileHandlerとTextToSpeechがbcc32c(BCC102)でビルド出来ないか、というものであり,TextToSpeechの成功は大きな成果だと持参しています。(打率5割ですが。汗;)

 

ということで、今回のClang対応や25の公開サンプルのテストによる修正内容はアップしているものに反映させましたので、御利用の方はお差し替えください。(但し、TextToSpeechを除くすべてがbcc32(5.5)でコンパイル可能であり、プログラムサイズも6割とコンパクトなので、敢えてbcc32c版をお勧めはしません。唯一TextToSpeechのみ、お勧めです。(どういう訳かプログラムサイズも1KBしか変わりませんし。)

 

なお、今後は今回の対応で使用した「コマンドツール」の解説を考えています。お楽しみに。

 

BCCSkelton(のファイル群)をClang対応のbcc32cでコンパイルして出される警告、エラーに対応した修正版をアップし、HelloWorldプロジェクトが正常にコンパイルされたので大丈夫かと思いましたが、「因みに」と先般やったDumpプロジェクトをコンパイルしたところ、

 

Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:
Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Fatal: Expected an option: C:\Borland\BCC102\lib\win32c\release;
 

というilink32のエラーが出ました。

少なくともbcc32c(Embarcadero C++ 7.30 )によるコンパイリングは通っており、コンパイラーのエラーメッセージ(Embarcadero「コンパイラのエラーと警告(C++)」)ではないので、リンク段階で何らかのオプションを与えなければならなかったようです。

ということで、またwebを漁りますが、余りストライクど真ん中の記事はありません。関連するところでEmbarcaderoの

 

 

があり、次のように書かれています。

ILINK32

BCC32.EXE での ILINK32 の使用

コマンド ラインで .OBJ や .LIB の拡張子を明示してファイル名を入力することで、コマンドライン コンパイラ(BCC32.EXE)を介して ILINK32 にオプションやファイルを渡すことができます。 たとえば、このコマンドは:

BCC32 mainfile.obj sub1.obj mylib.lib

MAINFILE.OBJ、SUB1.OBJ、MYLIB.LIB をリンクし、実行可能ファイル MAINFILE.EXE を生成します。

BCC32.EXE のリンカにオプションを渡すには、-l を使用できます。 たとえば、-ls は -s をリンカに渡します。 また、BCC32C.EXE とも動作します。

 

調べてゆくうちにEmbarcaderoの英文のilinkのエラーサイトを発見。その中に、

 

があり、移動してみると、

 

Expected an option 'identifier'

Go Up to C++ Linker Error and Warning Messages

Fatal error. There’s a problem with your linker command specifications on the command line, linker configuration file, or response file.

 

とあります。

まさに今回の問題ど真ん中なんですが、畢竟何が問題なのかわかりません。ということで一応ilink32.cfgもチェックします。

 

-L C:\Borland\BCC102\lib\win32c\release;
-L C:\Borland\BCC102\lib\win32c\release\psdk;
-L C:\Borland\BCC102\lib\win32c\debug;

 

ん?確か新しいバージョンはフルパスは不要なはず、と思い、オリジナルのダウンロード圧縮ファイルのbinフォールダーを確認するとilink32.cfgは「無い」ですね。その代わりにbcc32c.cfgにリンカ―オプションの新しい記載が入っています。

 

-isystem @\..\include\dinkumware64
-isystem @\..\include\windows\crtl
-isystem @\..\include\windows\sdk
-L@\..\lib\win32c\debug
-L@\..\lib\win32c\release
-L@\..\lib\win32c\release\psdk
-Xdriver -Qunused-arguments

 

為念、現在のbcc32c.cfgをチェックすると旧い表記でまた-Xdriver部分が脱落しています。(どうも仕様当初、bcc32のcfgファイルと入れ替えて使ったようです)これをダウンロードオリジナルと同じように修正してDumpを再コパイルすると、

 

【バッチファイルによるDump.cppファイルのコンパイルエラー出力全文】

C:\Users\(パス)\Dump>"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp" -tW -w -I"c:\Borland\BCCForm" -Xdriver -v
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Embarcadero Technologies Inc. bcc32c version 3.3.1 (36350.30c6854.779bede) (based on LLVM 3.3.1)
Target: i686-pc-win32-omf
Thread model: posix
C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:
 "C:/Borland/BCC102/bin/bcc32c.exe" -cc1 -triple i686-pc-win32-omf -emit-obj -disable-llvm-verifier -tW -main-file-name Dump.cpp -mrelocation-model static -mdisable-fp-elim -fmath-errno -masm-verbose -v -ffunction-sections -nobuiltininc -sys-header-deps -auto-dependency-output -isystem "C:\\Borland\\BCC102\\bin\\..\\include\\windows\\crtl" -isystem "C:\\Borland\\BCC102\\bin\\..\\include\\windows\\rtl" -isystem "C:\\Borland\\BCC102\\bin\\..\\include\\windows\\sdk" -isystem "C:\\Borland\\BCC102\\bin\\..\\include\\dinkumware64" -I "c:\\Borland\\BCCForm" -Wall -std=c++11 -fdeprecated-macro -ferror-limit 50 -fmessage-length 120 -fno-threadsafe-statics -fno-use-cxa-atexit -fborland-extensions -fobjc-runtime=gcc -std=c++11 -fcxx-exceptions -fexceptions -fseh -fdiagnostics-show-option -fcolor-diagnostics -fno-spell-checking -backend-option -vectorize-loops -cxx-abi borland -o C:/Users/ysama/AppData/Local/Temp/Dump-067270.o -x c++ "C:\\Users\\(パス)\\Dump\\Dump.cpp"
clang -cc1 version 3.3.1 based upon LLVM 3.3.1 default target i686-pc-win32
#include "..." search starts here:
#include <...> search starts here:
 c:\Borland\BCCForm
 C:\Borland\BCC102\bin\..\include\windows\crtl
  C:\Borland\BCC102\bin\..\include\windows\sdk
 C:\Borland\BCC102\bin\..\include\dinkumware64
End of search list.
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:26:
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:10:15: warning: 'CCTRL::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
        virtual bool Init(LPCTSTR, HINSTANCE);
                     ^
c:\Borland\BCCForm/.\BCCSkelton\CWND.h:26:15: note: hidden overloaded virtual function 'CWND::Init' declared here:
      different number of parameters (7 vs 2)
        virtual bool Init(LPCTSTR, HINSTANCE,
                     ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:27:
c:\Borland\BCCForm/.\BCCSkelton\CTBAR.h:11:7: warning: 'CTBAR::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Init(HWND, HINSTANCE, int, DWORD);
             ^
c:\Borland\BCCForm/.\BCCSkelton\CWND.h:26:15: note: hidden overloaded virtual function 'CWND::Init' declared here:
      different number of parameters (7 vs 4)
        virtual bool Init(LPCTSTR, HINSTANCE,
                     ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:28:
c:\Borland\BCCForm/.\BCCSkelton\CSBAR.h:11:7: warning: 'CSBAR::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Init(HWND, HINSTANCE, int, DWORD);
             ^
c:\Borland\BCCForm/.\BCCSkelton\CWND.h:26:15: note: hidden overloaded virtual function 'CWND::Init' declared here:
      different number of parameters (7 vs 4)
        virtual bool Init(LPCTSTR, HINSTANCE,
                     ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:29:
c:\Borland\BCCForm/.\BCCSkelton\CRBAR.h:14:7: warning: 'CRBAR::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Init(HWND, HINSTANCE, int, WORD, DWORD);   //<U+30EA><U+30D0><U+30FC><U+306E><U+521D><U+671F>...
             ^
c:\Borland\BCCForm/.\BCCSkelton\CWND.h:26:15: note: hidden overloaded virtual function 'CWND::Init' declared here:
      different number of parameters (7 vs 5)
        virtual bool Init(LPCTSTR, HINSTANCE,
                     ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:30:
c:\Borland\BCCForm/.\BCCSkelton\CREDIT.h:32:7: warning: 'CREDIT::Create' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Create(LPTSTR, DWORD,
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:11:7: note: hidden overloaded virtual function 'CCTRL::Create' declared here:
      type mismatch at 1st parameter ('LPCTSTR' (aka 'const char *') vs 'LPTSTR' (aka 'char *'))
        bool Create(LPCTSTR, DWORD, DWORD, HWND,
             ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:37:
c:\Borland\BCCForm/.\BCCSkelton\CTAB.h:19:7: warning: 'CTAB::Create' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Create(HWND, HINSTANCE, int, int, int, int, int);  //CTAB<U+30B3><U+30F3><U+30C8><U+30ED><U+30FC>...
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:11:7: note: hidden overloaded virtual function 'CCTRL::Create' declared here:
      different number of parameters (9 vs 7)
        bool Create(LPCTSTR, DWORD, DWORD, HWND,
             ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:38:
c:\Borland\BCCForm/.\BCCSkelton\CTREEVIEW.h:20:7: warning: 'CTREEVIEW::Create' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Create(HWND, HINSTANCE, int, int,
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:11:7: note: hidden overloaded virtual function 'CCTRL::Create' declared here:
      different number of parameters (9 vs 7)
        bool Create(LPCTSTR, DWORD, DWORD, HWND,
             ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:39:
c:\Borland\BCCForm/.\BCCSkelton\CLISTVIEW.h:27:7: warning: 'CLISTVIEW::Create' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Create(HWND, HINSTANCE, int, int,
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:11:7: note: hidden overloaded virtual function 'CCTRL::Create' declared here:
      different number of parameters (9 vs 7)
        bool Create(LPCTSTR, DWORD, DWORD, HWND,
             ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:40:
c:\Borland\BCCForm/.\BCCSkelton\CCMDEDIT.h:18:7: warning: 'CCMDEDIT::Create' hides overloaded virtual function
      [-Woverloaded-virtual]
        bool Create(HWND, HINSTANCE, int, int,
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:11:7: note: hidden overloaded virtual function 'CCTRL::Create' declared here:
      different number of parameters (9 vs 7)
        bool Create(LPCTSTR, DWORD, DWORD, HWND,
             ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:41:
c:\Borland\BCCForm/.\BCCSkelton\CHISTORY.h:52:24: warning: unknown escape sequence '\I'
        lstrcpy(m_InitPath, ".\Init.ini");      //ini<U+30D5><U+30A1><U+30A4><U+30EB><U+30D1><U+30B9><U+540D>...
                              ^~
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
In file included from C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:6:
In file included from c:\Borland\BCCForm\BCCSkelton.h:42:
c:\Borland\BCCForm/.\BCCSkelton\CLISTBOX.h:9:7: warning: 'CLISTBOX::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
        void Init(HINSTANCE);
             ^
c:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:10:15: note: hidden overloaded virtual function 'CCTRL::Init' declared here:
      different number of parameters (2 vs 1)
        virtual bool Init(LPCTSTR, HINSTANCE);
                     ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:5:
C:\Users\ysama\Programing\Borland C++\Dump/Dump.h:58:13: warning: conversion from string literal to 'char *' is
      deprecated [-Wdeprecated-writable-strings]
CMyWnd Dump("Dump");    //<U+30A6><U+30A3><U+30F3><U+30C9><U+30A6><U+30AF><U+30E9><U+30B9><U+30A4><U+30F3>...
            ^
In file included from C:\Users\ysama\Programing\Borland C++\Dump\Dump.cpp:7:
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:43:35: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_FILE, "Dump<U+3059><U+308B><U+30D5><U+30A1><U+30A4><U+30EB><U+3092><U+9078>...
                                         ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:45:35: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_EDIT, "Dump<U+30C7><U+30FC><U+30BF><U+8868><U+793A><U+30A8><U+30EA><U+30A2>"))
                                         ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:47:34: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_TOP, "<U+30D5><U+30A1><U+30A4><U+30EB><U+306E><U+5148><U+982D><U+306B>...
                                        ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:49:37: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_BEFORE, "<U+FF11><U+30DA><U+30FC><U+30B8><U+623B><U+308A><U+307E><U+3059>"))
                                           ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:51:35: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_PAGE, "<U+4E2D><U+592E><U+306E><U+30DA><U+30FC><U+30B8><U+3067><U+3059>"))
                                         ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:53:35: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_NEXT, "<U+FF11><U+30DA><U+30FC><U+30B8><U+9032><U+307F><U+307E><U+3059>"))
                                         ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:55:37: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDC_BOTTOM, "<U+30D5><U+30A1><U+30A4><U+30EB><U+306E><U+5F8C><U+5C3E><U+306B>...
                                           ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:57:31: warning: conversion from string literal to 'PTSTR'
      (aka 'char *') is deprecated [-Wdeprecated-writable-strings]
        if(!SetToolTip(m_hWnd, IDOK, "<U+7D42><U+4E86><U+3057><U+307E><U+3059>"))
                                     ^
C:\Users\ysama\Programing\Borland C++\Dump/DumpProc.h:120:43: warning: conversion from string literal to 'char *' is
      deprecated [-Wdeprecated-writable-strings]
                m_FileName = cmndlg.GetFileName(m_hWnd, "<U+3059><U+3079><U+3066><U+306E><U+30D5><U+30A1>...
                                                        ^
21 warnings generated.
 "C:\Borland\BCC102\bin\ilink32.exe" @"C:\Users\ysama\AppData\Local\Temp\Dump-067271.cfg"
Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.

 

ということで、仮想関数のオーバーライドに関わる警告と、char*変数にconst char*を代入する警告が21検出されましたが、無事コンパイルが終了しました。

 

生成されたDump.exe(90KB)はbcc32(5.5)で生成されたもの(69KB)より大きく、またアイコンがシステムアイコンになっています。

因みにダブルクリックすると...動かない!

 

為念プログラムマネージャーで確認しましたが,Dump.exeが動作していることはなくすぐに終了しているようです。

 

何故???

 

まだまだ、bcc32cに関わる旅は続きそうです。

 

【追補】上記の後、BCCMakerでMakeを使って再ビルドを試みた際の出力に別のリンクエラーが出ました。

MAKE Version 5.41  Copyright (c) 1987, 2014 Embarcadero Technologies, Inc.
    "C:\Borland\BCC102\bin\bcc32c.exe" -tW -Od -6 -pc -AT -w -e"C:\Users\ysama\Programing\Borland C++\Dump\Release\Dump.exe" "C:\Users\ysama\Programing\Borland C++\Dump\Release\Dump.obj"
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Warning: option '-6' is not supported in Clang-based compiler.

Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Error: Unresolved external '__InitExceptBlockLDTC' referenced from C:\USERS\YSAMA\PROGRAMING\BORLAND C++\DUMP\RELEASE\DUMP.OBJ
Error: Unresolved external '_vector_delete_ldtc_(void *, unsigned int, unsigned int, unsigned int, void *)' referenced from C:\USERS\YSAMA\PROGRAMING\BORLAND C++\DUMP\RELEASE\DUMP.OBJ
Error: Unresolved external '_vector_new_ldtc_(void *, unsigned int, unsigned int, unsigned int, void *, unsigned int, void *)' referenced from C:\USERS\YSAMA\PROGRAMING\BORLAND C++\DUMP\RELEASE\DUMP.OBJ
Error: Unable to perform link

** error 2 ** deleting "C:\Users\ysama\Programing\Borland C++\Dump\Release\Dump.exe"

 

これに関連する記事も発見し、

Problems When Mismatching Compilers

In 10.3.3 and newer, you will get a linker warning – see below – ahead of any of the following. But if you’re using an older version, before we added the warning diagnosing the issue, you might see only the following errors. These appear when linking the app.

Link-time errors

When an app built with the Clang toolchain is linked against a Classic-built library, you might see the following errors:

[ilink32 Error] Error: Unresolved external '__InitExceptBlockLDTC'
[ilink32 Error] Error: Unresolved external '_ThrowExceptionLDTC'
Error: Unresolved external '_CatchCleanup()'

 

 

どうも旧コンパイラーでビルドされたファイルとClang対応コンパイラーでビルドされたファイルを一緒にリンクしようとすると不具合が発生するようです。

TLDR(注)

Quick summary: mixing object files built with two compilers into one binary is a bad idea. The linker will give you a warning if you do, and tell you what is built with what so you can fix it. This fixes linker problems, and fixes app stability problems!

 

注:TLDRという意味が解らなかったので調べました。

TLDR

【略】

  1. =Too long. Didn't read.
    長過ぎ。読みませんでした。/長文うざい(と言う人のための要約)。◆【場面】ネット上などで「文章が長過ぎる」と文句を言う。転じて「長文を嫌う人のための要約」という意味でも用いられる。◆【用法】主にTLDRと略して用いられる。実際には"Too long; didn't read."の意味でtl;drと書くことが多い。このセミコロンは接続詞省略を表し、意味上「長過ぎ」「だから」「読まなかった」の「だから」に当たる。
【追補の追補-bcc32cによるDumpビルド完了報告】
上記ではコンパイルが成功して、うんともすんとも言わないので逆上して
動かない!
等と書きましたが、問題のヒント(「またアイコンがシステムアイコンになっています」)は分かっており、ダイアログベースのプログラムでリソースが付いていなければ動かないのは当たり前でした。ということで、バッチファイル(Dump.bat)を次のように修正して実行。
"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\Dump\Dump.cpp" -tW -w -I"c:\Borland\BCCForm" -Xdriver -v
"C:\Borland\BCC102\bin\brc32c.exe" "C:\Users\(パス)\Dump\Dump.rc" "C:\Users\ysama\Programing\Borland C++\Dump\Dump.exe"
pause
見事にビルド成功!!!、正常に実行できました。
 
しかし、makファイルでのエラー問題はまだ解決していません。新しい課題とします。
<参考>
bcc32(5.5) 69KB
bcc32c(7.3) 94KB(リソース無しのexeファイルよりも4KB増えたのはリソースが付加された為)
 

前に「【BCC】C++コンパイラーの謎」シリーズをやりましたが、一番困ったのは、Clang対応となったbcc32c(7.3)コンパイラーは旧バージョンのbcc32(5.5)と異なり標準出力にエラーメッセージを出さない(注)ので、BCCDeveloperでもBCCMakerでもエラーの内容が分からなかったことです。

注:bcc32(5.5)はエラーメッセージを標準出力に出しており、BCCDeveloperもBCCMakerもそれを拾っていました。しかしbcc32c(7.3)は標準出力には"** error 1 ** deleting "C:\Users\(パス)\Release\(プロジェクト).obj"のメッセージだけ出し、エラーメッセージを出しません。これはCUI環境ではエラーは必ず"CON:"デバイスに出す、という作法を踏襲していたのだと思われます。

 

この為、bcc32c(7.3)のコンパイルの際にはリダイレクトで標準出力をファイル化(Error.txt)する一方、"pause"コマンドを入れてコンソール出力を監視できるようにバッチファイルを組みました。

【UpDateBCCSkelton.bat】

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\UpDateBCCSkelton.cpp" -tW -w >Error.txt
pause
 

こうしてコンソール画面(cmd.exe)に表示されたエラー出力をマウスで選択し、(右クリックしてもポップアップのコピーメニューが出ませんので)DOS時代の技"Ctrl+C"でコピーし、エディターに"Ctrl+V"で貼り付けることで、やっとこさエラーメッセージを取得することができました。それが【BCC】C++コンパイラーの謎(5)-bcc32cのチェックで書かれていた内容です。後日の為に、皆さんにbcc32c(7.3)のエラーメッセージの取り方として公開します。

 

次にBCCSkeltonの全ファイルを対象として、メッセージボックスを出すだけのUpdateBCCSkeltonプログラム

 

#include    "BCCSkelton.h"
////////////////
// WinMain関数
////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow) {

    MessageBox(NULL, "This is a test.", "Test", MB_OK | MB_ICONINFORMATION);
    return 0L;
}
 

を組んでバッチファイルでエラーメッセージを取得すると、出力された"56 warnings and 6 errors generated"は、ほぼ、

 

(1)クラス定義で「bool メンバー関数」としたが、値を返すのを忘れていた警告→ソリューションとしてvoidに変更するか、return文を付加します。

(2)if~else文のネスティングが深い時に{}を使わないと「次のelseはどっちのifにかかるの?」とコンパイラーが怒る」警告→{}を付加して明確化を図ります。

(3)派生元クラス(①CWND、②CCTR等)の仮想関数を派生先クラス(①CCTRL、②CTREEVIEWやCLISTVIEW等)で書き換える(override)した場合の警告→これは仮想関数のルール通りなのでそのまま残します。

(4)あるクラス(例:CCLASS)の引数付きコンストラクターで既定値を規定する(例:CCLASS(int i = 0, char* str = NULL))ことにより、実質的に引数無しコンストラクター(例:既定値を使いCCLASS()として呼ぶことができる)を実現していたのが、Clangでは違法としてエラーになる→これは引数無しコンストラクターを付加し、既定値を削除しました。

 

にまとめられ、一つずつコツコツと修正をかけて行き、現在は上記(3)の適法警告が11だけになっています。

 

と、いうことで、昨日(2021年11月19日)に新しい(上記修正済)BCCSkeltonを入れてBCCFormandBCCSkelton.zipをアップしましたので差し替えていただければbcc32c(7.3)でBCCSkeltonファイルもコンパイル可能になっています。(因みに本ブログの冒頭のHelloWorldプロジェクトをbcc32(5.5)でコンパイルすると60KBの実行ファイルになりますが、bcc32c(7.3)でコンパイルすると84KBになりました。)

 

やろう、やろう、と思っていましたが、大分遅くなり大変申し訳ございませんでした。また今後もbcc32cを使っていて不具合等が出ればレポートしてゆきます。

 

【CLいghtCycle.h】

///////////////////////////////
//CLIGHTCYCLEクラス定義ファイル
///////////////////////////////
///////////////////////////////【仕様】///////////////////////////////
//【識別色】(m_Color)
//LightCycle車両は識別色を持つ。識別色はユーザーが決定する。
//【方向】(m_Dir)
//LightCycleでは方向を0-7で表す。
//    0                //y--
//  7 ↑ 1            //y--, x++(x--)
//6←    →2        //x++(x--)
// 5 ↓ 3            //y++, x++(x--)
//    4                //y++
//【位置関係】
//LightCycleの現在位置は2次元(平面)上のx(m_x)y(m_y)座標で特定する。
//走行範囲は競技場限界(m_MinX/-x座標最小値、m_MinY-y座標最小値、
//m_MaxX-x座標最大値、m_MaxY-y座標最大値)内とする。
//また、現在の方向と位置に基づく次の移動位置をm_nx、m_nyとする。
//競技の勝敗決定因である移動回数(プログラム的にはコールされた回数)
//はm_Timesとする。(要すれば一番長く生き残った者が勝つ。)
//【方向転換】
//方向転換のルールとして、進行方向の左右二つずつまでを許容
//    0                //m_Dir == 0であれば
//  7 ↑ 1            //左右斜め上(線をまたぐのは禁止)と
//6←    →2        //左右の転回を許す。
//また、競技車に一定間隔で方向を自ら変えることを許す。プ
//ログラム上は一定の動作回数で方向を変換させる。
//(m_Caprice:0-しない、1-1/100、2-1/200、3-1/400 )
//【速度】(m_Speed)
//速度はタイマー、可変とする。(0-1/1000、1-10/1000、2-100/1000ミリ秒)
//////////////////////////////////////////////////////////////////////
class CLIGHTCYCLE
{
public:        //メンバー変数
    //クラス共通静的変数(クラス外宣言が必要)
    static HDC m_hDC;        //表示デバイスのハンドル
    static int m_MinX;        //x座標最小値
    static int m_MinY;        //y座標最小値
    static int m_MaxX;        //x座標最大値
    static int m_MaxY;        //y座標最大値
    //競技車の属性変数
    int m_Color;            //競走車の色(CANVASクラスのカラー番号)
    int m_StartDir;            //競走車の設定進行方向(0-7)
    int m_StartX;            //競走車の最初のx座標
    int m_StartY;            //競走車の最初のy座標
    int m_Speed;            //競走車のスピード(0-1/1000、1-10/1000、2-100/1000ミリ秒)
    int m_Caprice;            //何遍に1回方向転換を行うか(0-しない、1-1/100、2-1/200、3-1/400)
    //内部管理変数
    UINT m_Times;            //競走車の移動回数
    bool m_Alive;            //現在走行できるか否か
    int m_Dir;                //競走車の進行方向(0-7)
    int m_x;                //競走車の現在のx座標
    int m_y;                //競走車の現在のy座標
    int m_nx;                //競走車の方向の次のx座標
    int m_ny;                //競走車の方向の次のy座標
    char m_msg[32];            //死亡時のメッセージ

public:        //メンバー関数
    CLIGHTCYCLE();            //コンストラクター
    void SetDC(HDC);        //デバイスコンテキストの初期化
    void SetLimit(int, int, int, int);    //座標限界の設定
    void Init();            //競技車データ初期化
    void GetReady();        //競技車スタート位置設定
    void SetData(int, int, int, int, int, int);    //競走車の初期化
    bool CheckNext(int);    //現在方向と位置から次に進めるかを確認
    bool CanGo();            //可能なら進みTRUE、不可なら進まずFALSE
    bool ChangeDir();        //現在の方向以外で進める方向があれ方向転換
    char* Dead();            //死亡処理と通知
};

///////////////////
//コンストラクター
///////////////////
CLIGHTCYCLE::CLIGHTCYCLE() {

    srand(time(NULL));        //乱数の初期化
    Init();
}

///////////////////////////////
//デバイスコンテキストの初期化
//全車共通で一回だけ実行する
///////////////////////////////
void CLIGHTCYCLE::SetDC(HDC hDC) {    

    m_hDC = hDC;
}

/////////////////
//座標限界の設定
/////////////////
void CLIGHTCYCLE::SetLimit(int minx, int miny, int maxx, int maxy) {

    m_MinX = minx;        //x座標最小値
    m_MinY = miny;        //y座標最小値
    m_MaxX = maxx;        //x座標最大値
    m_MaxY = maxy;        //y座標最大値
}

/////////////////////
//競技車データ初期化
/////////////////////
void CLIGHTCYCLE::Init() {

    //個車属性情報
    m_Color = 0;
    m_StartDir = 0;
    m_StartX = 0;
    m_StartY = 0;
    m_Speed = 0;
    m_Caprice = 0;
    m_Alive = FALSE;
}

/////////////////////////
//競技車スタート位置設定
/////////////////////////
void CLIGHTCYCLE::GetReady() {

    //内部管理変数の設定
    m_Times = 0;
    m_Alive = TRUE;
    m_Dir = m_StartDir;
    m_x = m_StartX;
    m_y = m_StartY;
}

///////////////////////
//競走車の初期化
//各車の属性を設定する
///////////////////////
void CLIGHTCYCLE::SetData(int col, int dir, int x, int y, int sp = 0, int cap = 0) {

    m_Color = col;
    m_StartDir = dir;
    m_StartX = x;
    m_StartY = y;
    m_Speed = sp;
    m_Caprice = cap;
}

/////////////////////////////////////////
//現在位置(m_x、m_y)と引数の方向(dir)
//から次の位置(m_nx、m_nyに代入される)
//を求め、競技場内か否か、未走破(黒)か
//否か、斜線跨ぎが無いかで進める(TRUE)
//か否(FALSE)かを返す
/////////////////////////////////////////
bool CLIGHTCYCLE::CheckNext(int dir) {

    bool OK = TRUE;        //戻り値用ブール変数
    //現在方向から次の位置をm_nx、m_nyに代入
    switch(dir) {
    case 0:
        m_nx = m_x;
        m_ny = m_y - 1;
        if(m_ny <= m_MinY)    OK = FALSE;
        break;
    case 1:
        m_nx = m_x + 1;
        if(m_nx >= m_MaxX)    OK = FALSE;
        m_ny = m_y - 1;
        if(m_ny <= m_MinY)    OK = FALSE;
        break;
    case 2:
        m_nx = m_x + 1;
        if(m_nx >= m_MaxX)    OK = FALSE;
        m_ny = m_y;
        break;
    case 3:
        m_nx = m_x + 1;
        if(m_nx >= m_MaxX)    OK = FALSE;
        m_ny = m_y + 1;
        if(m_ny >= m_MaxY)    OK = FALSE;
        break;
    case 4:
        m_nx = m_x;
        m_ny = m_y + 1;
        if(m_ny >= m_MaxY)    OK = FALSE;
        break;
    case 5:
        m_nx = m_x - 1;
        if(m_nx <= m_MinX)    OK = FALSE;
        m_ny = m_y + 1;
        if(m_ny >= m_MaxY)    OK = FALSE;
        break;
    case 6:
        m_nx = m_x - 1;
        if(m_nx <= m_MinX)    OK = FALSE;
        m_ny = m_y;
        break;
    case 7:
        m_nx = m_x - 1;
        if(m_nx <= m_MinX)    OK = FALSE;
        m_ny = m_y - 1;
        if(m_ny <= m_MinY)    OK = FALSE;
        break;
    }    
    //次の位置が黒(COLORREF 0)以外であれば走行済
    if(GetPixel(m_hDC, m_nx, m_ny))
        OK = FALSE;
    else {        //黒であっても
        if(dir % 2)    {    //進行方向斜めで
            COLORREF c = GetPixel(m_hDC, m_x, m_ny);
            //m_(x, y)とm_n(x, y)の線が他の線と交差ていなら不可る
            if(c && c == GetPixel(m_hDC, m_nx, m_y))
                OK = FALSE;
        }
    }
    return OK;
}

////////////////////////////////////////
//可能なら進みTRUE、不可なら進まずFALSE
////////////////////////////////////////
bool CLIGHTCYCLE::CanGo() {

    //走行不能の場合は何もしない
    if(!m_Alive)
        return FALSE;
    //方向転換(不能の場合、m_Dirは変更されない)
    if(m_Caprice && !(rand() % ((1 << m_Caprice) * 50)))
        ChangeDir();
    //現在位置に基づいて次の位置を取得
    if(CheckNext(m_Dir)) {            //走行継続可能
        //現在位置に次の位置を代入
        m_x = m_nx;
        m_y = m_ny;
        //競走車の動作回数を増やす
        m_Times++;
        return TRUE;
    }
    else {
        if(ChangeDir()) {            //方向転換で走行継続可能
            //現在位置に次の位置を代入
            m_x = m_nx;
            m_y = m_ny;
            //競走車の動作回数を増やす
            m_Times++;
            return TRUE;
        }
        else {                        //走行継続不能
            m_Alive = FALSE;
            return FALSE;
        }
    }
}

///////////////////////////////////////////
//現在の方向以外で進める方向があれ方向転換
// 7 0 1    現在の方向から時計回りに2つ、
// 6 * 2    反時計回りに2つ方向を設定し、
// 5 4 3    CanGo()を実行する
//進めればTRUE、不可であればFALSEを返す
//FALSEの場合、進行ができないので終了する
///////////////////////////////////////////
bool CLIGHTCYCLE::ChangeDir() {

    //方向転換候補の特定
    int newdir[4];                        //転換方向候補
    newdir[0] = (m_Dir + 6) % 8;        //反時計回り2つ目
    newdir[1] = (m_Dir + 7) % 8;        //反時計回り1つ目
    newdir[2] = (m_Dir + 1) % 8;        //時計回り1つ目
    newdir[3] = (m_Dir + 2) % 8;        //時計回り2つ目
    //4候補について進行可能か否か確認(不可は-1を代入)
    int result[4];                        //進行可能方向とその数
    int count = 0;
    for(int i = 0; i < 4; i++) {
        if(CheckNext(newdir[i])) {
            result[count] = newdir[i];
            count++;
        }
    }
    //結果処理-進行可能方向(複数)があれば(乱数で選択し)m_Dirを変更する
    if(count) {
        m_Dir = result[rand() % count];    //進行可能方向候補からランダムに選ぶ
        CheckNext(m_Dir);                //再度m_nxとm_nyを設定する為
        return TRUE;
    }
    else
        return FALSE;
}

/////////////////
//死亡処理と通知
/////////////////
char* CLIGHTCYCLE::Dead() {

    m_Alive = FALSE;
    wsprintf(m_msg, "は走行距離%dで死亡。", m_Times);
    return m_msg;
}
 

【LihtCycle.mak】

#-------------------------------------------------------
# BCC Developer 1.2.21
# Copyright (C) 2003 jun_miura@hi-ho.ne.jp
#-------------------------------------------------------
.autodepend
CC=bcc32
RC=brc32
CFLAG=-W  -6 -O2 -w- -AT -pc -H- -k -b  
OUTDIR=-nRelease
CINCS=-IC:\Borland\BCCForm
TARGET=Release\LightCycle.exe
SRC1="C:\Users\(パス)\LightCycle\LightCycle.cpp"
OBJ1=Release\LightCycle.obj
RC1="C:\Users\(パス)LightCycle\LightCycle.rc"
RES1=Release\LightCycle.res

TARGET: $(TARGET)

$(TARGET): $(OBJ1) $(RES1)
    $(CC) $(CFLAG) -e$(TARGET) $(OBJ1)
    $(RC) $(RES1) $(TARGET)

$(OBJ1): $(SRC1)
    $(CC) $(CFLAG) $(OUTDIR) $(CINCS) -c $(SRC1)

$(RES1): $(RC1)
    $(RC) $(RESINCS) -r -fo$(RES1) $(RC1)

【LightCycleProc.h】

//////////////////////////////////////////
// LightCycleProc.h
// Copyright (c) 10/30/2021 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_BITMAP), 7);
    //ツールバーボタン追加
    TBBUTTON tbb[10];
    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_ADD;
    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].fsState = TBSTATE_ENABLED;
    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].fsStyle = TBSTYLE_BUTTON;
    tbb[6].idCommand = IDM_GO;
    tbb[7].iBitmap = TBar.m_id + 5;
    tbb[7].fsStyle = TBSTYLE_BUTTON;
    tbb[7].idCommand = IDM_STOP;
    tbb[8].fsStyle = TBSTYLE_SEP;    //セパレーター
    tbb[9].iBitmap = TBar.m_id + 6;
    tbb[9].fsState = TBSTATE_ENABLED;
    tbb[9].fsStyle = TBSTYLE_BUTTON;
    tbb[9].idCommand = IDM_VERSION;
    TBar.AddButtons(10, tbb);

    //ステータスバー登録-Init(hWnd, hIinstance, ID, (以下省略可)Style)
    SBar.Init(m_hWnd, m_hInstance, STATUSBAR);
    //ステータスバー区画設定
    int sec[3] = {100, 300, -1};
    SBar.SetSection(3, sec);
    //ステータスバー文字列設定
    SBar.SetText(0, "LightCycle Ver 1.0");
    SBar.SetText(2, "マウス左クリックでケーム実行、右クリックでゲーム中止");

    //仮想ウィンドウの初期化
    m_cvs.SetCanvas(m_hWnd);
    //背景を黒の塗り潰しブラシで描画
    m_cvs.Color(0);
    m_cvs.BrSelection(1);
    m_cvs.Clear();

    //LightCycleのデバイスコンテキストの設定
    //m_LC[0].SetDC(m_cvs.hDC());でもよい
    CLIGHTCYCLE::m_hDC = m_cvs.hDC();

    //「ゲームメニュー」とステータスバーを不可にする
    ChangeMenuStatus(FALSE, FALSE);  

    //LightCycle登録数の初期化
    m_NoLC = 0;

    return TRUE;
}

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

    int no;
    switch(wParam) {
    case IDT_TIMER0:
        no = 0;
        break;
    case IDT_TIMER1:
        no = 1;
        break;
    case IDT_TIMER2:
        no = 2;
        break;
    case IDT_TIMER3:
        no = 3;
        break;
    default:
        return FALSE;
    }
    if(m_LC[no].CanGo()) {
        m_cvs.Color(m_LC[no].m_Color);            //競技車両色で
        m_cvs.Dot(m_LC[no].m_x, m_LC[no].m_y);    //走行痕ドットを打つ
    }
    else {
        //タイマー割り込みを停止
        m_tm[no].Stop();
        //ステータスバー第3区分の文字列取得
        char str[MAX_PATH];
        SBar.GetText(2, str);
        CSTR Msg1 = str;
         CSTR Msg2 = g_Col[m_LC[no].m_Color - 8];
        Msg2 = Msg2 + "号";
        Msg2 = Msg2 + m_LC[no].Dead();
        Msg1 = Msg1 + Msg2;
        SBar.SetText(2, Msg1.ToChar());
    }
    //ゲーム終了処理
    if(!m_LC[0].m_Alive && !m_LC[1].m_Alive &&
        !m_LC[2].m_Alive && !m_LC[3].m_Alive) {
        MessageBox(m_hWnd, "全車両停止しました", "競技終了",
                    MB_OK | MB_ICONEXCLAMATION);
        g_On = FALSE;
        ChangeMenuStatus(TRUE, FALSE);
    }
    return TRUE;
}

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

    //メニューアイテムインフォ構造体
    MENUITEMINFO mii;
    //メニューハンドルの取得
    HMENU hMenu = GetMenu(m_hWnd);
    HMENU hGameMenu = GetSubMenu(hMenu, 1);
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_STATE;
    GetMenuItemInfo(hGameMenu, IDM_GO, FALSE, &mii);
    if(mii.fState & MFS_DISABLED) {
        MessageBox(m_hWnd, "現在左クリックは使用できません", "注意",
                    MB_OK | MB_ICONWARNING);
        return FALSE;
    }
    else {
        OnGo();
        return TRUE;
    }
}

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

    //メニューアイテムインフォ構造体
    MENUITEMINFO mii;
    //メニューハンドルの取得
    HMENU hMenu = GetMenu(m_hWnd);
    HMENU hGameMenu = GetSubMenu(hMenu, 1);
    mii.cbSize = sizeof(mii);
    mii.fMask = MIIM_STATE;
    GetMenuItemInfo(hGameMenu, IDM_STOP, FALSE, &mii);
    if(mii.fState & MFS_DISABLED) {
        MessageBox(m_hWnd, "現在右クリックは使用できません", "注意",
                    MB_OK | MB_ICONWARNING);
        return FALSE;
    }
    else {
        OnStop();
        return TRUE;
    }
}

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

    //ツールバーツールチップ-因みにこの時のidCtrl(wParam)はメニューアイテムになる(例:IDM_OPEN)
    if(((LPNMHDR)lParam)->code == TTN_NEEDTEXT) {    //TTN_GETDISPINFOと同じ
        ((LPTOOLTIPTEXT)lParam)->hinst = m_hInstance;
        char* tag;
        switch(((LPTOOLTIPTEXT)lParam)->hdr.idFrom) {
        case IDM_ADD:
            tag = "追加・編集";
            break;
        case IDM_OPEN:
            tag = "データを開く";
            break;
        case IDM_SAVE:
            tag = "データの保存";
            break;
        case IDM_EXIT:
            tag = "終了";
            break;
        case IDM_GO:
            tag = "実行";
            break;
        case IDM_STOP:
            tag = "中止";
            break;
            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();
    //ゲーム開始迄にゲーム領域を確定する
    if(!g_On) {
        CLIGHTCYCLE::m_MinX = 0;
        CLIGHTCYCLE::m_MaxX = LOWORD(lParam);
        RECT rec;
        GetWindowRect(TBar.m_hWnd, &rec);
        CLIGHTCYCLE::m_MinY = rec.bottom - rec.top;
        CLIGHTCYCLE::m_MaxY = HIWORD(lParam);    //仮置き
        GetWindowRect(SBar.m_hWnd, &rec);
        CLIGHTCYCLE::m_MaxY -= (rec.bottom - rec.top);
        //競技場枠の表示
        m_cvs.Clear();
        m_cvs.Color(7);
        m_cvs.Box(CLIGHTCYCLE::m_MinX, CLIGHTCYCLE::m_MinY, CLIGHTCYCLE::m_MaxX, CLIGHTCYCLE::m_MaxY, 0);
        //競技場座標の表示
        char str[64];
        wsprintf(str, "競技場 (%d,%d)-(%d,%d)", CLIGHTCYCLE::m_MinX, CLIGHTCYCLE::m_MinY, CLIGHTCYCLE::m_MaxX, CLIGHTCYCLE::m_MaxY);
        SBar.SetText(1, str);
    }
    return TRUE;
}

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

    PAINTSTRUCT paint;
    m_cvs.OnPaint(BeginPaint(m_hWnd, &paint));
    EndPaint(m_hWnd, &paint);
    return TRUE;
}

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

    if(MessageBox(m_hWnd, "終了しますか", "終了確認",
                    MB_YESNO | MB_ICONINFORMATION) == IDYES) {
        //タイマーを廃棄
        for(int i = 0; i < m_NoLC; i++)
            m_tm[i].Stop();
        //処理をするとDestroyWindow、PostQuitMessageが呼ばれる
        return TRUE;
    }
    else
        //そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
        return FALSE;
}

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

    //典型的なウィンドウのサイズ制限処理
    MINMAXINFO *pmmi;
    pmmi = (MINMAXINFO*)lParam;
    pmmi->ptMinTrackSize.x = 656;    // 最小幅(競技エリア640)
    pmmi->ptMinTrackSize.y = 509;    // 最小高(競技エリア400)

    return FALSE;                    //処理はDefWndProcに任す
}

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

    if(inputdlg.DoModal(m_hWnd, "IDD_INPUT", inputdlgProc)) {
        if(m_NoLC)    //m_NoLCが0でなくなったらゲーム実行可能
            ChangeMenuStatus(TRUE, FALSE);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "処理がキャンセルされました。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnOpen() {

    char* fn = cmndlg.GetFileName(m_hWnd, "LightCycleデータファイル(*.lcd)\0*.lcd\0\0", TRUE, "lcd", ".", "LightCycleデータを開く");
    if(fn) {
        g_File.SetName(fn);    //ファイルパス、名の登録
        m_NoLC = g_File.ReadInt("車両", "台数");
        char str[16];        //セクション名用バッファ
        int Color, StartDir, StartX, StartY, Speed, Caprice;    //各属性変数
        for(int i = 0; i < m_NoLC; i++) {
            wsprintf(str, "車両No. %d", i);
            Color = g_File.ReadInt(str, "車両色");
            StartDir = g_File.ReadInt(str, "方向");
            StartX = g_File.ReadInt(str, "X座標");
            StartY = g_File.ReadInt(str, "Y座標");
            Speed = g_File.ReadInt(str, "速度");
            Caprice = g_File.ReadInt(str, "転回頻度");
            //データをセットするのみならず、走行可能状態にする
            m_LC[i].SetData(Color, StartDir, StartX, StartY, Speed, Caprice);
        }
        //m_NoLCが0でなくなったら以下を実行する。
        if(m_NoLC)
            ChangeMenuStatus(TRUE, FALSE);
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "処理がキャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnSave() {

    char* fn = cmndlg.GetFileName(m_hWnd, "LightCycleデータファイル(*.lcd)\0*.lcd\0\0", FALSE, "lcd", ".", "LightCycleデータを保存");
    if(fn) {
        g_File.SetName(fn);    //ファイルパス、名の登録
        g_File.WriteInt("車両", "台数", m_NoLC);
        char str[16];        //セクション名用バッファ
        for(int i = 0; i < m_NoLC; i++) {
            wsprintf(str, "車両No. %d", i);
            g_File.WriteInt(str, "車両色", m_LC[i].m_Color);
            g_File.WriteInt(str, "方向", m_LC[i].m_StartDir);
            g_File.WriteInt(str, "X座標", m_LC[i].m_StartX);
            g_File.WriteInt(str, "Y座標", m_LC[i].m_StartY);
            g_File.WriteInt(str, "速度", m_LC[i].m_Speed);
            g_File.WriteInt(str, "転回頻度", m_LC[i].m_Caprice);
        }
        return TRUE;
    }
    else {
        MessageBox(m_hWnd, "処理がキャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
}

bool CMyWnd::OnExit() {

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

bool CMyWnd::OnGo() {

    //ゲーム終了後は画面をクリア
    if(!g_On) {
        m_cvs.Clear();
        //競技場枠の表示
        m_cvs.Color(7);
        m_cvs.Box(CLIGHTCYCLE::m_MinX, CLIGHTCYCLE::m_MinY, CLIGHTCYCLE::m_MaxX, CLIGHTCYCLE::m_MaxY, 0);
    }
     //競技車のデータが登録されてい ない場合の処置
    if(!m_NoLC) {
        MessageBox(m_hWnd, "競技車のデータが登録されていません\n「ファイル"\
                        "「新規」または「データを開く」で設定してください。",
                        "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    else {
        //タイマーの設定(ミリ秒設定なので0.001秒タイマーとなっている)
        for(int i = 0; i < m_NoLC; i++) {
            if(!g_On)                    //ゲーム開始前なら
                m_LC[i].GetReady();        //出走用意
            if(!m_tm[i].Begin(m_hWnd, g_Timer[i], g_Velo[m_LC[i].m_Speed])) {
                MessageBox(m_hWnd, "タイマーを設定できませんでした", "エラー",
                    MB_OK | MB_ICONERROR);
                return FALSE;
            }
        }
        //ゲーム開始後は「実行」メニューアイテムとボタンを凍結する。
        g_On = TRUE;
        //「ファイル」メニューを不可にする
        EnableMenuItem(GetMenu(m_hWnd), 0, MF_BYPOSITION | MF_GRAYED);
        //「ゲーム」メニューのアイテムの状態を変更する
        ChangeMenuStatus(FALSE, TRUE);
        //ステータスバーによる結果表示の為
        SBar.SetText(2, "");
        return TRUE;
    }
}

bool CMyWnd::OnStop() {

    int ret = MessageBox(m_hWnd, "完全に中止しますか(No-一時停止)", "確認",
                        MB_YESNOCANCEL | MB_ICONQUESTION);
    switch(ret) {
    case IDYES:
        //全車死亡、タイマーを廃棄
        for(int i = 0; i < m_NoLC; i++)
            m_LC[i].Dead();
        g_On = FALSE;
    case IDNO:
        //タイマーを廃棄
        for(int i = 0; i < m_NoLC; i++) {
            m_tm[i].Stop();
        }
        //「ファイル」メニューを可にする
        EnableMenuItem(GetMenu(m_hWnd), 0, MF_BYPOSITION | MF_ENABLED);
        //「ゲーム」メニューのアイテムの状態を変更する
        ChangeMenuStatus(TRUE, FALSE);
        return TRUE;
    case IDCANCEL:
        return FALSE;
    }
}

bool CMyWnd::OnVersion() {

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

//////////////////
//ユーザー定義関数
//////////////////
///////////////////////////////////////////
//「ゲーム」メニュー、ツールバー状態の変更
///////////////////////////////////////////
void CMyWnd::ChangeMenuStatus(bool Flag1, bool Flag2) {

    //メニューハンドルの取得
    HMENU hMenu = GetMenu(m_hWnd);
    //メニューアイテムを不可にする
    HMENU hGameMenu = GetSubMenu(hMenu, 1);
    EnableMenuItem(hGameMenu, IDM_GO, MF_BYCOMMAND | (Flag1 ? MF_ENABLED : MF_GRAYED));
    EnableMenuItem(hGameMenu, IDM_STOP, MF_BYCOMMAND | (Flag2 ? MF_ENABLED : MF_GRAYED));
    DrawMenuBar(LightCycle.GetHandle());    //Re-draw "&Game" menu

    //ツールバー(MAKELONGマクロは16bit整数2つをunsigned 32bit整数にする)
    SendMessage(TBar.GetHandle(), TB_ENABLEBUTTON, IDM_GO, MAKELONG(Flag1, 0));
    SendMessage(TBar.GetHandle(), TB_ENABLEBUTTON, IDM_STOP, MAKELONG(Flag2, 0));
}

///////////////////////////////
//ユーザーダイアログの関数定義
//Inputダイアログ
///////////////////////////////
bool INPUTDLG::OnInit(WPARAM, LPARAM) {

    char str[16];    //コンボボックス登録用文字列変数
    //競技車番号
    for(int i = 0; i < LightCycle.m_NoLC; i++) {
        wsprintf(str, "%d", i + 1);
        SendItemMsg(IDC_COMBOBOX0, CB_ADDSTRING, 0, (LPARAM)str);
    }
    //競技車色
    for(int i = 0; i < 8; i++) {
        SendItemMsg(IDC_COMBOBOX1, CB_ADDSTRING, 0, (LPARAM)g_Col[i]);
    }
    //進行方向
    for(int i = 0; i < 8; i++) {
        SendItemMsg(IDC_COMBOBOX2, CB_ADDSTRING, 0, (LPARAM)g_Dir[i]);
    }
    //速度
    for(int i = 0; i < 3; i++) {
        SendItemMsg(IDC_COMBOBOX3, CB_ADDSTRING, 0, (LPARAM)g_Speed[i]);
    }
    //転回頻度
    for(int i = 0; i < 4; i++) {
        SendItemMsg(IDC_COMBOBOX4, CB_ADDSTRING, 0, (LPARAM)g_Caprice[i]);
    }
    //登録車両数が0 - 3迄は次の未入力LC[i]を、4の場合先頭を指すは
    if(LightCycle.m_NoLC < 4)
        SendItemMsg(IDC_COMBOBOX0, CB_SETCURSEL, LightCycle.m_NoLC, 0);
    else {
        SendItemMsg(IDC_COMBOBOX0, CB_SETCURSEL, 0, 0);
        SetData(0);
    }

    //対象コントロールのハンドルを取得する
    HWND hCtrl = GetDlgItem(m_hWnd, IDC_IMAGE);
    //文字列リソースのある親ウィンドウ(ダイアログ)のインスタンス
    m_hInstance;
    //ツールチップの作成
    m_hTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
                WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
                CW_USEDEFAULT, CW_USEDEFAULT,
                CW_USEDEFAULT, CW_USEDEFAULT,
                m_hWnd, NULL, 
                m_hInstance, NULL);
    //IDC_IMAGEにツールヒントコントロールを付ける
    TOOLINFO toolInfo = { 0 };    //ゼロクリアー
    toolInfo.cbSize = sizeof(toolInfo);
    toolInfo.hwnd = m_hWnd;
    toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
    toolInfo.uId = (UINT_PTR)GetDlgItem(m_hWnd, IDC_IMAGE);
    toolInfo.hinst = m_hInstance;
    toolInfo.lpszText = "編集状態でも、登録が4台未満ならクリックすると「追加」になります";
    SendMessage(m_hTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
    SetWindowPos(m_hTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
    return TRUE;
}

bool INPUTDLG::OnSelChange(WPARAM wParam) {

    //コンボボックスの選択変更以外は何もしない
    if(HIWORD(wParam) != CBN_SELCHANGE)
        return FALSE;
    //新しい選択アイテムインデックスを取得する
    int i = SendItemMsg(IDC_COMBOBOX0, CB_GETCURSEL, 0, 0);
    SetData(i);
    return TRUE;
}

bool INPUTDLG::OnImage() {

    if(LightCycle.m_NoLC < 4) {
        SendMsg(WM_SETTEXT, 0, (LPARAM)"LightCycleデータの追加");
        SendItemMsg(IDC_COMBOBOX0, CB_SETCURSEL, (WPARAM)-1, 0);
        SendItemMsg(IDC_COMBOBOX1, CB_SETCURSEL, (WPARAM)-1, 0);
        SendItemMsg(IDC_COMBOBOX2, CB_SETCURSEL, (WPARAM)-1, 0);
        SendItemMsg(IDC_COMBOBOX3, CB_SETCURSEL, (WPARAM)-1, 0);
        SendItemMsg(IDC_COMBOBOX4, CB_SETCURSEL, (WPARAM)-1, 0);
        SendItemMsg(IDC_EDIT2, WM_SETTEXT, 0, (LPARAM)"");
        SendItemMsg(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)"");
    }
}

bool INPUTDLG::OnOk() {

    //選択車両番号インデックス(0-3)を取得する
    int i = SendItemMsg(IDC_COMBOBOX0, CB_GETCURSEL, 0, 0);
    if(i == CB_ERR)    //選択が無ければ追加登録
        i = LightCycle.m_NoLC;
    //車両色(0-7)の取得
    int j = SendItemMsg(IDC_COMBOBOX1, CB_GETCURSEL, 0, 0);
    if(j == CB_ERR) {
        MessageBox(m_hWnd, "色が選択されていません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_Color = j + 8;    //CANVASクラスの色コード(8-15)
    //車両の方向(0-7)
    j = SendItemMsg(IDC_COMBOBOX2, CB_GETCURSEL, 0, 0);
    if(j == CB_ERR) {
        MessageBox(m_hWnd, "方向が選択されていません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_StartDir = j;
    //車両のX座標
    char str[16];        //エディットボックス用文字列変数
    SendItemMsg(IDC_EDIT2, WM_GETTEXT, 16, (LPARAM)str);
    j = atoi(str);        //エラー処理をどうするか?
    if(j <= 0 || j > CLIGHTCYCLE::m_MaxX) {
        MessageBox(m_hWnd, "Xの値が不正です。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_StartX = j;
    //車両のY座標
    SendItemMsg(IDC_EDIT3, WM_GETTEXT, 16, (LPARAM)str);
    j = atoi(str);        //エラー処理をどうするか?
    if(j <= 0 || j > CLIGHTCYCLE::m_MaxY) {
        MessageBox(m_hWnd, "Xの値が不正です。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_StartY = j;
    //車両の速度(0-2)
    j = SendItemMsg(IDC_COMBOBOX3, CB_GETCURSEL, 0, 0);
    if(j == CB_ERR) {
        MessageBox(m_hWnd, "速度が選択されていません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_Speed = j;
    //車両の転向頻度(0-3)
    j = SendItemMsg(IDC_COMBOBOX4, CB_GETCURSEL, 0, 0);
    if(j == CB_ERR) {
        MessageBox(m_hWnd, "転向頻度が選択されていません。", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;    //ダイアログを開いたまま処理を無効とする
    }
    LightCycle.m_LC[i].m_Caprice = j;
    //走行可能状態にする
    LightCycle.m_LC[i].GetReady();
    //追加登録の場合、車両台数を増やす
    if(i == LightCycle.m_NoLC)
        LightCycle.m_NoLC++;
    EndModal(TRUE);
    return TRUE;
}

bool INPUTDLG::OnCancel() {

    EndModal(FALSE);
    return TRUE;
}

void INPUTDLG::SetData(int i) {

    char str[16];    //エディットボックス用文字列変数
    //既存データが選択されたので編集であることを示す
    SendMsg(WM_SETTEXT, 0, (LPARAM)"LightCycleデータの編集");
    //CANVASクラスの色コード8-15を使用している為
    SendItemMsg(IDC_COMBOBOX1, CB_SETCURSEL, (LightCycle.m_LC[i].m_Color - 8), 0);
    SendItemMsg(IDC_COMBOBOX2, CB_SETCURSEL, LightCycle.m_LC[i].m_StartDir, 0);
    wsprintf(str, "%d", LightCycle.m_LC[i].m_StartX);
    SendItemMsg(IDC_EDIT2, WM_SETTEXT, 0, (LPARAM)str);
    wsprintf(str, "%d", LightCycle.m_LC[i].m_StartY);
    SendItemMsg(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)str);
    SendItemMsg(IDC_COMBOBOX3, CB_SETCURSEL, (WPARAM)LightCycle.m_LC[i].m_Speed, 0);
    SendItemMsg(IDC_COMBOBOX4, CB_SETCURSEL, LightCycle.m_LC[i].m_Caprice, 0);
}

///////////////////////////////
//ユーザーダイアログの関数定義
//Versionダイアログ
///////////////////////////////
bool VERSIONDLG::OnOk() {

    EndModal(TRUE);
    return TRUE;
}

bool VERSIONDLG::OnCancel() {

    EndModal(FALSE);
    return TRUE;
}