あけましておめでとうございます。本年も BCCForm and BCCSkelton をよろしくお願いいたします。

さて、昨年を振り返れば、

【良かったこと】
20年ぶりに BCCForm and BCCSkelton を更新し、そのサポートとして本ブログを開き、サンプルの解説と共に(お約束の)Hello, World!、IDList,DPAPI、Dump、LightCycle、Dump、Command、(私的には待望していた)MENACE、BCCSkeltonのClang対応によるBatchGoodと9つのプログラムを開発し、とうとうbcc32c.exeの謎を解き明かすという実りある一年であるとともに、

【落胆したこと】
年末になり、良さげなCOMコントロールを探したのですが見つからず、夜中に思いついたアイデアでBCFSketonを改良しましたがまだまだ合格点は与えられず、最後にC++で扱えるビデオコントロールを探し、Windows Media Player(webから旧いEmbarcadero BCBのATLを導入したのだけど動かない)、libVLC(何も知らないし、資料を読むだけで長い時間がかかる)と巡り合えるも距離を置き、最もCOMらしくて手軽、手頃なDierctShowを知り、その基本的な使い方(↓)を知ったのですが、

//-------- サンプル開始 --------
//(1)DirectShowのヘッダーを入れる
#include <DShow.h>

    //(2)DirectShow関連変数を宣言する
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent *pEvent = NULL;
    IVideoWindow *pVideoWindow = NULL;
    //結果判定用変数
    HRESULT hr;

    //(3)COMの初期化
    hr = CoInitialize(NULL);
    //フィルターグラフマネージャーを作成し、IGraphBuilderインターフェイスを取得
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("フィルターグラフマネージャーを作成できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
    }
    //(4)IGraphBuilderからIMediaControlインターフェイスを取得
    hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("pControlを取得できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //(5)IGraphBuilderからIMediaEventインターフェイスを取得
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("pEventを取得できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //以上でフィルタ グラフを作成することができる
    //(6)指定されたファイルを再生できるフィルタ グラフを作成する
    //(指定されたファイルが存在しない場合、またはファイル フォーマットが認識されない場合、失敗する)

    hr = pGraph->RenderFile(g_Fn, NULL);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("フィルターグラフを作成できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        RenderFile_Err(hWnd, hr);
        wchar_t str[64];
        swprintf(str, 64, L"hr = 0X%x", hr);
        MessageBoxW(hWnd, str, L"エラーコード", MB_OK | MB_ICONERROR);
        //VFW_E_NOT_FOUND (0X80040216) エラー
        //https://www.solvusoft.com/ja/errors/%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0-%E3%82%A8%E3%83%A9%E3%83%BC/microsoft-corporation/directshow/0x80040216-vfw-e-not-found/
        break;
    }
    //(7)ビデオコントロールを取得する(必ずReadFile以降で実行する)
    hr = pGraph->QueryInterface(IID_IVideoWindow, (LPVOID*)&pVideoWindow);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("pVideoWindowを取得できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //(8)描画先ウィンドウの設定
    //描画先ウィンドウの親の設定

    hr = pVideoWindow->put_Owner((OAHWND)hWnd);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("親ウィンドウを設定できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //描画先ウィンドウのスタイルの設定
    hr = pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("スタイルを設定できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //描画先ウィンドウの表示
    hr = pVideoWindow->put_Visible(OATRUE);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("ビデオ画面を表示できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //描画先ウィンドウ内の描画位置の設定
    hr = pVideoWindow->SetWindowPosition(0, 0, 320, 240);    //(x, y, w, h)
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("ビデオ画面を表示できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //(9)ウィンドウを表示する
    hr = pVideoWindow->put_Visible(OATRUE);
    if(FAILED(hr)) {
        MessageBox(hWnd, _T("ビデオ画面を表示できませんでした"), _T("エラー"), MB_OK | MB_ICONERROR);
        break;
    }
    //(10)動画表示を開始する。
    hr = pControl->Run();
    if (SUCCEEDED(hr)) {
        // 完了するまで待機する。
        long evCode;
        pEvent->WaitForCompletion(INFINITE, &evCode);
        // 注 : 実際のアプリケーションでは INFINITE を使用しないこと。
        // 無期限にブロックする場合がある。
    }
    //(11)終了処理-メモリーの開放
    pVideoWindow->Release();
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();
//-------- サンプル終了 --------

RenderFileメソッドでエラーが出、手持ちのデータファイルで試したところ、aviは正常出力しますが、一番肝心なnp4やmov等は全滅です。
この為、webを調べてffdshowという優れモノのコーデックを導入、Microsoftのコーデックと被らないようにWin7DSFilterTweaker_6.4というユーティリティで設定を行いましたが、しつこくエラーが出続けるので、エラー検出関数(bool RenderFile_Err(HWND hWnd, HRESULT hr) )を自作して調べるも該当するエラーメッセージが無いということで、戻り値のhrを16進数出力したところ、

悪名高きVFW_E_NOT_FOUND (0X80040216) エラー

であることが判明。対処方法は(Microsoftが危険だからとして警告している)サードパーティソフトによるレジストリークリーニングらしく、失敗すればPC(と今まで蓄積してきた情報、データ)が完全アウトになるという重大リスク(注)があることから、即「既に高性能のフリーソフトが溢れているオーディオ、ビデオ関連のCOMプログラミング」を断念しました。
注:小生、現役時代は損害保険会社系のリスクコンサルタントでした。

と、いうことで、本年もweb系言語の方が優れているコンポーネントインターフェースを使ったプログラミングよりも、アルゴリズムとデータ処理に優れているC++のオブジェクトオリエンテッドプログラミング(OOP!-英語で「おっと!」という意味)を目指してゆく所存です。

m_(__)_m
 

【告知】

本日BCFEditorの処理に細工をしてアップしました。時々考え込んで使う気になれないくらい遅くなる悪癖が大分改善したように思えます。(開発者としてはサイテーなんですが、最近Sakura Editorを使っていました。今日からまたBCFEditorに戻します。)面倒くさいのでVersion変更を行っていませんが、「2021年12月29日10:17」となっていれば最新版です。(BCCForm and BCCSkeltonはポルシェと同じで、"The latest, the best."です。)

 

【よもやま話】

私の場合、着想は夜中に目が覚めた時に降ってくることが多いです。↑のBCFEditorのアルゴリズム変更もそうですが、(make.exeを使うBCCMakerからOBJとASM対応だけ減らしてバッチ処理にした)BatchGoodもそうですした。12月27日に修正告知を書いてから、BatchGoodはしっかりと働いております。

 

これでBCC102を使った小規模開発は楽になる、ということで、前に温めていた構想(ビデオコントロールを使った家庭アプリかなんか)を発展させようと、動画を再生できるコントロールを検討していました。

<MicrosoftのDirectShow>

最初はMicrosoftのDirectShow。これはCOMで簡単に実装できるのですが、何せオリジナルで対応するビデオ形式がAVIやWMVなどでmp4などで読み込もうとするとエラーが出ます。これはコーデックを増やす方法があるか検討しましょう。

 

<Windows Media Player>

これはカプセル化されたコントロールになるので、可也期待したのですが、Microsoftのサイトのサンプルを見るとATL( Active Template Library -COM 開発を簡単にするラッパー ライブラリ)の使用が必須です。ところが、EmbarcaderoはATLのライセンスを失い、自社のDelphi ActiveX(DAX)コントロールに移行してしまい、現在BCC102にはATLのヘッダーファイルも、libファイルもありません。(まぁ、もう陳腐化した技術になったようなので要らないっちゃ、そうですが。どうしても使いたい人に対してEmbarcaderoは「昔のBCBのファイルをコピーしなさい」と言っています。)

しかし、世の中には奇特な人がいるもので、その人のサイトではどこにコピーするかも含めてATL関連ファイル(atllフォールダーとlibフォールダー)の圧縮ファイルがダウンロードできるようになっています。

私も早速ダウンロードして、すべて大文字のファイル名を小文字化させ(注)、Microsoftのサンプルプログラムをコンパイルしました。

注:何か懐かしいC風野ツールですが、一挙に多くのファイルを投げ込むと正常動作しませんね。

/* ----------------------------------
    U2L.exe

    Rename Upper case file name into
    Lower case one
    --------------------------------- */
#include <stdio.h>
#include <conio.h>
#include <stdlib>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {

    char *p, buff[260];    // == MAX_PATH
    int i, j;
    if(argc > 1) {
        for(i = 1; i < argc; i++) {
            for(p = argv[i], j = 0; *p; p++, j++) {
                if(*p >= 'A' && *p <= 'Z')
                    buff[j] = *p + ('a' - 'A');
                else
                    buff[j] = *p;
            }
            buff[j] = *p;
            rename(argv[1], buff);
            cout << endl << argv[i] << " ---> " << buff << endl;
        }
    }
    else
        cout << "Usage: Throw files into this program icon." << endl;
    getch();
    return 0;
}

 

すると、でるわでるわ、エラーの山です。しかし、それらは皆次のように纏められます。

_Moduleが宣言されていない(71行目から)
m_hWndが宣言されていない(1349行目から)
GetDlgItemが宣言されていない、引数が2つの筈(1974行目から)-hWndChildは宣言されている
m_pCurrentMsgが宣言されていない(2090行目から)
m_thunkが宣言されていない(2174行目から)
StartWindowProcが宣言されていない(2235行目から)
GetWndClassNameが宣言されていない(2242行目から)
m_pfnSuperWindowProcが宣言されていない(2243行目から)
明らかに"windows.h"が効いておらず、クラスの宣言文がスキップされたエラーと思われ、来年条件コンパイル式をゆっくりとリビューしてみます。

 

libVLC

そんなこんなで動画コントロールをググっていたら、libVLCというサイトにぶつかりました。ライセンスの制約はありますが、サンプルにWin32SDKのコードが載っていたので「おっ!」と思いましたが、この方(あのフリーソフトVLCの開発チーム、VideoLANのメンバーの一人だと思われます)のサイトを上に上がって眺めたら、その知見がすごい。なんか、チャチャッと弄っってものにする、というのがためらわれる感じです。

 

ということで、この「BCC102でCOMプログラミングをしよう」というプロジェクトは来年じっくりと考えようかと思います。

 

(未だ明日と、明後日があり、よもやま話を書くかもしれませんが、ボケて忘れると嫌なので)本年は拙いブログ文にお付き合いいただきありがとうございました。良いお年をお迎えください。

 

bcc32c.exe専用のコンパイラーツールとして、(そのまま使えるのですが、オプション類がbcc32.exe用になっているので手作業が多い)BCCMakerをダウンサイズして開発していましたが、一応ーtCも-tWもコンパイルできるようになったので昨日プロトタイプをアップしました。

 

がっ、

 

余りにBCCSkeltonプログラムに傾注した余り(LIBファイルのツリービューのアイコンまで削除してしまい)libファイルを入れたコンパイルを失念し、また-tDオプションでDLLファイルは生成されるのですが、libファイルをつかった静的ロードには別作業でimplibを使ってlibファイルを生成しなければならないことに気が付きました。

 

「libファイルを入れてコンパイルしようにも、libファイルを登録できないじゃない?」

「あれだけ騒いでいたのに、DLLは作れるけどlibファイルは出さないじゃない?」

 

という苦情に対して「そこまでわかっているなら手作業でバッチファイルを修正すればいーじゃん!」と開き直るほど人間ができていないので、大急ぎで対応しました。以下はDLLをつくるDllSample.cppというファイルからDLLとLIBファイルを出し、それを使ったDllParent.cppというプログラムのコンパイル用のバッチファイルです。思ったようにできました。

 

【DllSample.bat】

@ECHO OFF
ECHO ----------------------------------
ECHO  BatchGood - Batch File Generator
ECHO  for Embarcadero "bcc32c.exe"
ECHO  Copyright (c) 2021 by Ysama
ECHO ----------------------------------

cd "C:\Users\ysama\Programing\Windows Program\Dll\Debug"
"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\ysama\Programing\Windows Program\Dll\DllSample.cpp" -tD -w-
del "C:\Users\ysama\Programing\Windows Program\Dll\Debug\DllSample.tds"
"C:\Borland\BCC102\bin\implib.exe" "C:\Users\ysama\Programing\Windows Program\Dll\Debug\DllSample.lib" "C:\Users\ysama\Programing\Windows Program\Dll\Debug\DllSample.dll"
pause
 

【DllParent.bat】
@ECHO OFF
ECHO ----------------------------------
ECHO  BatchGood - Batch File Generator
ECHO  for Embarcadero "bcc32c.exe"
ECHO  Copyright (c) 2021 by Ysama
ECHO ----------------------------------

cd "C:\Users\ysama\Programing\Windows Program\Dll\Debug"
"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\ysama\Programing\Windows Program\Dll\DllParent.cpp" "C:\Users\ysama\Programing\Windows Program\Dll\Debug\DllSample.lib" -tC -w-
del "C:\Users\ysama\Programing\Windows Program\Dll\Debug\DllParent.tds"
pause
 

結果は以下の通りです。

 

これでやっとbcc32cを使い倒せそうです。

 

【関連】

【BCC】C++コンパイラーの謎シリーズ

【bcc32c】BCCSkeltonのbcc32c.exe(Clang文法チェック)対応

【bcc32c】BCCSkeltonのClang対応修正後リンカーエラーとその対応

【bcc32c】BCCSkeltonのClang対応結果

【bcc32c】BCCSkeltonのClang対応結果(追補)

【bcc32c】BCCSkeltonのClang対応の恩恵

【bcc32c】コンパイラーオプション続報

【bcc32c】コンパイラーオプション続々報-DLL(12月22日修正、追記)

 

さて、表面上はMENACEの連続投稿をしていましたが、水面下で新しいプロジェクトを進行させていました。
それはbcc55用のBCCMakerに相当する、bcc32c専用の「バッチグー!」(旧っ!死語ですね)、"BatchGood"という、コンパイル用のバッチファイル生成ツールです。尤も、bcc32c.exeのオプションの使い方が充分にわかっていない状態なので大がかりなことをやるつもりはなく、BCCMakerをダウンサイズして「コンパイルするのはcppファイル一つとrcファイル一つ」に限定し、「暇プログラマーが通常使うようなオプションだけ」に絞ったシンプルな簡易ツールを目指しました。

しかし、その途上でまたまた頭を抱える事態が出来しましたので、今までの振り返りを行います。

(1)先ず、bcc32c(Embarcadero C++ 7.30)でbcc32(Borland C++ 5.5.1)のコマンドオプションを使うと(例:BCCMaker)、

Warning: option '-6' is not supported in Clang-based compiler.(その他'-H-'や'-k'等)
という警告の他に、Turbo Linkerから

Error: Unresolved external '_main' referenced from C:\BORLAND\BCC102\LIB\WIN32C\DEBUG\C0X32.OBJ
というエラーが出ました。(ウィンドウプログラムの"-W"オプションを与えているが、コンソールプログラムと認識されている。)

(2)従って、bcc32cは新しいClang対応のコンパイラーで、文法チェックが厳しくなり、bcc32とはオプションも異なると認識しました。それがBCCSkeltonのClang対応修正につながります。

(3)ここで頭を悩ませたのがbcc32cにはヘルプが二つあることです。具体的には、

①"bcc32c -h"で出てくるもの(これは旧bcc32とほぼ同じものです)と、

②内容は全く異なりますが、"bcc32c -Xdriver --help"(この"-Xdriver"オプションは「その後のオプションを直接Clangコンパイラーに伝える」ものだそうです)で出てくる

   Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.

   OVERVIEW: Embarcadero C/C++ compiler driver for Win32

   USAGE: bcc32c [options] <inputs>

ものです。(後でわかったのですが、これは従来のコンパイラーとは完全に別物のClangコンパイラー、bcc32x.exeのヘルプそのものでした。

    Embarcadero CLANG 7.30 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
    OVERVIEW: clang LLVM compiler
    USAGE: bcc32x [options] <inputs>

更に困惑したのは、矢張りbcc32cでプログラムをされている方のサイトで、↑の①のヘルプについて「※「-h」で出てくるオプションの一覧はこちらをご覧ください。BCC32C.EXEで有効ではないオプションが分かるようにしています。」とされていたからです。

 

「では、いったい何が正しいんだ???」

 

というのが正直なところであり、それがBatchGoodを作ろうという動機になりました。そして上記の情報を基にBatchGoodのプロトタイプができたのでテストをしたころ、呼び出し規約で躓きます。bcc32cのヘルプファイルには"-pxxx Use Pascal calling convention"と使えるように書かれていますが、Pascal呼び出しなのか?xxxxは何なのか?が分かりません。また-Xdriver --Helpで取得するClangヘルプファイルには"-mcdecl Make CDecl calling convention the default"、"-mfastcall Make FastCall calling convention the default"、"-mstdcall Make StdCall calling convention the default"と表記が違います。更にEmbarcaderoのコンパイラースイッチの解説サイトでは「呼び出し規約」のところで、bcc32では"-pc"、"-ps"、"-pm"ですが、bcc32cでは"-mcdecl"、"-mstdcal"、"-mfastcall"とはっきり書かれています。ということなので、BatchGoodはbcc32cの"-m系"オプションにしたのですが、テスト時になんと"E2075 Incorrect command line option: -mcdecl(-mstdcall、-mfastcall)"というエラーが出ました。E2075は「E2075 type オプションの間違い」とのことで「どうなっているの???」と頭を抱えました。

所が「ひょっとして?」と、このオプションにbcc55の"-p系"のものを使うと、あーら不思議、キレイに通ってしまいます。

これではWEBの情報はうのみにできないと必要な情報は自分でテストして調べることにし、その結果を一覧(注)にまとめたところ、「bcc32cでは使えない」とEmbarcaderoや他の方のサイトで言われていたことが、呼び出し規約の他にも最適化の-Oxオプションや、-R、-vや-yオプション等が実はコンパイラーを通ることが判明しました。

注:BatchGoodのヘルプに掲載します。

 

このことから"-Xdriver"の説明も氷解し、「bcc32cは、bcc32xというClangコンパイラーに従来のbcc32と可能な限り互換性のあるオプションを変換して伝える翻訳機能付きのコンパイラーである」ということです。それは、

bcc32c.exe 17,438KB(翻訳機能の分だけClangコンパイラーよりも大きい)

bcc32x.exe 17,243KB

からも傍証されます。

 

この結果を踏まえて、BatchGoodは要らない情報は排除する次の方針をプログラムの中で謳っています。

【BatchGoodReadMe.txt】

【bcc32c.exe】BCCForm and BCCSkeltonプログラムのコンパイルに関連するオプション

1.必須のオプション
(1)ターゲットオプション(-tX)
   -tC CUIアプリケーション
   -tW Windowsアプリケーション
   -tD Dynamic Link Library(DLL)
   -tR RTL(Run Time Library - cc32c250.dll)を利用するアプリ
(2)ファイルインクルードオプション
   -I<ファイルパス、名>(#includeで読み込むヘッダーファイルのサーチパス)
(3)警告表示オプション(-w、-w-)
   警告を表示する(-w)、表示しない(-w-)
(4)最適化オプション(-Ox)
   -O1 サイズを最適化
   -O2 速度を最適化

2.使う可能性のあるオプション
(1)コンパイルのみオプション(別途リンクが必要)
   -c
(2)呼び出し規約オプション
   -pc C呼び出し
   -ps stdcall呼び出し
   -pm fastcall呼び出し
(3)重複文字列等号オプション
   -d
(4)出力先指定オプション
   -n<出力先パス>
(5)OBJファイルパス、名指定オプション
   -o<OBJファイルパス、名>
(6)enum列挙型をint とするオプション(デフォルト)
   -b
(7)ソースレベルデバッグオプション(デバッグ用)
   -v
(8)ソース行番号オプション(デバッグ用)
   -y

【参考】使う可能性のあるbrc32c.exeコンパイルオプション
-r コンパイルのみ(resファイル出力-別途リンクが必要)

 

なお、前にも書きましたが、デバッグ環境が無いので-v、-yオプションはほとんど不要です。

 

【BatchGoodリリース予定】 本年中

なお、BCCMakerもbcc32cに対応しています。-Wや-WCを使わずきちんと-tW、-tCオプションを与えればビルド可能です。(注)

注:-Od<最適化しない>は-O1,-O2と同じく通りますが、与えなくても結果は同じです。また不要なオプション(-6、-AT、-H、-k、-Q)を与えてもエラーではなく、警告で見逃してくれます。

 

現在作成中のアプリの開発中、BCCSkeltonのCARDクラス(CARG.h)の引数付きコンストラクター部分に不良が発見されました。

ファイルは既に修復済で本日Vectorにアップしましたので、適宜お差し替え願います。

なお、ダウンロードされた際にはzipファイルの日時が「12月24日15:53」のものか、ご確認をお願いします。

 

さぁ、それでは最後にソースとリソースをコンパイルし、ビルドしましょう。

今までに解説してきた以下のファイルを一つのフォールダーに用意します。

 

【プログラム関係】

CBOARD.h

CMENACE.h

MENACE.h

MENACEProc.h

MENACE.cpp

【リソース関係】

MENACE.rc

ResMENACE.h

ToolBar.bmp

Nought.bmp

Cross.bmp

Vacant.bmp

Icon.ico

 

これらファイルをこのフォールダーでコンパイルしてもよいのですが、同じような名前のファイルが溢れかえると面倒なので、別のフォールダーに移しましょう。仮に上記のソースの入っているフォールダーの下に"Release"というフォールダーを作ったとしましょう。(注1

注1:C++のプログラミングIDEは通常プロジェクトのフォールダーの下にDebugとReleaseというフォールダーを作らせ、Debugでバグ取りして問題が無い状態になったら、コンパイラーに最適化指示をしてReleaseに出力します。しかし、フリーのEmbarcadero C++コマンドラインツール(bcc32c.exeの入ったBCC102一式)にはデバッグツールが無く(注2)、私はソースレベルでデバッグをするので(注3)、最初からReleaseフォールダー一本でやってますが、何か?(汗;)

注2:BCC55の時代にはCUIベースのTurbo Debuggerがオプションで付いてきましたが、既にウィンドウプログラムをやっていたので私は使ったことが無いです。現在EmbarcaderoからフリーのRestDebuggerが提供されていますが、これはインターネット上のオンラインデバッガー(?)のようで、IDE上のヘルプ等がなければ使いこなせないのでは?と思わせる難解さです。私は諦めました。

注3:前に紹介した(Error.hファイルにいれた)ErrorMsg関数はエラーが出た際のエラー番号(メッセージ)を確認するものですが、通常は動作がおかしそうなところにMessageBoxやCSTRクラスのPrint()関数を使って、確認したい変数や状態をモニターしてデバッグします。実際昨日現在開発中のアプリの動作がおかしいので調べてゆき、20年前に犯した誤りを見つけちゃいました。

 

ではコンパイルしてリソースをくっつけ、ビルドするバッチファイルを紹介します。

【MENACE.bat-これが(Current Directoryとしての)Releaseフォールダー内にある想定】

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\MENACE\MENACE.cpp" -tW -O2 -I"c:\Borland\BCCForm" -Xdriver -v
rem まずbcc32c.exeに命令を出します。ファイルパスにスペース文字がありえる場合は括弧("")で括りましょう。オプションの-tWhaWindowsアプリのコンパイル、-O2は速度最適化、-Iは#includeのファイルパスでBCCSkelton.hのあるフォールダーを続けます。-XdriverとはClangコンパイラーへ直接オプションを渡す方法で、ヘルプファイルでは「-v Show commands to run and use verbose output(実行する命令を表示し、詳細を出力する)」という意味だそうです。ここまでで他のファイルに依存したMENACE.cppファイルをコンパイルし、リソース無しのMENACE.exeファイルをReleaseフォールダーに出力します。

"C:\Borland\BCC102\bin\brc32.exe" "C:\Users\ysama\Programing\Borland C++\MENACE\MENACE.rc" "C:\Users\(パス)\MENACE\Release\MENACE.exe"
rem ↑が、brc32を起動し、""で括ったMENACE.rcファイルをコンパイルし、Releaseフォールダー内のMENACE.exeファイルにくっつけろ、という命令になります。brc32の命令は通常これだけで足りるので、オプションはほとんど使わないでしょう。(なお、BCCMakerなどでは、bcc32に-cオプションを与えてobjファイルどまりとし、brc32も-rオプションを付けてresファイル迄のコンパイルをしてから、後でまとめてilink32にすべてリンクさせます。)

del "C:\Users\ysama\Programing\Borland C++\MENACE\MENACE.res"
del "C:\Users\ysama\Programing\Borland C++\MENACE\bcc32c_ver\MENACE.tds"
rem ディスクスペースが貴重であった時代の名残で、使わないファイルはさっさと消去する悲しい性があります。数Tバイトのスト―レージなんて気味が悪いです。

pause

rem これを付けていないとPCがDOS窓をすぐに閉じるので、「うすのろの人間」にはメッセージが読めません。

 

どうでしょう?うまくコンパイルできましたか?↑の例ではbcc32cに「速度を最適化するオプション(-O2)」を与えましたが、これを与えないとexeファイルは192KBの大きさとなります。また気まぐれから「サイズを最適化するオプション(-O1)を与えてビルドしたらexeファイルのサイズは174KBまで小さくなりました。↑の速度最適化コンパイルでは、しかし、(予想を裏切って)exeファイルのサイズが115KBと最小となりました。こんなこともあるので、色々と失敗を繰り返しながら、試行錯誤することは悪いことではないですよね。

 

上手くいかなかった人は、更にバッギングの楽しみが増えますね!うらやましい!ではEnjoy!

【MENACEのおまけ】

MENACEは先手勝利(1)の際、後手勝利(2)の際の最終盤面、それらの一手前のそれぞれ後手敗北(-2)と先手敗北(-1)盤面、および先手の投了(-1)、後手の投了(-2)の盤面を登録することご高尚の通りです。(↑のWindows版では登録数は541ですが、↓にあるように、現在は1090を超えました。)で、またまた夜中に目が覚めて、MENACEDBの登録盤面の4分類の分布はどうなっているのか気になって眠れなくなりました。

夜中に起きてMENACEDBクラスに↓の関数を入れ、MENACDDB.cppの最後にその関数を呼び出して確認しました。

<CMENACEDB.h変更点>

メンバー関数の追加(宣言と定義)

    void Count(int&, int&, int&, int&);    //勝敗結果の集計


//勝敗結果の集計
void CMENACEDB::Count(int& fwin, int& swin, int& flose, int& slose) {

    for(m_Itr1 = m_BoardDB.begin(); m_Itr1 != m_BoardDB.end(); m_Itr1++) {
        switch(m_Itr1->m_Outcome) {
        case 1:
            fwin++;
            break;
        case 2:
            swin++;
            break;
        case -1:
            flose++;
            break;
        case -2:
            slose++;
            break;
        }
    }
}

 

<MENACEDB.cpp変更点>

変数の追加と関数呼び出し

    int fwin = 0, swin = 0, flose = 0, slose = 0;    //先手、後手の勝利数と投了数
 

    cout << endl << "<<現在のデータベースレコードの登録状況>>" << endl;
の次に以下の通り挿入。

    menace.Count(fwin, swin, flose, slose);
    cout << "先手の勝ち:" << fwin << "回" << endl;
    cout << "後手の勝ち:" << swin << "回" << endl;
    cout << "先手の投了:" << flose << "回" << endl;
    cout << "後手の投了:" << slose << "回" << endl;

 

その結果(登録盤面数とその分布)が以下の通りです。

腑に落ちたでしょうか?(なお、投了には一手前の忌避盤面も含まれます。)

最後はMENACEProc.hファイルです。ここにCUI版でつくったゲームプロセスを移植します。

 

//////////////////////////////////////////
// MENACEProc.h
// Copyright (c) 12/11/2021 by BCCSkelton
//////////////////////////////////////////

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

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

    //ツールバー登録-SetHandle(hWnd))
    TBar.SetHandle(GetDlgItem(m_hWnd, IDC_TOOLBAR));
    //ツールバーボタン用カスタムビットマップ追加
    TBar.AddBmp(m_hInstance, MAKEINTRESOURCE(IDI_TOOLBAR), 8);
    //ツールバーボタン追加
    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_SAVEAS;
    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_FM;
    tbb[6].iBitmap = TBar.m_id + 4;
    tbb[6].fsState = TBSTATE_ENABLED;
    tbb[6].fsStyle = TBSTYLE_BUTTON;
    tbb[6].idCommand = IDM_SM;
    tbb[7].iBitmap = TBar.m_id + 5;
    tbb[7].fsState = TBSTATE_ENABLED;
    tbb[7].fsStyle = TBSTYLE_BUTTON;
    tbb[7].idCommand = IDM_AUTO;
    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_ABOUT;
    tbb[10].iBitmap = TBar.m_id + 7;
    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] = {90, -1};
    SBar.SetSection(2, sec);
    //ステータスバー文字列設定
    SBar.SetText(0, "MENACE Ver 1.0");

//(解説:ここまでは基本的にSkeltonWizardが書き出してくれるので、ステータスバーの修正程度の手直しです。)


    //ボタンに張り付け用ビットマップの読み込み
    g_hNought = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_NOUGHT), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
    g_hCross = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_CROSS), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);
    g_hVacant = (HBITMAP)LoadImage(m_hInstance, MAKEINTRESOURCE(IDI_VACANT), IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE);

//(解説:前回思い出したビットマップの廃棄忘れの原因となる〇×空白ビットマップの読み込みです。)

 

    //メンバー変数の初期化
    m_Games = 0;        //連続機械対戦のゲーム数
    m_1st = 0;            //先手の勝数
    m_2nd = 0;            //後手の勝数
    m_Draw = 0;            //引き分け数
//(解説:CUIプログラムでもゲーム数を入れました。Windows版では勝敗結果も記録します。)

 

    //データファイル名の設定と読み込み
    m_FileName = arg.Path();
    m_FileName = m_FileName + "\\MENACE.dat";
    m_Menace.BoardRead(m_FileName.ToChar());

//(解説:CUIゲームと同じく、毎回の勝負で記録したDBデータを蓄積する為のファイルの読み書きです。CARGクラス変数を利用してMENACE.exeのパスを取り、ファイル名MENACE.datをくっつけています。)

 

    //メニュー、メニューボタンを有効化し、〇×ボタンを無効化する

    ChangeMenuStatus(TRUE);

//(解説:メンバー関数に対戦関連メニューとボタンを対戦中は無効にする処理を入れました。)


    return TRUE;
}

bool CMyWnd::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_SAVEAS:
                    lptip->lpszText = "データコピーを保存";
                    break;
                case IDM_EXIT:
                    lptip->lpszText = "終了";
                    break;
                case IDM_FM:
                    lptip->lpszText = "先手";
                    break;
                case IDM_SM:
                    lptip->lpszText = "後手";
                    break;
                case IDM_AUTO:
                    lptip->lpszText = "自動対局";
                    break;
                case IDM_ABOUT:
                    lptip->lpszText = "MENACEについて";
                    break;
                case IDM_VERSION:
                    lptip->lpszText = "バージョン情報";
                    break;
            }
    }
}

//(解説:定番のツールチップ処理です。)


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

    if(MessageBox(m_hWnd, "終了しますか", "終了確認",
    MB_YESNO | MB_ICONINFORMATION) == IDYES) {
        //データを保存
        m_Menace.BoardWrite(m_FileName.ToChar());

//(解説:データの書き出しを最後に行います。)

        DeleteObject((HGDIOBJ)g_hNought);
        DeleteObject((HGDIOBJ)g_hCross);
        DeleteObject((HGDIOBJ)g_hVacant);

//(解説:これがブログを書いていて思い出したビットマップ廃棄の処理です。)

        //処理をするとDestroyWindow、PostQuitMessageが呼ばれる
        return TRUE;
    }
    else
        //そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
        return FALSE;
}

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

    PostQuitMessage(0);
    return TRUE;
}

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

    if(m_You == FIRST)
        SendItemMsg(IDC_0, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_0, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(0);
    return TRUE;
}

bool CMyWnd::On1() {

    if(m_You == FIRST)
        SendItemMsg(IDC_1, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_1, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(1);
    return TRUE;
}

bool CMyWnd::On2() {

    if(m_You == FIRST)
        SendItemMsg(IDC_2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_2, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(2);
    return TRUE;
}

bool CMyWnd::On3() {

    if(m_You == FIRST)
        SendItemMsg(IDC_3, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_3, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(3);
    return TRUE;
}

bool CMyWnd::On4() {

    if(m_You == FIRST)
        SendItemMsg(IDC_4, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_4, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(4);
    return TRUE;
}

bool CMyWnd::On5() {

    if(m_You == FIRST)
        SendItemMsg(IDC_5, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_5, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(5);
    return TRUE;
}

bool CMyWnd::On6() {

    if(m_You == FIRST)
        SendItemMsg(IDC_6, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_6, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(6);
    return TRUE;
}

bool CMyWnd::On7() {

    if(m_You == FIRST)
        SendItemMsg(IDC_7, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_7, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(7);
    return TRUE;
}

bool CMyWnd::On8() {

    if(m_You == FIRST)
        SendItemMsg(IDC_8, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
    else if(m_You == SECOND)
        SendItemMsg(IDC_8, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
    Turn(8);
    return TRUE;
}

//(解説:ダイアログの盤面に見立てた9つのプッシュボタンを、(1)まず空白で初期化し、(2)先手(FIRST)の場合〇のビットマップ、後手(SECOND)の場合×のビットマップを表示し、毎回の打手と手の評価をするTurn()関数に飛びます。)


bool CMyWnd::OnOpen() {

    char* fn = cmndlg.GetFileName(m_hWnd, "DATファイル(*.dat)\0*.dat\0\0", TRUE, "dat", ".", "MENACEデータファイルを開く");
    if(!fn) {
        MessageBox(m_hWnd, "処理がキャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    else
        m_Menace.BoardRead(fn);
    return TRUE;
}

//(解説:自分の盤面のデータは自動で読書きしますが、任意のDBデータを読むことも可能にしました。)


bool CMyWnd::OnSave() {

    //データを保存
    m_Menace.BoardWrite(m_FileName.ToChar());
    return TRUE;
}

//(解説:試合中等、任意で盤面を保存できます。)


bool CMyWnd::OnSaveas() {

    char* fn = cmndlg.GetFileName(m_hWnd, "DATファイル(*.dat)\0*.dat\0\0", FALSE, "dat", ".", "MENACEデータファイル(コピー)の保存");
    if(!fn) {
        MessageBox(m_hWnd, "処理がキャンセルされました", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    else {
        //データを保存
        m_Menace.BoardWrite(fn);
    }
    return TRUE;

}

//(解説:自分の盤面DBデータを人にあげられるように別途保存できるようにしました。)


bool CMyWnd::OnExit() {

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

//(解説:終了処理です。)


bool CMyWnd::OnFm() {

    m_You = FIRST;
    Init();
    ChangeMenuStatus(FALSE);
    return TRUE;
}

//(解説:人間対PCの対戦で、メニューで先手を取った場合です。自分を先手と定義して、ゲームの初期化を行い、メニューツールバーボタンを無効化します。)


bool CMyWnd::OnSm() {

    m_You = SECOND;
    Init();
    ChangeMenuStatus(FALSE);
    Turn(0);
    return TRUE;
}

//(解説:後手の場合です。基本的にやることは変わりませんが、PCに第1手を取らせるためにTurn()関数を呼び出します。(この引数は無視されるのでダミーです。)


bool CMyWnd::OnAuto() {

    m_You = 3;
    int n = inputdlg.DoModal(m_hWnd, "IDD_INPUT", inputdlgProc);
    for(int i = 0; i < n; i++) {    //100、500、1000、10000連戦
        Init();
        ChangeMenuStatus(FALSE);
        while(!Turn(0));            //機械対戦に引数は関係ない
    }
    return TRUE;
}

//(解説:PC対PCの自動対戦です。インプットダイアログで100 - 1000連戦を選択してループで実行します。コメントにあるように当初10000回のオプションも考えてましたが、占有時間が長すぎてWindowsにおおられるので(別スレッドにするまでもないと思い)1000回までとしました。)


bool CMyWnd::OnAbout() {

    infodlg.DoModal(m_hWnd, "IDD_INFO", infodlgProc);
    return TRUE;
}

//(解説:これは元々MessageBoxで表示していましたが、専用のダイアログを作ってみました。)


bool CMyWnd::OnVersion() {

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

//(解説:バージョン表示です。)


///////////////////
//ユーザー定義関数
///////////////////
//ゲーム初期化関数
void CMyWnd::Init() {

    m_Menace.Init();
    m_Result = 0;        //ゲーム結果(未了0、先手勝利1、後手勝利2、引き分け3)
    m_Times = 0;        //各対戦の手数カウンター
    for(int i = 0; i < 9; i++)
        SendItemMsg(IDC_0 + i, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hVacant);

}

//(解説:ゲーム開始の際に、メナスクラスの初期化、対戦結果、打手数の初期化と盤面を空白ビットマップにします。)


//ゲーム処理関数
int CMyWnd::Turn(int pos) {

    for(int i = 0; !m_Result && i < 2; i++) {    //対戦未了なら先手、後手が一回づつ手を打つ

//(解説:書かれている様に1回のTurnで先手後手が一回づつ手を打ちます。)

        switch(m_You) {
        case FIRST:        //Playerが先手
            if(!(m_Times % 2))        //先手番
                m_Menace.MakeMove(pos, FIRST);
            else {                    //後手番
                if(!m_Menace.AutoMove(SECOND)) {    //打つ手がない場合
                    m_Result = m_Menace.WhoWon();    //先手の勝利
                }
                else {
                    //AutoMoveの際の〇×の表示
                    int move = m_Menace.GetLastMove();    //m_Board.m_LastMoveの最後の手の位置を取得
                    SendItemMsg(IDC_0 + move, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
                }
            }
            break;

//(解説:CUIの際には盤面をm_Pos配列そのものを読んで表示していましたが、今回は9桝のプッシュボタンのビットマップを変更するので、人間の手は自分で、PCはAutoMoveに応じてビットマップを変えます。この時にGetLastMove関数が生きてきます。)

        case SECOND:    //Playerが後手
            if(m_Times % 2) {        //先手番
                if(!m_Menace.AutoMove(FIRST)) {        //打つ手がない場合
                    m_Result = m_Menace.WhoWon();    //相手の勝利
                }
                else {
                    //AutoMoveの際の〇×の表示
                    int move = m_Menace.GetLastMove();    //m_Board.m_LastMoveの最後の手の位置を取得
                    SendItemMsg(IDC_0 + move, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
                }
            }
            else                    //後手番
                m_Menace.MakeMove(pos, SECOND);
            break;

//(解説:同じことですが、PCのAutoMove()が先行するので、人間がメニューで後手を選んだ時にTurn()に飛ばしてやらないとずーっと何もしない状態が続いてしまいます。)

        case 3:            //機械対戦
            if(!(m_Times % 2)) {    //先手番
                if(!m_Menace.AutoMove(FIRST)) {        //打つ手がない場合
                    m_Result = m_Menace.WhoWon();    //相手の勝利
                }
                else {
                    int move = m_Menace.GetLastMove();    //m_Board.m_LastMoveの最後の手の位置を取得
                    SendItemMsg(IDC_0 + move, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hNought);
                }
            }
            else {                    //後手番
                if(!m_Menace.AutoMove(SECOND)) {    //打つ手がない場合
                    m_Result = m_Menace.WhoWon();    //相手の勝利
                }
                else {
                    int move = m_Menace.GetLastMove();    //m_Board.m_LastMoveの最後の手の位置を取得
                    SendItemMsg(IDC_0 + move, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)g_hCross);
                }
            }
            break;

//(解説:これはPC同士がAutoMove()でやりあうだけなのでループになります。Windowsは割り込みOSなので、長い時間を占有する際にはスレッドを別にすることが必要ですが、1000回程度はこなせますのでそのままにしています。)

        }
        m_Times++;
        //第5手目(プログラム上はゼロベースなので4手目)以降勝敗が付いていないならば
        if(!m_Result && m_Times > 4)
            m_Result = m_Menace.IsOver();

//(解説:CUIでもやりましたが、投了があるので"!m_Result"条件が必要です。)

    }
    if(m_Result)
        WhenOver();        //ゲーム終了処理
    return m_Result;    //ゲーム結果を返す
}

//(解説:自動対戦の際のループはメニューの自動対戦の

//while(!Turn(0));            //機械対戦に引数は関係ない

//に対応します。それゆえ、m_Resultを返すことが必要です。)
 

void CMyWnd::WhenOver() {
    
    m_Games++;
    switch(m_Result) {    //先手勝利(-1)、後手勝利(2)、先手敗北(-1)、後手敗北(-2)
    case FIRST:
        SBar.SetText(1, "先手の勝ち");
        SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)"先手の勝ち");    //ToolTipをつける
        m_1st++;
        m_Menace.Register();        //終了時盤面の記録
        m_Menace.PushBack();        //一手前の相手の盤面(敗北因)に戻す
        m_Menace.Register();        //敗北盤面の記録
        break;
    case SECOND:
        SBar.SetText(1, "後手の勝ち");
        SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)"後手の勝ち");    //ToolTipをつける
        m_2nd++;
        m_Menace.Register();        //終了時盤面の記録
        m_Menace.PushBack();        //一手前の相手の盤面(敗北因)に戻す
        m_Menace.Register();        //敗北盤面の記録
        break;
    case 3:
        SBar.SetText(1, "引き分け");
        SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)"引き分け");    //ToolTipをつける
        m_Draw++;
        break;
    case -1:
        SBar.SetText(1, "先手の負け");
        SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)"先手の負け");    //ToolTipをつける
        m_2nd++;
        m_Menace.Register();        //終了時一手前、投了時盤面の記録
        break;
    case -2:
        SBar.SetText(1, "後手の負け");
        SBar.SendMsg(SB_SETTIPTEXT, 1, (LPARAM)"後手の負け");    //ToolTipをつける
        m_1st++;
        m_Menace.Register();        //終了時一手前、投了時盤面の記録
        break;
    }
//(解説:勝敗決定後の記録処理とDB処理で、ここはCUIと全く同じです。勝利した際には勝利盤面(プラス)と一手前の忌避盤面(マイナス)を記録し、投了の場合は投了盤面(マイナス)を記録します。→従って、マイナス盤面の方が推論により多くなると思われます。)

    ChangeMenuStatus(TRUE);            //ゲームメニューと先手、後手、機械対戦のボタンを有効化する
}

void CMyWnd::ChangeMenuStatus(bool EnableFlag) {

    HMENU hMenu = GetMenu(MENACE.GetHandle());    //メニューハンドルの取得
    //メニューバー
    EnableMenuItem(hMenu, 1, MF_BYPOSITION | (EnableFlag ? MF_ENABLED : MF_GRAYED));
    DrawMenuBar(MENACE.GetHandle());    //Re-draw
    //ツールバー(MAKELONGマクロは16bit整数2つをunsigned 32bit整数にする)
    SendMessage(TBar.GetHandle(), TB_ENABLEBUTTON, IDM_FM, MAKELONG(EnableFlag, 0));
    SendMessage(TBar.GetHandle(), TB_ENABLEBUTTON, IDM_SM, MAKELONG(EnableFlag, 0));
    SendMessage(TBar.GetHandle(), TB_ENABLEBUTTON, IDM_AUTO, MAKELONG(EnableFlag, 0));
    //〇×ボタンの有効、無効化
    for(int i = 0; i < 9; i++)
        EnableWindow(GetDlgItem(m_hWnd, IDC_0 + i), (EnableFlag ? FALSE : TRUE));
}
//(解説:メニューの対戦とツールバーボタンの有効、無効を切り替えます盤面のプッシュボタンも非対戦中は無効にします。)
 

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

    SendItemMsg(IDC_GAMES, CB_INSERTSTRING, 0, (LPARAM)"100");
    SendItemMsg(IDC_GAMES, CB_INSERTSTRING, 1, (LPARAM)"500");
    SendItemMsg(IDC_GAMES, CB_INSERTSTRING, 2, (LPARAM)"1000");
    return TRUE;
}

//(解説:前に書いたように現在10,000回はやめています。1,000回を10回やってください。)

bool INPUTDLG::OnOk() {

    int n = SendItemMsg(IDC_GAMES, CB_GETCURSEL, 0, 0);
    if(n == CB_ERR) {
        MessageBox(m_hWnd, "回数が選択されていません", "エラー", MB_OK | MB_ICONERROR);
        return FALSE;
    }
    switch(n) {
    case 0:
        n = 100;
        break;
    case 1:
        n = 500;
        break;
    case 2:
        n = 1000;
        break;
    }
    EndModal(n);
    return TRUE;
}

bool INPUTDLG::OnCancel() {

    EndModal(FALSE);
    return TRUE;
}

//(解説:キャンセルのプッシュボタンが無いのに関数を入れているのはEscキーによるキャンセルを想定してです。)
 

///////////////////////////////
//ユーザーダイアログの関数定義
//Infoダイアログ
///////////////////////////////
bool INFODLG::OnInit(WPARAM wParam, LPARAM lParam) {

    //MENACEについての情報開示
    CSTR num(MENACE.m_Menace.DBSize());
    SendItemMsg(IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)num.ToChar());
    num = MENACE.m_Games;
    SendItemMsg(IDC_EDIT2, WM_SETTEXT, 0, (LPARAM)num.ToChar());
    num = MENACE.m_1st;
    SendItemMsg(IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)num.ToChar());
    num = MENACE.m_2nd;
    SendItemMsg(IDC_EDIT4, WM_SETTEXT, 0, (LPARAM)num.ToChar());
    num = MENACE.m_Draw;
    SendItemMsg(IDC_EDIT5, WM_SETTEXT, 0, (LPARAM)num.ToChar());
    return TRUE;
}
//(解説:単にダイアログに数値を表示するだけです。)

bool INFODLG::OnOk() {

    EndModal(TRUE);
    return TRUE;
}

bool INFODLG::OnCancel() {

    EndModal(FALSE);
    return TRUE;
}

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

    EndModal(TRUE);
    return TRUE;
}

bool VERSIONDLG::OnCancel() {

    EndModal(FALSE);
    return TRUE;
}

 

これですべてのWindows版MENACEのファイルを紹介しました。次回はこれをコンパイルする方法(bcc55ではコンパイルできませんのでご注意を!)とMENACEDB付加した新しい機能を紹介しましょう。

 

リソースを消化しましたので、次はCPPファイルとHファイルを解説します。

 

【MENACE.cpp】

//////////////////////////////////////////
// MENACE.cpp
//Copyright (c) 12/11/2021 by BCCSkelton
//////////////////////////////////////////
#include    "MENACE.h"
#include    "MENACEProc.h"

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

    //2重起動防止
    if(!MENACE.IsOnlyOne()) {
        //ここに2重起動時の処理を書く(下記は1例)
        HWND hWnd = FindWindow("MainWnd", "MENACE");
        if(IsIconic(hWnd))
            ShowWindow(hWnd, SW_RESTORE);
        SetForegroundWindow(hWnd);
        return 0L;
    }

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

    //メッセージループに入る
    return MENACE.Loop();
}
 

正直解説するところがありません。いつもながらのモードレスダイアログベースのcppファイルです。(二重起動は禁止)

 

【MENACE.hファイル】

こちらの方はやや(解説:。)が必要です。

 

//////////////////////////////////////////
// MENACE.h
// Copyright (c) 12/11/2021 by BCCSkelton
//////////////////////////////////////////
//BCCSkeltonのヘッダー-これに必要なヘッダーが入っている
#include    "BCCSkelton.h"
//リソースIDのヘッダー
#include    "ResMENACE.h"
//CMENACEのヘッダー
#include    "CMENACE.h"
(解説:既にCUIプログラムでテストしたCMENACEの宣言、定義ファイルです。これを読み込むとBOARD構造体、CBOARDクラスおよびSTL関連が使えるようになります。)
 

/////////////////////////////////////////////////////////////////////
//CMyWndクラスをCDLGクラスから派生させ、メッセージ用の関数を宣言する
/////////////////////////////////////////////////////////////////////
class CMyWnd : public CDLG
{
public:    //以下はコールバック関数マクロと関連している
    //2重起動防止用のMutex用ID名称
    CMyWnd(char* UName) : CDLG(UName) {}
    //メンバー変数
    CMENACE m_Menace;        //MENACE本体
    int m_You;                //Player(先手-1、後手-2、機械対戦-3)
    int m_Result;            //ゲーム結果(未了0、先手勝利1、後手勝利2、引き分け3)
    int m_Times;            //各対戦の手数カウンター
    int m_Games;            //連続機械対戦のゲーム数
    int m_1st;                //先手の勝数
    int m_2nd;                //後手の勝数
    int m_Draw;                //引き分け数
    CSTR m_FileName;        //データファイル名
(解説:。CUIプログラムでmain関数内にあったものをIDD_MENACEダイアログのクラス(CMyWnd)へ移植します。順にCMENACE本体(インスタンス)、先手・後手・機械対戦選択フラグ、ゲーム勝敗結果フラグ、手数カウンター、対戦数カウンター、先手勝利・後手勝利・引き分けカウンターとMENACE.datというデータファイルのファイルパス、名の記録用CSTR変数です。)

 

    //メニュー項目、ダイアログコントロール関連
    bool On0();
    bool On1();
    bool On2();
    bool On3();
    bool On4();
    bool On5();
    bool On6();
    bool On7();
    bool On8();

(解説:0 - 8は〇×ゲーム盤の打手用ボタンです。)
    bool OnOpen();
    bool OnSave();
    bool OnSaveas();
    bool OnExit();
    bool OnFm();
    bool OnSm();
    bool OnAuto();
    bool OnAbout();
    bool OnVersion();
(解説:メニューアイテムです。)

    //ウィンドウメッセージ関連
    bool OnInit(WPARAM, LPARAM);
    bool OnNotify(WPARAM, LPARAM);
    bool OnClose(WPARAM, LPARAM);
    bool OnDestroy(WPARAM, LPARAM);
    //ユーザー定義関数
    void Init();                    //ゲーム初期化関数
    int Turn(int);                    //ゲーム処理関数
    void WhenOver();                //ゲーム終了関数
    void ChangeMenuStatus(bool);    //メニュー関係有効無効化処理
(解説:ユーザー定義関数として、順に対戦前の初期化、一手ごとに通過する打手処理、勝負判定処理の関数、勝敗が決着した際の処理関数、メニューとツールバーボタンを対戦中無効にする関数です。)

};

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

BEGIN_MODELESSDLGMSG(ModelessProc, MENACE)    //コールバック関数名は主ウィンドウの場合ModelessProcにしている
    //メニュー項目、ダイアログコントロール関連
    ON_COMMAND(MENACE, IDC_0, On0())
    ON_COMMAND(MENACE, IDC_1, On1())
    ON_COMMAND(MENACE, IDC_2, On2())
    ON_COMMAND(MENACE, IDC_3, On3())
    ON_COMMAND(MENACE, IDC_4, On4())
    ON_COMMAND(MENACE, IDC_5, On5())
    ON_COMMAND(MENACE, IDC_6, On6())
    ON_COMMAND(MENACE, IDC_7, On7())
    ON_COMMAND(MENACE, IDC_8, On8())
    ON_COMMAND(MENACE, IDM_OPEN, OnOpen())
    ON_COMMAND(MENACE, IDM_SAVE, OnSave())
    ON_COMMAND(MENACE, IDM_SAVEAS, OnSaveas())
    ON_COMMAND(MENACE, IDM_EXIT, OnExit())
    ON_COMMAND(MENACE, IDM_FM, OnFm())
    ON_COMMAND(MENACE, IDM_SM, OnSm())
    ON_COMMAND(MENACE, IDM_AUTO, OnAuto())
    ON_COMMAND(MENACE, IDM_ABOUT, OnAbout())
    ON_COMMAND(MENACE, IDM_VERSION, OnVersion())
    //ウィンドウメッセージ関連
    //自動的にダイアログ作成時にOnInit()、終了時にOnClose()を呼びます
    ON_DESTROY(MENACE)
END_DLGMSG
(解説:マクロでウィンドウコールバック関数に展開される部分です。)
///////////////////////////////////////////
// CDLGクラスからINPUTDLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class INPUTDLG : public CDLG {
public:
    //コントロール関連
    bool OnInit(WPARAM, LPARAM);
    bool OnOk();
    bool OnCancel();
};
(解説:機械対戦(自動対戦)の際の何回勝負にするかの入力ダイアログです。)
////////////////////////////////////////////////////////////////////////////
// INPUTDLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
INPUTDLG inputdlg;

BEGIN_MODALDLGMSG(inputdlgProc, inputdlg)    //第1引数がコールバック関数の名前
    ON_COMMAND(inputdlg, IDOK, OnOk())
    ON_COMMAND(inputdlg, IDCANCEL, OnCancel())
END_DLGMSG

///////////////////////////////////////////
// CDLGクラスからINFODLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class INFODLG : public CDLG {
public:
    //コントロール関連
    bool OnInit(WPARAM, LPARAM);
    bool OnOk();
    bool OnCancel();
};
(解説:現在のMENACE.datに記録され、毎回DBに読み込まれる盤面データの数、先手勝利、後手勝利、引き分けの毎回使用時の集計です。再shMessageBoxで表示していたのですが、専用ダイアログを作ってあげました。なお、iniファイルをつかって累計を記録することも考えましたが、最後は引き分けばかりで無駄なので止めました。)
////////////////////////////////////////////////////////////////////////////
// INFODLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
INFODLG infodlg;

BEGIN_MODALDLGMSG(infodlgProc, infodlg)    //第1引数がコールバック関数の名前
    ON_COMMAND(infodlg, IDOK, OnOk())
    ON_COMMAND(infodlg, IDCANCEL, OnCancel())
END_DLGMSG

///////////////////////////////////////////
// CDLGクラスからVERSIONDLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class VERSIONDLG : public CDLG {
public:
    //コントロール関連
    bool OnOk();
    bool OnCancel();
};
(解説:単なるバージョン表示ダイアログです。)
////////////////////////////////////////////////////////////////////////////
// VERSIONDLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
VERSIONDLG versiondlg;

BEGIN_MODALDLGMSG(versiondlgProc, versiondlg)    //第1引数がコールバック関数の名前
    ON_COMMAND(versiondlg, IDOK, OnOk())
    ON_COMMAND(versiondlg, IDCANCEL, OnCancel())
END_DLGMSG

///////////////////
//ツールバーの作成
///////////////////
CTBAR TBar;

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

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

////////////////////////
//CARGインスタンスの作成
////////////////////////
CARG arg;
(解説:ツールバーやステータスバー、コモンダイアログは説明不要でしょう。CARGクラスは、Windowsプログラムが起動した際に、昔のCのプログラムのint argc、char** argvをarg.c()、arg.v()で実現するツールクラスです。)
///////////////////////////////
//ボタン貼り付け用ビットマップ
///////////////////////////////
HBITMAP g_hNought;
HBITMAP g_hCross;
HBITMAP g_hVacant;

(解説:。OnCreate()でリソースのビットマップを読み込んでおきますが、そのハンドルです。

あっ!!!OnClose()で廃棄するのを忘れていました!!!

ブログのおかげでメモリーリークが防げました。差し替え版を本日アップしました。)

 

後はProc.hですね。年内に完了しそうでほっとします。

 

昨日はまたまたbcc32cにはまり、exhaustしました。(ヤレヤレ)しかし、今日も朝2時から目がさえて、色々なオプションをCommand(BCCSkeltonアプリ)を使って試し、「これは時間がかかるな」ということから、先ずはMENACEを手じまいしようということで、ササっとプログラム説明します。

 

BCCSkelton流に先ずはリソースから。

 

1.イメージ

MENACEのWindows版には次のイメージファイルが必要です。

(1)〇、×と空白

私はNought.bmp、Cross.bmp、Vacant.bmpというビットマップを作りました。これはMSPaintでテキトーに手書き風味の〇、×を書き、それを64 x 64にトリムしてセーブします。(EZImageの裏ワザとして、このビットマップを読み込むと規定外サイズですが、エラー無く編集、上書きできます。)

(2)ツールバービットマップ

私はTBEditorで、ToolBar.bmpという16 x 15(16x16のビットマップだとTBEditorに「規定外」と怒られますので、MSPaintでトリムしてください)を、メニューの"データを開く"(IDM_OPEN)、"データコピーを保存"(IDM_SAVEAS)、"終了"(IDM_EXIT)、"先手"(IDM_FM)、"後手"、(IDM_SM)、"自動対局"(IDM_AUTO)、"MENACEについて"(IDM_ABOUT)、"バージョン情報"(IDM_VERSION)の8つ用意しました。(定番BMPはSampleBMPフォールダーにありますから漁ってください。)

(3)アイコン

これは32x32の〇×ゲームの絵のアイコンをIcon.icoとしてEZImageでつくりました。

 

2.RCファイル

上記1をつかってつくったRCファイルがこちら(↓)です。赤字部分には気を付けてください。

//-----------------------------------------
//             BCCForm Ver 2.41
//    An Easy Resource Editor for BCC
//  Copyright (c) February 2002 by ysama
//-----------------------------------------
#include    "ResMENACE.h"
#define        TBSTYLE_TOOLTIPS    0x0100
#define        SBT_TOOLTIPS        0x0800


//----------------------------------
// ダイアログ (IDD_MENACE)
//----------------------------------
IDD_MENACE DIALOG DISCARDABLE 0, 0, 174, 207
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_DLGFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT | DS_CENTER
CAPTION "MENACE"
MENU IDM_FORM_MENU
FONT 8, "MS 明朝"
{
 CONTROL 0, IDC_0, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 3, 24, 54, 54
 CONTROL 0, IDC_1, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 60, 24, 54, 54
 CONTROL 0, IDC_2, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 117, 24, 54, 54
 CONTROL 0, IDC_3, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 3, 81, 54, 54
 CONTROL 0, IDC_4, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 60, 81, 54, 54
 CONTROL 0, IDC_5, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 117, 81, 54, 54
 CONTROL 0, IDC_6, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 3, 138, 54, 54
 CONTROL 0, IDC_7, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 60, 138, 54, 54
 CONTROL 0, IDC_8, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP, 117, 138, 54, 54
 CONTROL "", IDC_TOOLBAR, "TOOLBARWINDOW32", WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS | TBSTYLE_SEP | WS_BORDER, 0, 0, 174, 19
 CONTROL "", IDC_STATUSBAR, "MSCTLS_STATUSBAR32", WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS | CCS_TOP | CCS_NOMOVEY, 0, 195, 174, 12
}

//----------------------------------
// ダイアログ (IDD_INPUT)
//----------------------------------
IDD_INPUT DIALOG DISCARDABLE 0, 0, 169, 72
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION "対戦回数入力"
FONT 9, "MS 明朝"
{
 CONTROL IDI_ICON, 0, "STATIC", SS_SUNKEN | SS_ICON | WS_CHILD | WS_VISIBLE, 2, 2, 32, 32
 CONTROL "対戦回数を入力してください", 0, "STATIC", SS_CENTER | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 42, 2, 120, 13
 CONTROL "", IDC_GAMES, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_SORT, 42, 20, 96, 96
 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 128, 53, 32, 16
}

//----------------------------------
// ダイアログ (IDD_INFO)
//----------------------------------
IDD_INFO DIALOG DISCARDABLE 0, 0, 141, 108
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION "MENACEについて"
FONT 9, "MS 明朝"
{
 CONTROL IDI_ICON, 0, "STATIC", SS_SUNKEN | SS_ICON | WS_CHILD | WS_VISIBLE, 2, 2, 32, 32
 CONTROL "登録盤面数", 0, "STATIC", SS_LEFT | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 26, 8, 68, 12
 CONTROL "今回のゲーム数", 0, "STATIC", SS_LEFT | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 2, 26, 92, 12
 CONTROL "今回の先手勝利数", 0, "STATIC", SS_LEFT | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 2, 42, 92, 12
 CONTROL "今回の後手勝利数", 0, "STATIC", SS_LEFT | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 2, 58, 92, 12
 CONTROL "今回の引き分け数", 0, "STATIC", SS_LEFT | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 2, 74, 92, 12
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_READONLY | ES_RIGHT, 98, 8, 40, 13, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT2, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_READONLY | ES_RIGHT, 98, 26, 40, 13, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT3, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_READONLY | ES_RIGHT, 98, 42, 40, 13, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT4, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_READONLY | ES_RIGHT, 98, 58, 40, 13, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT5, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | ES_READONLY | ES_RIGHT, 98, 74, 40, 13, WS_EX_CLIENTEDGE
 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 98, 90, 40, 16
}

//----------------------------------
// ダイアログ (IDD_VERSION)
//----------------------------------
IDD_VERSION DIALOG DISCARDABLE 0, 0, 160, 40
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION "バージョン情報"
FONT 9, "Times New Roman"
{
 CONTROL IDI_ICON, 0, "STATIC", SS_SUNKEN | SS_ICON | WS_CHILD | WS_VISIBLE, 12, 10, 32, 32
 CONTROL "MENACE Version 1.0\nCopyright 2021 by Ysama", 0, "STATIC", SS_CENTER | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 42, 8, 80, 24
 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 14, 20, 12
}

//----------------------------------
// ダイアログで使用するメニュー
//----------------------------------
IDM_FORM_MENU MENU DISCARDABLE
{
    POPUP "ファイル(&F)"
    {
        MENUITEM "データを開く(&O)", IDM_OPEN
        MENUITEM "データの保存(&S)", IDM_SAVE
        MENUITEM "データコピーを保存(&A)", IDM_SAVEAS
        MENUITEM SEPARATOR
        MENUITEM "終了(&X)", IDM_EXIT
    }
    POPUP "対戦(&G)"
    {
        MENUITEM "先手(&F)", IDM_FM
        MENUITEM "後手(&S)", IDM_SM
        MENUITEM "自動対局(&A)", IDM_AUTO
    }
    POPUP "ヘルプ(&H)"
    {
        MENUITEM "MENACEについて(&A)", IDM_ABOUT
        MENUITEM "バージョン情報(&V)", IDM_VERSION
    }
}

//--------------------------
// イメージ(IDI_NOUGHT)
//--------------------------
IDI_NOUGHT    BITMAP    DISCARDABLE    "C:\Users\(パス)\MENACE\Nought.bmp"

//--------------------------
// イメージ(IDI_CROSS)
//--------------------------
IDI_CROSS    BITMAP    DISCARDABLE    "C:\Users\(パス)\MENACE\Cross.bmp"

//--------------------------
// イメージ(IDI_VACANT)
//--------------------------
IDI_VACANT    BITMAP    DISCARDABLE    "C:\Users\(パス)\MENACE\Vacant.bmp"

//--------------------------
// イメージ(IDI_TOOLBAR)
//--------------------------
IDI_TOOLBAR    BITMAP    DISCARDABLE    "C:\Users\(パス)\MENACE\ToolBar.bmp"

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

 

3.Res.hファイル

値は(かぶらなければ)変わっても構いません。

//-----------------------------------------
//             BCCForm Ver 2.41
//   Header File for Resource Script File
//   Copyright (c) February 2002 by ysama
//-----------------------------------------
#define        TBSTYLE_TOOLTIPS    0x0100    //ツールバー用スタイル
#define        SBT_TOOLTIPS        0x0800    //スティタスバー用スタイル
//---------------------
//  ダイアログリソース
//---------------------
// ダイアログ IDD_MENACE
#define    IDC_0            100
#define    IDC_1            101
#define    IDC_2            102
#define    IDC_3            103
#define    IDC_4            104
#define    IDC_5            105
#define    IDC_6            106
#define    IDC_7            107
#define    IDC_8            108
#define    IDC_TOOLBAR        109
#define    IDC_STATUSBAR    110
// ダイアログ IDD_INPUT
#define    IDC_GAMES        120
// ダイアログ IDD_INFO
#define    IDC_EDIT1        130
#define    IDC_EDIT2        131
#define    IDC_EDIT3        132
#define    IDC_EDIT4        133
#define    IDC_EDIT5        134
//---------------------
//  メニューリソース
//---------------------
// メニュー IDM_FORM_MENU
#define    IDM_OPEN        200
#define    IDM_SAVE        201
#define    IDM_SAVEAS        202
#define    IDM_EXIT        203
#define    IDM_FM            204
#define    IDM_SM            205
#define    IDM_AUTO        206
#define    IDM_ABOUT        207
#define    IDM_VERSION        208

//---------------------
//  イメージリソース
//---------------------
#define    IDI_NOUGHT        300
#define    IDI_CROSS        400
#define    IDI_VACANT        500
#define    IDI_TOOLBAR        600
#define    IDI_ICON        700

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

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

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

4.SkeltonWizardを使う

こうして作成したMENACE.rcファイルをSkeltonWizardに通します。今日は手抜きで画像無しです。

(1)第1画面

ウィンドウタイプはダイアログです。IDD_MENACEを選んでください。

ツールバー、ステータスバー、コモンダイアログにチェックを入れます。

メニュー(私の場合はIDM_FORM_MENU)、アイコン(IDI_ICON)を選択します。

(2)第2画面

「コモンコントロールのスタンダードビットマップ」チェックボックスを外し、プルダウンメニューでIDI?TOOLBARを選択します。

適宜<SEPARATOR<を挟みながら(メニューアイテムを選択した状態でダブルクリックする)ツールバーボタンに選択したメニューアイテムとビットマップボタンを連携付けます。ステータスバーは枠の数を2とし、枠内文字列は「"MENACE Ver 1.0",""」としておきます。

(3)第3画面

メッセージはWM_DESTROYを、音とロールやメニューは以下が必要ですが、(とりあえず)すべて入れて後で削除していただいて構いません。

IDC_0からIDC_8までは〇×ゲームの盤、後はメニューアイテムです。

    ON_COMMAND(MENACE, IDC_0, On0())
    ON_COMMAND(MENACE, IDC_1, On1())
    ON_COMMAND(MENACE, IDC_2, On2())
    ON_COMMAND(MENACE, IDC_3, On3())
    ON_COMMAND(MENACE, IDC_4, On4())
    ON_COMMAND(MENACE, IDC_5, On5())
    ON_COMMAND(MENACE, IDC_6, On6())
    ON_COMMAND(MENACE, IDC_7, On7())
    ON_COMMAND(MENACE, IDC_8, On8())
    ON_COMMAND(MENACE, IDM_OPEN, OnOpen())
    ON_COMMAND(MENACE, IDM_SAVE, OnSave())
    ON_COMMAND(MENACE, IDM_SAVEAS, OnSaveas())
    ON_COMMAND(MENACE, IDM_EXIT, OnExit())
    ON_COMMAND(MENACE, IDM_FM, OnFm())
    ON_COMMAND(MENACE, IDM_SM, OnSm())
    ON_COMMAND(MENACE, IDM_AUTO, OnAuto())
    ON_COMMAND(MENACE, IDM_ABOUT, OnAbout())
    ON_COMMAND(MENACE, IDM_VERSION, OnVersion())
(3)第4画面

何もしないで「完了」で結構です。

 

なお、最終的にプログラムはbcc32.exeでコンパイルするBCCDeveloperのmakeではなく、bcc32c.exeをバッチファイルでコンパイルしますが、この段階ではbcc32.exeでコンパイルするBCCDeveloperのmakeを使って「がらんどうMENACE.exe」を楽しんでください。その際、

・BCCDeveloperの「"MENACE.rc」となっているリソースファイルから「"」を取ってやる。

・MENACE.cppの二重起動処理を決める。(禁止にするか、許可するか)

に注意してください。この段階でエラーが出れば、早期に潰して、完動するスケルトンにしておいてください。

 

ここのところMENACEにかまけていたのですが、後はウィンドウズ版の解説をするだけでよいので、またネタを考えていました。

・最初はCOMの続きで、矢張り動画表示コントロールをやりたいな、

と思っていたところ、

・となるとBCC102でコンパイルすることになるが、ここのところマニュアルでバッチファイルを書いているので、何かツールが欲しいな、

と思い、簡単なツールを作ろうかと思ってオプションを与える際に(未だbcc32c.exeのオプションを理解していないので)また面倒だな、と考えていて、

・DLLを作成する場合は-WDでは問題があるのか?-tDなのか?と考えて、サンプルをコンパイルしたら「bcc55.exeでは"-WD"オプションでコンパイルするとDLLファイルの他にLIBファイルを生成しましたが、bcc32c.exeはDLLファイルだけでLIBファイルを出しません!」(汗)

 

ということで、BCC102のbcc32c.exe(Embarcadero C++ Compiler Ver  7.30)はまだまだ色々と謎がありますね。

【12月22日修正】

ーWDでLIBファイルを出力していたというのは私の誤解で、その後.makファイルを確認し、

ILIB=implib
...

LIB=Release\IconView_DLL.lib
...

    $(ILIB) $(LIB) $(TARGET)
"implib(.exe) IconView_DLL.lib IconView_DLL.dll"

というコマンドで、dllファイルからlibファイルを作っていたことが分かりました。浅学にてご容赦ください。

 

【関連】

【BCC】C++コンパイラーの謎シリーズ

【bcc32c】BCCSkeltonのbcc32c.exe(Clang文法チェック)対応

【bcc32c】BCCSkeltonのClang対応修正後リンカーエラーとその対応

【bcc32c】BCCSkeltonのClang対応結果

【bcc32c】BCCSkeltonのClang対応結果(追補)

【bcc32c】BCCSkeltonのClang対応の恩恵

【bcc32c】コンパイラーオプション続報

 

最後にDLLファイルを作ったのはIconViewer_DLLの時だったのでファイルを見ると、

やはりビルドした時に一挙にLIBファイル迄出していますね。

以下のようなサンプルがあったので、

次のバッチファイルでコンパイルしてみると、

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\Dll\DllSample.cpp" -tD
del "C:\Users\(パス)\Dll\DLLSample.tds"
pause

見事にDllSample.dllができますが、DllSample.libがありません。(汗汗;)

これじゃ、DLLの静的ロード(LIBファイルと一緒にビルド)ができないじゃないか?!」と結構焦りましたが、途中「絶対にツールがあるはずだ!」と思い直し、BCC102のbinフォールダーの中に"tlib.exe"があることを発見しました。まずはこいつのヘルプファイルやEmbarcaderoのサイトを見てみました。

【12月21日追記】

tlibはlibファイルの管理、編集用のツールですが、implibはdllファイルからlibファイルをimp(ort)するツールなのでより本件処理に向いています。(例:ABC.dllというファイルがあれば、"implib(.exe) ABC.lib ABC.dll"とすることでlibファイルが作れます。)

TLIB 6.4 Copyright (c) 1987-2014 Embarcadero Technologies, Inc.
Syntax: TLIB options libname [commands | files], listfile
    libname     library file pathname
    commands    sequence of operations to be performed (optional)
    listfile    file name for listing file (optional)

A command is of the form: <symbol>module, where <symbol> is:
    +           add module to the library
    -           delete module from the library
    *           extract module without removing it
    -+ or +-    update module in library
    -* or *-    extract module and remove it
Set the default action for modules without a specified command:
    /a          add module to the library
    /d          delete module from library
    /e          extract module without removal
    /u          update module in library
    /x          extract module and remove it from library
    /L          write listfile to stdout
    /N          disable support of command syntax
    /O          enable support of command syntax
    /A          write GNU AR format archive with ELF objs
    /C          case-sensitive library
    /PSIZE      set the library page size to SIZE
    /0          purge comment records
    /8          output encoding is utf-8
Use @filepath to continue from file "filepath".
Use '&' at end of a line to continue onto the next line.

要すれば、"tlib <.libファイルパス、名> +<.objファイルパス、名>, <関数リストファイルパス、名>"と入力すれば、DLLファイルAAAのobjファイル(AAA.obj)からlibファイル(AAA.lib)ができるようです。

ということで、次のバッチファイルにして再度実行。

REM まずはDLLファイルを作る

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\Dll\DllSample.cpp" -tD
REM 次にobjファイル作る

"C:\Borland\BCC102\bin\bcc32c.exe" "C:\Users\(パス)\Dll\DllSample.cpp" -tD -c
REM tlibを使ってlibファイルを作る

"C:\Borland\BCC102\bin\tlib.exe" "C:\Users\(パス)\Dll\DllSample.lib" +"C:\Users\(パス)\Dll\DllSample.obj", DllSample.txt
REM 不要なファイルを削除

del "C:\Users\(パス)\Dll\DLLSample.tds"
pause

としてみたところ、見事に成功しました。(libファイルと関数リストのtxtファイルがある。)

bcc32c.exe(注)のツールのみならず、tlib.exeのツールも必要かもしれませんね。

注:今日こんなことで時間を費やしていたら、前に触れたbcc32cのヘルプファイル("bcc32c -h"で出力される)の他に、bcc32c独自のヘルプファイル("bcc32c -Xdriver --help"で出力される)があることが分かりました。以下に参考として載せます。

【12月22日追記】

使えそうなのは

-t(-tWでウィンドウズアプリ、-tCでコンソールアプリ、-tDでDLLが作られるのはご高承の通り。-tLや-tOは無効です。)

-n(出力場所を指定するのも有効。空白文字がある場合""が必要。)

-c(コンパイルのみでobjファイルが出力され、有効。)

-g(ソースレベルnotdsファイルが出力、とありますが、サポートされておらず無効だそうです。)

Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
OVERVIEW: Embarcadero C/C++ compiler driver for Win32

USAGE: bcc32c [options] <inputs>

OPTIONS:
  -###                    Print the commands to run for this compilation
  -E                      Only run the preprocessor
  -F <value>              Add directory to framework include search path
  -H                      Show header includes and nesting depth
  -I <value>              Add directory to include search path
  -MG                     Add missing headers to dependency list
  -MP                     Create phony target for each dependency (other than main file)
  -MQ <value>             Specify target to quote for dependency
  -MT <value>             Specify target for dependency
  -ObjC++                 Treat source input files as Objective-C++ inputs
  -ObjC                   Treat source input files as Objective-C inputs
  -PC<N>                  (deprecated, use --jobs=<N> instead) Perform a parallel compilation with a maximum of <N> processes
  -P                      Disable linemarker output in -E mode
  -Qunused-arguments      Don't emit warning for unused driver arguments
  -S                      Only run preprocess and compilation steps
  -Wa,<arg>               Pass the comma separated arguments in <arg> to the assembler
  -Wl,<arg>               Pass the comma separated arguments in <arg> to the linker
  -Wp,<arg>               Pass the comma separated arguments in <arg> to the preprocessor
  -W<warning>             Enable the specified warning
  -Xanalyzer <arg>        Pass <arg> to the static analyzer
  -Xassembler <arg>       Pass <arg> to the assembler
  -Xclang <arg>           Pass <arg> to the clang compiler
  -Xdriver <arg>          Pass <arg> directly to the clang compiler
  -Xlinker <arg>          Pass <arg> to the linker
  -Xpreprocessor <arg>    Pass <arg> to the preprocessor
  --analyze               Run the static analyzer
  -arcmt-migrate-emit-errors
                          Emit ARC errors even if the migrator can fix them
  -arcmt-migrate-report-output <value>
                          Output path for the plist report
  -cl-kernel-arg-info     OpenCL only. This option allows the compiler to store information about the arguments of a kernel(s)
  -cxx-isystem <directory>
                          Add directory to the C++ SYSTEM include search path
  -c                      Only run preprocess, compile, and assemble steps
  -dD                     Print macro definitions in -E mode in addition to normal output
  -dM                     Print macro definitions in -E mode instead of normal output
  -dependency-dot <value> Filename to write DOT-formatted header dependencies to
  -dependency-file <value>
                          Filename (or -) to write dependency output to
  -emit-ast               Emit Clang AST files for source inputs
  -emit-llvm              Use the LLVM representation for assembler and object files
  -external-linker <arg>  Select <arg> as external linker
  -faltivec               Enable AltiVec vector initializer syntax
  -fapple-kext            Use Apple's kernel extensions ABI
  -fapple-pragma-pack     Enable Apple gcc-compatible #pragma pack handling
  -fblocks                Enable the 'blocks' language feature
  -fborland-auto-refcount Enable Automatic Reference Counting for Delphi style class pointers
  -fborland-define-external-types
                          Generate definitions for external debug types into PCH file
  -fborland-extensions    Accept non-standard constructs supported by the Borland compiler
  -fborland-use-external-types
                          Generate references to external debug types from PCH file
  -fbounds-checking       Enable run-time bounds checks
  -fcolor-diagnostics     Use colors in diagnostics
  -fcomment-block-commands=<arg>
                          Treat each comma separated argument in <arg> as a documentation comment block command
  -fcxx-exceptions        Enable C++ exceptions
  -fdata-sections         Place each data in its own section (ELF Only)
  -fdelayed-template-parsing
                          Parse templated function definitions at the end of the translation unit 
  -fdiagnostics-parseable-fixits
                          Print fix-its in machine parseable form
  -fdiagnostics-print-source-range-info
                          Print source range spans in numeric form
  -fdiagnostics-show-name Print diagnostic name
  -fdiagnostics-show-note-include-stack
                          Display include stacks for diagnostic notes
  -fdiagnostics-show-option
                          Print option name with mappable diagnostics
  -fdiagnostics-show-template-tree
                          Print a template comparison tree for differing templates
  -fdollars-in-identifiers
                          Allow '$' in identifiers
  -femit-all-decls        Emit all declarations, even if unused
  -fexceptions            Enable support for exception handling
  -ffast-math             Enable the *frontend*'s 'fast-math' mode. This has no effect on optimizations, but provides a preprocessor macro __FAST_MATH__ the same as GCC's -ffast-math flag
  -ffp-contract=<value>   Form fused FP ops (e.g. FMAs): fast (everywhere) | on (according to FP_CONTRACT pragma, default) | off (never fuse)
  -ffreestanding          Assert that the compilation takes place in a freestanding environment
  -ffunction-sections     Place each function in its own section (ELF Only)
  -fgnu-keywords          Allow GNU-extension keywords regardless of language standard
  -fgnu-runtime           Generate output compatible with the standard GNU Objective-C runtime
  -fgnu89-inline          Use the gnu89 inline semantics
  -finstrument-functions  Generate calls to instrument function entry and exit
  -flimit-debug-info      Limit debug information produced to reduce size of debug binary
  -fmath-errno            Require math functions to indicate errors by setting errno
  -fmodules-cache-path=<directory>
                          Specify the module cache path
  -fmodules-ignore-macro=<value>
                          Ignore the definition of the given macro when building and loading modules
  -fmodules-prune-after=<seconds>
                          Specify the interval (in seconds) after which a module file will be considered unused
  -fmodules-prune-interval=<seconds>
                          Specify the interval (in seconds) between attempts to prune the module cache
  -fmodules               Enable the 'modules' language feature
  -fms-compatibility      Enable Microsoft compatibility mode
  -fms-extensions         Accept some non-standard constructs supported by the Microsoft compiler
  -fmsc-version=<value>   Version of the Microsoft C/C++ compiler to report in _MSC_VER (0 = don't define it (default))
  -fno-access-control     Disable C++ access control
  -fno-assume-sane-operator-new
                          Don't assume that C++'s global operator new can't alias any pointer
  -fno-autolink           Disable generation of linker directives for automatic library linking
  -fno-builtin            Disable implicit builtin knowledge of functions
  -fno-common             Compile common globals like normal definitions
  -fno-constant-cfstrings Disable creation of CodeFoundation-type constant strings
  -fno-diagnostics-fixit-info
                          Do not include fixit information in diagnostics
  -fno-diagnostics-show-note-include-stack
                          Display include stacks for diagnostic notes
  -fno-dollars-in-identifiers
                          Disallow '$' in identifiers
  -fno-elide-constructors Disable C++ copy constructor elision
  -fno-elide-type         Do not elide types when printing diagnostics
  -fno-lax-vector-conversions
                          Disallow implicit conversions between vectors with a different number of elements or different element types
  -fno-limit-debug-info   Do not limit debug information produced to reduce size of debug binary
  -fno-merge-all-constants
                          Disallow merging of constants
  -fno-objc-infer-related-result-type
                          do not infer Objective-C related result type based on method family
  -fno-operator-names     Do not treat C++ operator name keywords as synonyms for operators
  -fno-rtti               Disable generation of rtti information
  -fno-sanitize-blacklist Don't use blacklist file for sanitizers
  -fno-sanitize-recover   Disable sanitizer check recovery
  -fno-show-column        Do not include column number on diagnostics
  -fno-show-source-location
                          Do not include source location information with diagnostics
  -fno-spell-checking     Disable spell-checking
  -fno-threadsafe-statics Do not emit code to make initialization of local statics thread safe
  -fno-use-cxa-atexit     Don't use __cxa_atexit for calling destructors
  -fno-use-init-array     Don't use .init_array instead of .ctors
  -fobjc-arc-exceptions   Use EH-safe code when synthesizing retains and releases in -fobjc-arc
  -fobjc-arc              Synthesize retain and release calls for Objective-C pointers
  -fobjc-exceptions       Enable Objective-C exceptions
  -fobjc-gc-only          Use GC exclusively for Objective-C related memory management
  -fobjc-gc               Enable Objective-C garbage collection
  -fobjc-runtime=<value>  Specify the target Objective-C runtime kind and version
  -fpack-struct=<value>   Specify the default maximum struct packing alignment
  -fpascal-strings        Recognize and construct Pascal-style string literals
  -fsanitize-address-zero-base-shadow
                          Make AddressSanitizer map shadow memory at zero offset
  -fsanitize-blacklist=<value>
                          Path to blacklist file for sanitizers
  -fsanitize-memory-track-origins
                          Enable origins tracking in MemorySanitizer
  -fsanitize=<check>      Enable runtime instrumentation for bug detection: address (memory errors) | thread (race detection) | undefined (miscellaneous undefined behavior)
  -fshort-enums           Allocate to an enum type only as many bytes as it needs for the declared range of possible values
  -fshort-wchar           Force wchar_t to be a short unsigned int
  -fshow-overloads=<value>
                          Which overload candidates to show when overload resolution fails: best|all; defaults to all
  -fslp-vectorize-aggressive
                          Enable the BB vectorization passes
  -fslp-vectorize         Enable the superword-level parallelism vectorization passes
  -fstrict-enums          Enable optimizations based on the strict definition of an enum's value range
  -ftrap-function=<value> Issue call to specified function rather than a trap instruction
  -ftrapv-handler=<function name>
                          Specify the function to be called on overflow
  -ftrapv                 Trap on integer overflow
  -funroll-loops          Turn on loop unroller
  -fuse-init-array        Use .init_array instead of .ctors
  -fvectorize             Enable the loop vectorization passes
  -fvisibility-inlines-hidden
                          Give inline C++ member functions default visibility by default
  -fvisibility-ms-compat  Give global types 'default' visibility and global functions and variables 'hidden' visibility by default
  -fvisibility=<value>    Set the default symbol visibility for all global declarations
  -fwrapv                 Treat signed integer overflow as two's complement
  -fwritable-strings      Store string literals as writable data
  -gcc-toolchain <value>  Use the gcc toolchain at the given directory
  -gline-tables-only      Emit debug line number tables only
  -g                      Generate source level debug information
  -help                   Display available options
  -idirafter <value>      Add directory to AFTER include search path
  -iframework <value>     Add directory to SYSTEM framework search path
  --ilink-no-cfg          Do not use cfg file for Borland linker
  -imacros <file>         Include macros from file before parsing
  -include-pch <file>     Include precompiled header file
  -include <file>         Include file before parsing
  -index-header-map       Make the next included directory (-I or -F) an indexer header map
  -iprefix <dir>          Set the -iwithprefix/-iwithprefixbefore prefix
  -iquote <directory>     Add directory to QUOTE include search path
  -isysroot <dir>         Set the system root directory (usually /)
  -isystem <directory>    Add directory to SYSTEM include search path
  -iwithprefixbefore <dir>
                          Set directory to include search path with prefix
  -iwithprefix <dir>      Set directory to SYSTEM include search path with prefix
  -iwithsysroot <directory>
                          Add directory to SYSTEM include search path, absolute paths are relative to -isysroot
  --jobs=<N>              Perform a parallel compilation with a maximum of <N> processes
  -mcdecl                 Make CDecl calling convention the default
  -mfastcall              Make FastCall calling convention the default
  --migrate               Run the migrator
  -mllvm <value>          Additional arguments to forward to LLVM's option processing
  -mms-bitfields          Set the default structure layout to be compatible with the Microsoft compiler standard
  -mno-cdecl              Do not make CDecl calling convention the default
  -mno-fastcall           Do not make FastCall calling convention the default
  -mno-global-merge       Disable merging of globals
  -mno-implicit-float     Don't generate implicit floating point instructions
  -mno-stdcall            Do not make StdCall calling convention the default
  -momit-leaf-frame-pointer
                          Omit frame pointer setup for leaf functions
  -mqdsp6-compat          Enable hexagon-qdsp6 backward compatibility
  -mrelax-all             (integrated-as) Relax all machine instructions
  -mrtd                   Make StdCall calling convention the default
  -msoft-float            Use software floating point
  -mstack-alignment=<value>
                          Set the stack alignment
  -mstackrealign          Force realign the stack at entry to every function
  -mstdcall               Make StdCall calling convention the default
  -mstrict-align          Force all memory accesses to be aligned (ARM only)
  -nobuiltininc           Disable builtin #include directories
  -nostdinc++             Disable standard #include directories for the C++ standard library
  -n <directory>          Write object files to <directory>
  -objcmt-migrate-literals
                          Enable migration to modern ObjC literals
  -objcmt-migrate-subscripting
                          Enable migration to modern ObjC subscripting
  -o <file>               Write output to <file>
  -pg                     Enable mcount instrumentation
  -pipe                   Use pipes between commands, when possible
  -print-file-name=<file> Print the full library path of <file>
  -print-ivar-layout      Enable Objective-C Ivar layout bitmap print trace
  -print-libgcc-file-name Print the library path for "libgcc.a"
  -print-prog-name=<name> Print the full program path of <name>
  -print-search-dirs      Print the paths used for finding libraries and programs
  -pthread                Support POSIX threads in generated code
  -q                      Suppress compiler identification banner
  -rewrite-legacy-objc    Rewrite Legacy Objective-C source to C++
  -rewrite-objc           Rewrite Objective-C source to C++
  -save-temps             Save intermediate compilation results
  -serialize-diagnostics <value>
                          Serialize compiler diagnostics to a file
  -std=<value>            Language standard to compile for
  -stdlib=<value>         C++ standard library to use
  -target <value>         Generate code for the given target
  -time                   Time individual commands
  -traditional-cpp        Enable some traditional CPP emulation
  -trigraphs              Process trigraph sequences
  -t<value>               Specify Borland target executable
  -undef                  undef all system defines
  -verify                 Verify output using a verifier
  -v                      Show commands to run and use verbose output
  -working-directory <value>
                          Resolve file paths relative to the specified directory
  -w                      Suppress all warnings
  -x <language>           Treat subsequent input files as having type <language>
  +<arg>                  Load user specified configuration file

 

ps. ウィンドウズ版MENACEやbcc32cのツール等は年越しになるかも、です。(あー、疲れた。)