IconViewerのプログラム企画は次のようにしました。

・アイコン(カーソル)ファイルからアイコン(カーソル)の画像とともに、その基本情報も表示する。

・(EZImage等他のダイアログ、更にDLL化して使えるように、モーダル)ダイアログベースのアプリとする。

・スタンドアロンの場合、単に表示だけだとつまらないのでビットマップの選択、登録をしてアイコン化できるようにする。

・スタンドアロンの場合、ユーザーインターフェースを充実させる。

 

この結果、(既に20年前のソースコードは失っていたので、WEBでActiveBasic用に書いたスタンドアロンプログラムをDLして、参考とし)、次のような具体仕様を想定しました。

・昔のIconViewerのデザインを極力生かす。即ち、ダイアログ上にアイコン、ANDビットマップ、XORビットマップを表示させ、主要データはリストビューで表示(且つ選択でき)、詳細情報はエディットボックスでじっくり見れるようにする。

・BCCFormでメインダイアログ、保存用ダイアログ、Hotspot設定用ダイアログ、および関連イメージリソースを作成してダイアログベースのアプリとする。

・アイコンファイルの管理用にCICONクラスを作ろうと思いますが、その中で構成ビットマップをリスト管理しようと思います。

・インターフェースとして、メインダイアログにはメニュー、ツールバーとステータスバーを付け、ショートカットキー(アクセラレーター)を付け、ドラッグアンドドロップにも対応させる。

 

以上の内容を元に先ずはリソースを開発します。(以下は"IconViewer.rc"と"ResIconViewer.h"を参照して読んでください。)

・メインダイアログ(IDD_ICON)、保存用ダイアログ(IDD_SAVE)、Hotspot設定用ダイアログ(IDD_HOTSPOT)およびバージョン表示用ダイアログ (IDD_VERSION)を作ります。

・メニュー(「ダイアログで使用するメニュー」IDM_FORM_MENU)を、基本テンプレートを改造して作ります。

・ツールバー用のビットマップ(IDI_TBAR)をTBEditorで、およびプログラムアイコン(IDI_ICON)をEZImageで作ります。

・最後にアクセラレーター(IDA_ACCEL)を作成します。(これはBCCFormでは、実際にショートカットキーを押してキー設定を行うインターフェースを取っています。勿論選択による設定もできます。)

なお、プログラムにはツールバーとステータスバーにツールチップを付けていますが、これが新しめのスタイルなので、

#define        TBSTYLE_TOOLTIPS    0x0100
#define        SBT_TOOLTIPS        0x0800
というスタイル定数用のリソースコンパイル用定義を追加しています。

 

これで出来上がった"IconViewer.rc"と"ResIconViewer.h"をエディターで美しく仕上げてから、SkeltonWizardにかけます。

IDD_ICONをベースとするダイアログベースのプログラムを選択し、ツールバー(IDI_TOOLBARのビットマップとメニューをシンクロさせます)、ステータスバー(文字列区分は2つ)、ウィンドウメッセージ割り込みはWM_NOTIFYとWM_DESTROY(注)を選択します。

注:BCCSkeltonのモードレスダイアログ用コールバック関数マクロにWM_INITDIALOG、WM_NOTIFYとWM_CLOSEが既定で組み込み済です。
 

これで自動的にIconViewer.h、IconViewerProc.h、IconViewer.cppが出来上がります。

 

今日はここまでにしましょう。

アイコンについてはIconViewerのヘルプファイルの資料にちょっと載せていますが、その何たるかについては書かれていません。

アイコンに関してご本家MSの説明を探すと

こんな感じのものしかありません。英語でなかろうと、ここに書かれている内容がすーっと腑に落ちるまでにはもう少しオードブルが必要でしょう。

 

・アイコンとは画像リソースのことです。(前回のWikiなどを参照。)

・アイコンとカーソルは実はほとんど同じものです。(↑の内容を見ると、iconにもhotspotについて記述されています。しかし、普通の人には「共に画像リソースでファイル上もICONDIR構造体のidTypeのフラグの違いとhotspotの取扱が違うくらい」と覚えておけばよいでしょう。)

・アイコンはリソースエディターやイメージエディターなどで作成し、大体16x16、32x32が主流で(48x48、64x64、128x128などもあり、だが稀)と考えたらよいでしょう。色は現在はほとんどモノクローム、16色は使われず、256色(8bit)から32bit迄が主流ではないでしょうか?

・作ったアイコンは通常LoadImage関数等でリソースまたは外部記憶のファイルから呼び出し、画像として、またはコントロールに張ったりして使い、使用後は必ずDestroyIcon(やDestroycursor)等で破棄(記憶領域の解放)することが必要です。

位のことを覚えておくとよいでしょう。

 

また、IconViewerのヘルプファイルの資料にあるアイコン(カーソル)ファイルの基本的構造はしっかり通させてください。ウェブにも色々と説明記事が出ています。

 

 

覚えておくべきポイントは、

・ICONDIRは「iconかcursorか、何個画像があるのか」が分かる。

・画像数が分かるとその数だけ「ICONDIRENTRY」構造体情報と「画像情報(ここではBitmap情報→BITMAPINFO(BITMAP
BITMAPINFOHEADER+RGBQUAD(ColorBit(BITMAPINFOHEADER.biBitCount)が8以下の場合、画像のANDビットパターン(表示部分)およびXORビットパターン(表示画像の輪郭))」が連なっている。

・LoadImage関数は複数の画像から環境(デバイスコンテキスト)に最適なものを選んで表示する(アイコンやカーソルのハンドルを渡す)。

・ハンドルを使って使用されるアイコン(カーソル)リソースは最後に破棄してメモリーを解放する。

 

こんなところでしょうか?

 

次にIconViewerについてですが、アイコンやカーソルファイルを読み込んでLoadImageによる選択画像を表示するのみならず、構成ビットマップ画像とICONDIRENTRY、BITMAPINFOHEADERの情報をリストビューおよびテキストボックスで表示するツールです。

とあるゲームのiconを拾ってきて表示している上記サンプルでは、冒頭に8bit(256色)の16x16と32x32、0x0のビットマップ画像を持ちますが(画像では表示されていない)、最後の3つは32bitの48x48、32x32、16x16画像まで6種類のビットマップが入っています。

 

なお、このスタンドアローンツールはメニュー、ツールバーとステータスバーを持ち、ドラッグアンドドロップを受け付け、更に構成ビットマップを削除したり、任意のビットマップ画像を追加したりしてアイコンやカーソルファイルを再構成することができます。次回からはその中身を見ていきましょう。

 

BCCForm and BCCSkelton 2021でアップデートしたツールにEZImage

があります。20年前はモノクロ、16色(4bit)、256色(8bit)までのicon/cursor画像および16、24、32bit迄のBitmap画像を編集するツールでした。しかし、現在は32bitまでのicon/cursorも許されており、それらに対応しています。(但し、開発用なので、サイズは16x16と32x32のままで、gifやpng等の画像には対応していません。汗;)また、当時はicon/cursorファイルに複数のBitmap画像があってもLoadImage(旧LoadIconやLoadCursorも同じ)が(勝手に)選択する画像しか取り扱えませんでしたが、現在はIconViewダイアログで選択できるようにしています。

このIconViewダイアログの導入理由は、「何故LoadImage関数は複数のBitmap画像の任意選択ができないのだろうか?」という疑問から、(旧バージョンではicon/cursorの属性表示の為に使われていた)IconViewダイアログを使って任意の一つの画像を選択できるようにしたのですが、(Bitmapファイルは単一の画像しか持たないのに)「何故icon/cursorファイルは複数の画像ファイルを扱うのであろうか?」というより根本的な疑問に繋がりました。

実はこの疑問は、色々とウェブで調べてみたのですが未だに直接回答している情報を得ていません。(例:

)従って推定するしかないのですが、おそらく20年前のハード環境(カラーでは分厚いCRTディスプレーが主流で、LCD等ではモノクロでした!)では、異なる環境でアイコンやカーソル画像を表示させるために、複数画像を持たせ、対象ハードの能力、仕様に合わせて最大かつ最高色深度(Color bitが大きい)の画像を選択するような仕様だったのではないでしょうか?現在の殆どのPCユーザーが当たり前に高解像度、多色の画像を表示できているので、このような疑問の意味もなくなり、回答すら残っていないのかもしれません。

 

というような年寄りの感慨もあり、第2回目のプログラミング講座はサンプルにあるIconViewer(↑の画像のIconViewダイアログをベースにしたスタンドアロンツール)について解説してみようかな、と考えます。

 

最後に実行ファイルについてですが、新しいコンパイラーはRTLが大きいためか、データサイズが大きくなっているのか、一般に実行ファイルサイズが大きくなりますね。これはbcc32 5.5とbcc32cについても同じです。

 

MakeファイルのところでビルドしたWinTemlate.exeですが、47.5KBに対して62.5KBになっています。

 

【bcc32 5.5】

【bcc32c】

bcc32cはClangベースのコンパイラーで、「C++コンパイラーの謎(2)」において「・より具体的かつ詳細な新しい警告およびエラー メッセージ」を出すというEmbarcadero社記述を引用しましたが、HelloWorld(注)をビルドした結果が如実にそれを実証しました。

注:bcc32 5.5を使ったBCCForm and BCCSkeltonのサンプル。本ブログ【HelloWorld】その(1)~(6)参照。(当然no errorでした。)

 

bcc32cによるビルドの結果は、"56 warnings and 6 errors generated"という情けないものになりました。以下は代表的警告例と6つのエラーの内容です。()で反省のコメントを添えます。

【警告例】
CANVAS.h:125:1: warning: control reaches end of non-void function [-Wreturn-type]
(関数がvoidではないにもかかわらず、値を返さずに終了している。)
bool CANVAS::Clear() {

    PatBlt(m_hDC, m_Left, m_Top, m_Right, m_Bottom, PATCOPY);
    InvalidateRect(m_hWnd, NULL, TRUE);
}
(voidにするか、return (InvalidateRect(m_hWnd, NULL, TRUE));等が適当でしたね。)


CANVAS.h:217:7: warning: unused variable 'rec' [-Wunused-variable]
(使われない変数がある。)
//線を引く
bool CANVAS::Line(int sx, int sy, int ex, int ey) {

    HPEN pen = CreatePen(m_PStyle, m_PWidth, m_Color);
    m_Pen = (HPEN)SelectObject(m_hDC, pen);
    MoveToEx(m_hDC, sx, sy, NULL);
    LineTo(m_hDC, ex, ey);
    SelectObject(m_hDC, m_Pen);
    DeleteObject(pen);
    RECT rec;
    ReDraw(sx, sy, ex, ey);
}
(確かに。これは当時ReDraw関数の引数をRECTにするか、int x 4にするか揺れていた時のコーディングでこんな形になってしまったようです。なお、これもreturnがないですね。)


CBMP.h:328:3: warning: add explicit braces to avoid dangling else [-Wdangling-else]
(if文のネスティングがある場合、混乱を避けるうえで明確に括弧をつけろ。(以下の文のelseがかかるのは前か後ろか分からない。))
    if(m_PBmf) (ここに'{')   //m_PBmfにメモリーが与えられていた場合はこれを開放する
        if(LocalFree(m_PBmf))    //エラーでなければNULL
            MessageBox(NULL, "m_PBmfのローカルメモリーの削除に失敗しました",
                        "エラー!", MB_OK | MB_ICONSTOP);
        else
            m_PBmf = NULL;

(ここに'}')
(まさにその通りで、最初のif文に括弧を付けておけば中のif文の対のelseだとすぐにわかるわけです。

CFILE.h:232:11: warning: using the result of an assignment as a condition without
      parentheses [-Wparentheses]
CFILE.h:232:11: note: place parentheses around the assignment to silence this warning
CFILE.h:232:11: note: use '==' to turn this assignment into an equality comparison
(指摘三連発ですね。

代入結果を条件にする時には括弧をつけろ。
警告がいやならカッコつけろ。(代入'='と'=='等号の誤りが多いための措置。)
等価比較なら等号'=='を使え。

代入結果をif文の条件式とする場合には念のため括弧でくくる、というこの指摘は正しいのですが、BCCSkeltonのマクロ全体にその指摘が及んでいます。)

        while(cp = strstr(cp, org)) {

C:\Borland\BCCForm/.\BCCSkelton\CMDI.h:35:26: warning: conversion from string literal to 'char *' is deprecated
      [-Wdeprecated-writable-strings]
(文字列変数へ文字列定数を初期値として代入していることが、コンストラクターなので文字列定数の文字列変数への転換として問題視されています。これも多い指摘です。)

//CMDIのコンストラクター
CMDI::CMDI(char* UName = "") {

C:\Borland\BCCForm/.\BCCSkelton\CCTRL.h:10:15: warning: 'CCTRL::Init' hides overloaded virtual function
      [-Woverloaded-virtual]
((CWNDから派生させたCCTRLの)仮想関数は(派生元の仮想関数を)隠蔽している。)
        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,
(この指摘も結構ありました。)

【指摘されたエラー】
C:\Borland\BCCForm/.\BCCSkelton\CMDI.h:35:18: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CMDI::CMDI(char* UName = "") {
                 ^       ~~
C:\Borland\BCCForm/.\BCCSkelton\CMDI.h:19:2: note: previous declaration is here
        CMDI(char*);
        ^
(↑の警告部分がエラーでも指摘されています。「引数初期値を与えることでこのコンストラクターをデフォルトにしている。」ということです。実は将にクラス宣言でデフォルトのコンストラクターを宣言し、その定義で初期値を与えていたので、意図通りなのですが、それがエラーであるとされました。)

C:\Borland\BCCForm/.\BCCSkelton\CTAB.h:34:16: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CTAB::CTAB(int w = 16, int h  = 16, int c = ILC_COLOR4) {
               ^   ~~
C:\Borland\BCCForm/.\BCCSkelton\CTAB.h:16:2: note: previous declaration is here
        CTAB(int, int, int);                                                    //<U+30B3><U+30F3><U+30B9>...
        ^
C:\Borland\BCCForm/.\BCCSkelton\CTREEVIEW.h:44:26: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CTREEVIEW::CTREEVIEW(int imgw = 0, int imgh = 0, int imgnum = 0,
                         ^      ~
C:\Borland\BCCForm/.\BCCSkelton\CTREEVIEW.h:17:2: note: previous declaration is here
        CTREEVIEW(int, int, int, UINT, int);//<U+30B3><U+30F3><U+30B9><U+30C8><U+30E9><U+30AF><U+30BF><U+30FC>
        ^
C:\Borland\BCCForm/.\BCCSkelton\CLISTVIEW.h:59:26: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CLISTVIEW::CLISTVIEW(int imgw1 = 0, int imgh1 = 0, int imgnum1 = 0,
                         ^       ~
C:\Borland\BCCForm/.\BCCSkelton\CLISTVIEW.h:23:2: note: previous declaration is here
        CLISTVIEW(int, int, int, UINT, int,
        ^
C:\Borland\BCCForm/.\BCCSkelton\CCMDEDIT.h:27:24: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CCMDEDIT::CCMDEDIT(int i = 4) { //<U+51FA><U+529B><U+30D0><U+30C3><U+30D5><U+30A1><U+30FC><U+306E><U+898F>...
                       ^   ~
C:\Borland\BCCForm/.\BCCSkelton\CCMDEDIT.h:15:2: note: previous declaration is here
        CCMDEDIT(int);                                                          //<U+5F15><U+6570><U+4ED8>...
        ^
C:\Borland\BCCForm/.\BCCSkelton\CHISTORY.h:49:26: error: addition of default argument on redeclaration makes this
      constructor a default constructor
CHISTORY::CHISTORY(char* InitName = ".\Init.ini") {
                         ^          ~~~~~~~~~~~~
C:\Borland\BCCForm/.\BCCSkelton\CHISTORY.h:39:2: note: previous declaration is here
        CHISTORY(char* InitName);       //<U+30B3><U+30F3><U+30B9><U+30C8><U+30E9><U+30AF><U+30BF><U+30FC>...
        ^
(これはCTAB、CTREEVIEW、CLISTVIEW、CCMDEDITやCHISTORYでも行っていたので再度指摘され、合計6つのエラーとなっています。)

 

内容的に致命的なものはありませんでしたが、コンストラクターを宣言して初期値を代入する定義が使えなくなるので初期化関数を作って初期値を与える必要が出てきますね。

 

何とかバッチファイルでビルドできたので、次はMakeでビルドすることを確認します。

BCCDeveloperやBCCMakerはbcc32.exe用にできているので、テストには次の一行バッチファイルを使います。

 

"DoMake.bat"

C:\Borland\BCC102\bin\make.exe -f%1 > bccerror.txt

 

前回の経験からコンパイラーはbcc32c、リソースコンパイラーをbrc32として次のBCCDeveloperやBCCMakerの作るmakファイルで、コンパイラーフラグを変更してテストを実施。

#-----------------------------
# BCCMaker 1.0
# Copyright (C) 2002 by ysama
#-----------------------------
.autodepend
CC="C:\Borland\BCC102\bin\bcc32c.exe"
RC="C:\Borland\BCC102\bin\brc32.exe"
ASM="C:\nasm-2.14.02\nasm.exe"
CFLAG=-tW -w
RFLAG=-r
AFLAG=-omf
OUTDIR=-nRelease
CINCS=-I"C:\Borland\BCCForm"
TARGET="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.exe"
SRC1="C:\Users\ysama\Programing\Windows Program\WinTemplate\WinTemplate.cpp"
OBJ1="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.obj"
RC1="C:\Users\ysama\Programing\Windows Program\WinTemplate\MenuTempl.rc"
RES1="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\MenuTempl.res"

TARGET: $(TARGET)
$(TARGET): $(OBJ1) $(RES1)
    $(CC) $(CFLAG) -e$(TARGET) $(OBJ1)
    $(RC) $(RESINCS) $(RES1) $(TARGET)
$(OBJ1): $(SRC1)
    $(CC) $(CFLAG) $(CINCS) -o$(OBJ1) -c $(SRC1)
$(RES1): $(RC1)
    $(RC) $(RESINCS) $(RFLAG) -fo$(RES1) $(RC1)
次の結果出力を出して、正常にビルドが完了します。

MAKE Version 5.41  Copyright (c) 1987, 2014 Embarcadero Technologies, Inc.
    "C:\Borland\BCC102\bin\bcc32c.exe" -tW -w -I"C:\Borland\BCCForm" -o"C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.obj" -c "C:\Users\ysama\Programing\Windows Program\WinTemplate\WinTemplate.cpp"
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
C:\Users\ysama\Programing\Windows Program\WinTemplate\WinTemplate.cpp:
    "C:\Borland\BCC102\bin\brc32.exe"  -r -fo"C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\MenuTempl.res" "C:\Users\ysama\Programing\Windows Program\WinTemplate\MenuTempl.rc"
Borland Resource Compiler  Version 5.40
Copyright (c) 1990, 1999 Inprise Corporation.  All rights reserved.
    "C:\Borland\BCC102\bin\bcc32c.exe" -tW -w -e"C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.exe" "C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.obj"
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
    "C:\Borland\BCC102\bin\brc32.exe"  "C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\MenuTempl.res" "C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.exe"
Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
 

従って、bcc32cを使う場合、コンパイルフラグを"-tW(コンソールなら"-tC")"に警告出力の"-w"を付けてビルドすればBccdeveloperやBCCMakerで十分ビルド可能ということになります。(Bccdeveloperはファイルを開くダイアログでは"bcc32.exe"しか表示しないので、手入力することが必要です。)

 

最後にbcc102から添付されているMicrosoftのrc.exeでもテストをします。

rc.exeはHelpファイルによるとオプションの引数が次の通りとなってます。

Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
Copyright (C) Microsoft Corporation.  All rights reserved.
Usage:  rc [options] .RC input file
Switches:
   /r    Emit .RES file (optional)
(省略)

   /fo   Rename .RES file
(省略)Flags may be either upper or lower case
ということでMakファイルは次の通り。

#-----------------------------
# BCCMaker 1.0
# Copyright (C) 2002 by ysama
#-----------------------------
.autodepend
CC="C:\Borland\BCC102\bin\bcc32c.exe"
RC="C:\Borland\BCC102\bin\rc.exe" #ここを変更
ASM="C:\nasm-2.14.02\nasm.exe"
CFLAG=-tW -w
RFLAG=/r #ここを変更
AFLAG=-omf
OUTDIR=-nRelease
CINCS=-I"C:\Borland\BCCForm"
TARGET="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.exe"
SRC1="C:\Users\ysama\Programing\Windows Program\WinTemplate\WinTemplate.cpp"
OBJ1="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.obj"
RC1="C:\Users\ysama\Programing\Windows Program\WinTemplate\MenuTempl.rc"
RES1="C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\MenuTempl.res"

TARGET: $(TARGET)
$(TARGET): $(OBJ1) $(RES1)
    $(CC) $(CFLAG) -e$(TARGET) $(OBJ1)
    $(RC) $(RESINCS) $(RES1) $(TARGET)
$(OBJ1): $(SRC1)
    $(CC) $(CFLAG) $(CINCS) -o$(OBJ1) -c $(SRC1)
$(RES1): $(RC1)
    $(RC) $(RESINCS) $(RFLAG) /fo$(RES1) $(RC1) #ここを変更

これでビルドすると、実行ファイルができますがメニューがなく、結果ファイルには次のエラーが出ます。

(コンパイラー系は問題なし)

Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
Copyright (C) Microsoft Corporation.  All rights reserved.
fatal error RC1107: invalid usage; use RC /? for Help

** error 1 ** deleting "C:\Users\ysama\Programing\Windows Program\WinTemplate\Release\WinTemplate.exe"
おーい、何がまずいんだよぉ?

 

補足:リソースのところを

$(RES1): $(RC1)
    $(RC) $(RC1)
としてビルドしても結果は同じでした。

 

ということで次回。

まず手始めは「旧ボーランドBCC32.exe(Ver5.5.1)との互換性」です。

 

先に作った簡単なウィンドウプログラムでありHelloWorldをBCCDeveloperとBCC325.5でコンパイルすると当然に完了します。

ところが実行ファイルHelloWorld.exeと同じReleaseフォールダーに入っているHelloWorld.makを使って、コンパイラーをbcc32c、リソースコンパイラーをrc.exeにしてコンパイルすると、

とエラーになります。(なお、コンパイラーをbcc32、リソースコンパイラーをbrc32.exeにしてコンパイルすれば勿論成功します。)

画面ではわかりにくいので、コンパイラーのエラー出力を下に示します。

【bcc32cのエラーメッセージ】

MAKE Version 5.41  Copyright (c) 1987, 2014 Embarcadero Technologies, Inc.
    "C:\Borland\BCC102\bin\bcc32c.exe" -W -6 -O2 -w- -AT -pc -H- -k -b -I"C:\Borland\BCCForm" -o"C:\Users\ysama\Programing\Borland C++\HelloWorld\Release\HelloWorld.obj" -c "C:\Users\ysama\Programing\Borland C++\HelloWorld\HelloWorld.cpp"
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Warning: option '-6' is not supported in Clang-based compiler.
Warning: option '-H-' is not supported in Clang-based compiler.
Warning: option '-k' is not supported in Clang-based compiler.
C:\Users\ysama\Programing\Borland C++\HelloWorld\HelloWorld.cpp:

** error 1 ** deleting "C:\Users\ysama\Programing\Borland C++\HelloWorld\Release\HelloWorld.obj"

 

では、面倒くさいのでオプションを"-W"だけにしてビルドしますが、矢張りエラーとなります。また厄介なことに今度はエラーメッセージにヒントがありません。("-W"オプションに文句をつけていないことに注意してください。)

【bcc32cのエラーメッセ―ジ2】

MAKE Version 5.41  Copyright (c) 1987, 2014 Embarcadero Technologies, Inc.
    "C:\Borland\BCC102\bin\bcc32c.exe" -W -I"C:\Borland\BCCForm" -o"C:\Users\ysama\Programing\Borland C++\HelloWorld\Release\HelloWorld.obj" -c "C:\Users\ysama\Programing\Borland C++\HelloWorld\HelloWorld.cpp"
Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
C:\Users\ysama\Programing\Borland C++\HelloWorld\HelloWorld.cpp:

** error 1 ** deleting "C:\Users\ysama\Programing\Borland C++\HelloWorld\Release\HelloWorld.obj"

 

「何が問題なのか?」が全くわからないので、今度はBCCSkeltonを全く使わない、C++とWin32で書いたWindowTemplate.cppと簡単なメニューテンプレートMenuTeml.rcのシンプルなウィンドウプログラムを、単なるバッチファイル

C:\borland\bcc102\bin\bcc32c -W WinTemplate.cpp > bccerror.txt
C:\borland\bcc102\bin\rc MenuTempl.rc
pause

でビルドしてみます。

結果は、

C:\Users\ysama\Programing\Windows Program\WinTemplate>C:\borland\bcc102\bin\bcc32c -W WinTemplate.cpp  1>bccerror.txt
bcc32c.exe: error: linker command failed with exit code 2 (use -Xdriver -v to see invocation)
 

C:\Users\ysama\Programing\Windows Program\WinTemplate>C:\borland\bcc102\bin\rc MenuTempl.rc
Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
Copyright (C) Microsoft Corporation.  All rights reserved.
 

C:\Users\ysama\Programing\Windows Program\WinTemplate>pause
続行するには何かキーを押してください . . .

 

とコンパイルは失敗、リソースコンパイルは成功してresファイルができています。(Microsoftだからcoffで、omfファイルではないのかな?)

またエラー出力(bccerror.txt)には、

Embarcadero C++ 7.30 for Win32 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
WinTemplate.cpp:
Turbo Incremental Link 6.91 Copyright (c) 1997-2017 Embarcadero Technologies, Inc.
Error: Unresolved external '_main' referenced from C:\BORLAND\BCC102\LIB\WIN32C\DEBUG\C0X32.OBJ
Error: Unable to perform link

 

コンソールアプリ("_main"ベース)になっており、"-W"をつかってもウィンドウアプリ("_winmain"ベース)ではなくなっています。

ということで、今度はエラー表示にある通り、" -Xdriver -v"を付け加え、"-W"を"-tW"にして、リソースコンパイラーもbrc32で試します。

C:\borland\bcc102\bin\bcc32c -tW  -Xdriver -v WinTemplate.cpp > bccerror.txt
C:\borland\bcc102\bin\brc32 -feWinTemplate.exe MenuTempl.rc
pause

結果は、やたらメッセージが出て来ましたが、

なんとか成功です。

 

結論的には、

Warning: option '-6' is not supported in Clang-based compiler.
Warning: option '-H-' is not supported in Clang-based compiler.
Warning: option '-k' is not supported in Clang-based compiler.

Error: Unresolved external '_main' referenced from C:\BORLAND\BCC102\LIB\WIN32C\DEBUG\C0X32.OBJ
という警告やエラーがでましたが、bcc32cのヘルプによれば、

Embarcadero CLANG 7.30 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
Available options (* = default setting, xxx = has sub-options: use -h -X):
(Note: -X- or -w-XXX will usually undo whatever was set or unset by -X or -wXXX.
 If two options conflict, the last one specified will be used.)
(省略)

  -6      Generate Pentium Pro instructions

(省略)

  -Hxxx   Generate and use precompiled headers

(省略)

 -W      Target is a Windows application
 (省略)

 -k      Generate standard stack frames
 とあり、「単純にbcc32c.exeのhelp表示を信じてはいけない」、また「エラーメッセージは小出しに出されるので、今出ていなくともエラーになっていることがあり得る」ということが実証できたのではないか、と思います。

 

大分疲れたので、今回はここまでにします。

 

2020年にリタイアし、20年のブランクから戻ってBCCを使おうと思った時は新しいECB CommunityEditionを使い、BCCForm and BCCSkeltonに戻ることは考えていませんでした。しかし、Embarcadero C++ Builder(ECB)がメモリーエラーを頻発したことと、作成されたRelease版の実行ファイルが矢鱈でかいので、20年前のBCC5.5(でコンパイルでき、Win10で走るものものなら)を使おうと思ったことと、快適な開発環境であるBCC Developerではコンパイラーに"bcc32.exe"しか選択できず、最新のフリーのEmbarcadero C++コンパイラー(ECC)の"bcc32c.exe"や"bcc32x.exe"が選択できないことが理由でした。

しかし、FileHandlerとTextToSpeechを開発した際には新しいヘッダーやライブラリーを利用するしかなく、その為にECBに入っている開発環境を利用したので、コンパイルはBCCMakerを使ってコンパイルを重ねました。

そんな際に感じたのはbcc5.5で通っていた"-W"(ウィンドウアプリのオプション)が通らず、"-tW"にしなければならないとかヘルプとは異なる動作とか、Microsoftの資料通りのインクルードヘッダーではエラーが出てしまい、Microsoftの資料で"#include <shlobj.h>"または<shlwapi.h>のところを、Embarcadero C++では"#include <Winapi.ShlObj.hpp>"とか"#include <Winapi.ShLwApi.hpp>"とかしなければならないとか、試行錯誤を繰り返さねばならなかったMicrosoftと旧Borland、現Embarcaderoの深い溝でした。

 

現在でもBCC5.5はウェブの片隅で入手可能ですが、最新のEmbarcaderoのフリーコンパイラーは次のような特徴を持っています。

・ほぼ完全に異なる コマンドライン オプション
・より厳密に C++ 言語標準に準拠(CLANG(注1))
・より具体的かつ詳細な新しい警告およびエラー メッセージ
注1:CLANG-"Clang: a C language family frontend for LLVM(注2)"(ClangプロジェクトはLLVMを目的にしたC言語ファミリー(集団-C, C++, Objective C/C++, OpenCL, CUDA, and RenderScript)の為の言語インターフェースとツールの基盤を提供します。)
注2:LLVM-LLVMとは、コンパイル時、リンク時、実行時などあらゆる時点でプログラムを最適化するよう設計された、任意のプログラミング言語に対応可能なコンパイラ基盤である。当初は、LLVMの名称の由来は、Low Level Virtual Machine の略であるとしていたが、現在は、何の頭文字でもないとしている。

 

具体的にどのように違うのかを先ず外景的に見てみましょう。

1. 所謂BCC32
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
サイズ826KB、2000年8月25日(ファイルスタンプの更新日時)

2. Embarcadero C++ Builder Community Edition付属BCC32
Embarcadero C++ 7.40 for Win32 Copyright (c) 1993-2017 Embarcadero Technologies, Inc.
Preliminary version built Aug 23 2019 08:48:10
サイズ1.54 MB、2019年11月14日

3. Embarcadero C++ Compiler BCC32C
Embarcadero CLANG 7.30 Copyright (c) 2012-2017 Embarcadero Technologies, Inc.
サイズ17.0 MB、2018‎年‎3‎月‎9‎日

 

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

サイズ16.8 MB MB、2018年3月9日

 

次回以降では新しいECCについて調べたことの紹介と、現在のBCCForm and BCCSkeltonのBCC32C対応

についてstruggleする様を描いてみたいと思います。

 

【ご参考】

Embarcadero社による「BCC32C」の説明
http://docwiki.embarcadero.com/RADStudio/Sydney/ja/BCC32C

Embarcadero社による「Clang 拡張 C++ コンパイラについて」の説明
http://docwiki.embarcadero.com/RADStudio/Sydney/ja/Clang_%E6%8B%A1%E5%BC%B5_C%2B%2B_%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9
・「BCC32C は、従来のコンパイラ フラグ(BCC32)と互換性があります。」

Embarcadero社による「Clang 拡張 C++ コンパイラと旧世代の C++ コンパイラの違い」の説明
http://docwiki.embarcadero.com/RADStudio/Sydney/ja/Clang_%E6%8B%A1%E5%BC%B5_C%2B%2B_%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%A8%E6%97%A7%E4%B8%96%E4%BB%A3%E3%81%AE_C%2B%2B_%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9%E3%81%AE%E9%81%95%E3%81%84

BCCForm and BCCSkeltonの簡単な開発手順の説明をしました。今回リリースしている2021版は旧いソフトの更新や、新しいソフトのサンプルが実行ファイルとソースコード付きで公開されているので参考になると思います。

しかし、

実は20年前のBCC5.5環境ではビルド出来ないサンプルが二つあります。それらは、

FileHandler

TextToSpeech

です。

これらは(下手にいじるとPCを壊しかねず、20年前は避けていた)COMサ-ビス(SHELLとSAPI)を使っており、20年前のBCC5.5にはサポートするヘッダーもライブラリーもありませんでした。

ではこれらをどうやって開発したのか、というと、Embarcadero社のC++ Builder(以下「ECB」)、Community Editionをダウンロードして使ったからです。(とはいっても開発環境はC++Builderではなく、BCCForm and BCCSkeltonですが。汗;)

現在当該Community Editionは1年のフリー使用ライセンスが切れて使えず、これらは再コンパイルできない状態であり、ライセンス期間のない新しいEmbarcadero C++コンパイラー、BCC102(以下「ECC」)で再コンパイルできないか、と考えたことが表題の謎の始まりでした。

 

最後の仕上げが、プログラムの本来の機能の実装です。(逆に言えば今まではその土台を作っていたにすぎません。)

 

実際のプログラムの動作を規定するのがProc.hファイル(場合によってはそれを補完する追加のUser.hファイル)です。今回のHelloWorldProjectの要求仕様である「”Hello World!!"を(視覚的に)面白く表示する。」は具体化されて、「ウィンドウ上で複数の文字列表示を行う。」→「(1)文字列表示は、ウィンドウタイトル、ウィンドウクライアントエリア、ダイアログ、メッセージボックス、ステータスバーとする。」となりました。それに対応するのがメニューの

「    POPUP "ファイル(&F)"
    {
        MENUITEM "終了(&X)", IDM_EXIT
    }
    POPUP "表示(&V)"
    {
        MENUITEM "画面表示(&D)", IDM_DISP
        MENUITEM "ウィンドウタイトルの表示(&T)", IDM_TITLE
        MENUITEM "ステータスバーの表示(&S)", IDM_STATUSBAR
    }
    POPUP "ヘルプ(&H)"
    {
        MENUITEM "バージョン情報(&V)", IDM_VERSION」

であり、それぞれのメニュー項目に対応する関数が

「    //メニュー項目、ダイアログコントロール関連
    bool OnExit();
    bool OnDisp();
    bool OnTitle();
    bool OnStatusbar();
    bool OnVersion();」

でした。それを踏まえて以下説明します。

 

ダイアログは生成された段階でWM_INITDIALOGメッセージが出され割り込み処理が行われます。それに対応するのがOnInit関数です。

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

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

    //ステータスバー登録-SetHandle(hWnd))
    SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));
    //ステータスバー区画設定
//    int sec[2] = {200, 400};    オリジナル
    int sec[2] = {120, -1};        //変更
    SBar.SetSection(2, sec);
    //ステータスバー文字列設定
    SBar.SetText(0, "HelloWorld Ver1.0");
//    SBar.SetText(1, "");        オリジナル(不要)

    //追加-CANVASインスタンスの対象ウィンドウをIDD_MAINのハンドルで初期化
    cvs.SetCanvas(m_hWnd);

    return TRUE;
}
ここでは↑のコメントのとおり、自動生成コードについてステータスバーに関する「200,400ピクセルの2区画を120ピクセルと残余の2区画に変更」し、ブランクのままにする第2区画の記述を削除しています。また、仮想ウィンドウインスタンスの対象ウィンドウをメインウィンドウ(ダイアログ)に設定しています。(BCCSkeltonのウィンドウハンドルは、CMyWndのようなメインウィンドウの中でも、VERSIONDLGのようなダイアログクラスの中でも常に"m_hWnd"です。)

 

次にIDM_EXITに対応する関数の定義が

bool CMyWnd::OnExit() {

    //追加-典型的な終了処理(SendMsgはSendMessageのBCCSkelton版)
    SendMsg(WM_CLOSE, 0, 0);
    return TRUE;
}
で、コメントにある通りの"SendMessage(m_hWnd, WM_CLOSE, 0, 0);"と等価のBCCSkelton関数を記述します。

 

次のIDM_DISPは長いので後回しにして、ウィンドウタイトルを設定するIDM_TITLEは

bool CMyWnd::OnTitle() {

    //追加-ダイアログタイトルに文字列を表示、なお"(LPARAM)"はキャスト)
    SendMsg(WM_SETTEXT, 0, (LPARAM)g_lpMsg);
    return TRUE;
}
単に"SendMessage(m_hWnd, WM_SETTEXT, 0, (LPARAM)g_lpMsg);"と等価のBCCSkelton関数を記述します。コメントにある通り、キャストを外すとエラ―になりますのでご注意を。

 

ステータスバーの第2区画に表示するIDM_STATUSBARもWin32で書くと長くなりますが、単純に

bool CMyWnd::OnStatusbar() {

    //追加-第2セクション(引数は1)に文字列を表示
    SBar.SetText(1, g_lpMsg);
    return TRUE;
}
これだけになります。SBarはIDD_MAINダイアログのステータスバー(コントロール)に紐づけた(注)CSBARクラスのインスタンスでSetTextメンバー関数を使っています。

注:"SBar.SetHandle(GetDlgItem(m_hWnd, IDC_STATUSBAR));"の部分です。

 

さて、飛ばしていたIDM_DISPの関数は次のようになります。ここではWin32でウィンドウに文字を書くコードとBCCSkeltonのコードを比較できるようにしました。(その関係で使い勝手の悪いPrintText関数を改造したPrintTextEx関数を追加して使っています。このメンバー関数は作った後、ダウンロードファイルを更新してアップしていますので、「そんな関数がない」とエラーが出る場合は再度BCCForm and BCCSkeltonをダウンロードしてBCCSkeltonフォールダーのCANVAS.hを確認してください。)

bool CMyWnd::OnDisp() {

    //追加-retuirn TRUEまですべて
    /////////////////
    //Win32での表示//
    /////////////////
    //クライアントエリアサイズを取得(RECT m_clientはCMyWndのメンバー関数)
    GetClientRect(m_hWnd, &m_client);
    //デバイスコンテキストの取得
    HDC hDC = GetDC(m_hWnd);
    //フォント変更
    HFONT hFont = CreateFont(48, 0,
                    0, 0, FW_BOLD,
                    FALSE, FALSE, FALSE,
                    SHIFTJIS_CHARSET,
                    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                    DEFAULT_QUALITY,
                    DEFAULT_PITCH,
                    "MS Pゴシック");
    HFONT hOldFont = (HFONT)SelectObject(hDC, hFont);
    // テキストカラー変更
    SetTextColor(hDC, RGB(0xff, 0x00, 0x00));
    //テキスト表示
    DrawText(hDC, g_lpMsg, -1, &m_client, DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_NOCLIP);
    //表示確認の為一旦停止
    MessageBox(m_hWnd, g_lpMsg, g_lpMsg, MB_OK | MB_ICONEXCLAMATION);
    SelectObject(hDC, hOldFont);
    DeleteObject(hFont);
    ReleaseDC(m_hWnd, hDC);
    //////////////////////////////////
    //仮想ウィンドウCANVASによる表示//
    //////////////////////////////////
    //画面のクリア
    cvs.Clear();
    //新しい関数PrintTextExによるテキスト表示
    cvs.PrintTextEx(g_lpMsg, &m_client, RGB(0x00, 0x00, 0xff),
                    "Times New Roman", 48,
                    DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_NOCLIP);
    return TRUE;
}
Win32のコードに比べ、BCCSkeltonでの表記が随分簡素になったことが分かると思います。なおヘルプに記載がないので説明するとPrintTextExの引数定義は次のようになっています。

bool CANVAS::PrintTextEx(LPCTSTR str, LPRECT lprec, //出力文字列とDrawText関数で使うRECT変数へのポインター

                COLORREF rgb, //SetTextColorで使うCOLORREF変数

                LPCTSTR FontName, int FontSize, //CreateFontで使うフォント名、フォントサイズ
                UINT fmt = DT_LEFT | DT_NOCLIP, //DrawTextのフォーマットオプション

                //ここからはデフォルト値を与えているので省略可
                int nEscapement = 0, int nOrientation = 0, int nWeight = FW_NORMAL, //文字送り方向角度、X軸との角度、文字太さ
                DWORD fdwItalic = FALSE, DWORD fdwUnderline = FALSE, //イタリックか否か、下線付きか否か
                DWORD fdwStrikeOut = FALSE, DWORD fdwCharSet = DEFAULT_CHARSET) //打消し線付か否か、キャラクターセット

 

最後にバージョン表示ダイアログに関してです。IDD_VERSIONDLGダイアログについてはメインウィンドウでの呼び出しは

bool CMyWnd::OnVersion() {

    //追加(m_hWndはIDD_MAINのハンドル、versiondlgProcはコールバック関数アドレス)
    versiondlg.DoModal(m_hWnd, "IDD_VERSION", versiondlgProc);
    return TRUE;
}
とVERSIONDLGダイアログクラスのインスタンスversiondlgのメンバー関数DoModalで、モーダルダイアログとして呼び出します。

 

呼び出されたダイアログでの対応は

bool VERSIONDLG::OnIdok() {

    //追加(DoModalと対-クラスの関数定義なのでインスタンス名versiondlgは不要)
    EndModal(TRUE);
    return TRUE;
}
と「追加コメント」の通り、あっさり記述されます。(なおEndModal関数の引数はversiondlg.DoModal関数の戻り値となります。)

 

以上で6回に亘って定番のHelloWorldプロジェクトの説明を行いました。いかがだったでしょうか?BCCForm and BCCSkeltonはフールプルーフなWYSWYG開発環境ではなく、C++とWin32のプログラミングに一定慣れた人が開発効率を上げるためのツールであることが実感されたと思います。

 

しかし、単に"Hello World!!"と表示されるだけのプログラムを組んでも(自分の学習意欲を満足させる以外の実益はなく)いずれあきてしまいます。やはり「実際に使えるソフト」を作ることがプログラミングの醍醐味でしょう。しかし個人のプライベートライフで使えるプログラムは、実は、余りなく(またその多くは既に先人が素晴らしいものを作っており)、「何を作ろうか?」が大きな問題となってきます。

 

私の場合、もともとBCCFormやBCCSkeltonはC++によるWin32プログラミング学習のために自作したわけですが、BCCMakerやBCFEditor、TBEditorやEZImage(と対になるIconViewer)はやはり開発環境として作成し、「個人のプライベートライフで使えるプログラム」ではありませんでした。機会があればそのようなプログラム開発を行う際に、同時進行でブログで開発状況を紹介してゆきたいと考えています。