Borland C++Builder の覚え書き

初版作成  2004.07.21
最終更新  2024.04.01


◆◆ C++Builder ver.6 の謎のエラーへの対処法 ◆◆


プロジェクトを新規作成して、「プロジェクトに名前を付けて保存」
した後、コンパイルすると

[リンカ 致命的エラー] Fatal: ファイル名が必要 :

というエラーが出ることがある。ディレクトリの深さが深すぎると、
エラーが出るように思われる。以下のように対処する。
**.dpr ファイルをエディタで開くと、

<INCLUDEPATH value="&quot;C:\Program Files\Borland\CBuilder6\Projects&quot;;[この部分];$(BCB)\include;$(BCB)\include\vcl"/>
<LIBPATH value="&quot;C:\Program Files\Borland\CBuilder6\Projects&quot;;[この部分];$(BCB)\lib\obj;$(BCB)\lib"/>

の [この部分] にプロジェクトを保存したフォルダが絶対パスで入っている。
LIBPATH に含まれている [この部分] を削除するとエラーは出なくなる。謎である。

ネットワークドライブを使用したときも同様のエラーがでる。この時にも
上記の対処法が有効か否かは未確認である。

◆◆ C++Builder の ver.5 以前のバージョンで開発したプロジェクトの ver.6 への移行 ◆◆


1.「ファイル」→「新規作成」→「アプリケーション」で新規アプリケーションを作成し、
  「ファイル」→「プロジェクトに名前を付けて保存」でフォルダに保存する。
2.「プロジェクト」→「プロジェクトから削除」で、全てのフォーム、リソースを削除する。
3. 移行元のプロジェクトのフォルダから、cpp, h, dfm, res ファイルをコピーしてくる。
   新規アプリケーション作成時に自動生成された Unit1.* などは不要なので削除する。
4. 「プロジェクト」→「プロジェクトに追加」で cpp, res ファイルを追加する。

◆◆ C++Builder の ver.1 で開発したプロジェクトの ver.5 への移行 ◆◆


バージョン 1.0 のプロジェクトファイルを 5.0 用に移行する方法

1.「ファイル」→「プロジェクトを開く」→「ファイルの種類」を
  「C++Builder 1.0 のプロジェクト(mak)」でプロジェクトファイル ◯◯.mak を開く。
2. ソースファイルのうち、#include &lt;stdio.h&gt; を記述していない
   ファイルは記述する。
3. 「ファイル」→「全て保存」で ◯◯.dpr ファイルを作成する。
4. ◯◯.dpr をエディタで開き、LIBRARIES と SPARELIBS の項目の部分を
   以下のように直す。

<LIBRARIES value="vcl50.lib">
<SPARELIBS value="vcl50.lib">

5.「ファイル」→「全て閉じる」のあと、「ファイル」→「プロジェクトを開く」
   で修正した ◯◯.dpr ファイルを開き直す。
6. 「プロジェクト」→「◯◯を再構築」でコンパイル〜リンクをやり直す。

◆◆ ランタイムライブラリ不要の exe ファイルを作成する ◆◆


C++Builder 1.0 ではデフォルトでランタイムライブラリ不要の exe ファイル
を作成していたが、C++Builder 5.0 ではデフォルトでランタイムライブラリが
必要な exe ファイルを作成する。C++Builder 5.0 でランタイムライブラリ
不要の exe ファイルを作成する方法は以下の通り。

「プロジェクト」→「オプション」のウィンドウにおいて

> 「パッケージ」で「実行時パッケージを使って構築」のチェックを外す
> 「リンカ」で「共有 RTL DLL を使う」のチェックを外す


◆◆ ディレクトリを選択する ◆◆


SelectDirectory という関数を使えばよい。

使い方はヘルプの「例」を参照。SelectDirectory を使うには

#include <FileCtrl.hpp>

が必要であるが、このインクルード文は *.cpp に書いてはいけない。
*.h の中に書く必要がある。

*.cpp の中に書き、かつ「実行時パッケージを使って構築」の
チェックを外した場合、「外部参照が未解決です」というリンクエラーが発生する。

◆◆ どの解像度のウィンドウで起動しても適切な位置にフォームを配置する ◆◆


Form1.Scaled = false : 起動するディスプレイの解像度によらず
                       固定サイズのウィンドウを開く
Form1.Posision = poDefaultPosOnly : サイズは設計時のままで、
                       位置は Windows が規定する位置にウィンドウを開く
Form1.BorderIcons.biMaximize = false : 最大化できなくする

◆◆ ボタンに表示する文字列を改行する ◆◆


情報源  http://tokyo.cool.ne.jp/purasupurasu/bcb.html#n9

フォーム生成時に、ボタンの属性を Windows の API を使って変更する。

Button1->Caption = "ab............\n............";
long bsp;
// Window の情報を取得する API。ボタンもウィンドウの一種 GWL_STYLE はスタイルを取得する
bsp = GetWindowLong(Button1->Handle, GWL_STYLE);
// Window の属性を定義する。ウィンドウハンドル, 属性, 新しい値
SetWindowLong(Button1->Handle, GWL_STYLE, bsp | BS_MULTILINE);

◆◆ Tstrings クラスの使い方 ◆◆


RadioGroup1->Items->Strings[0] = "abc";
RadioGroup1->Items->Add("abc");
RadioGroup1->Items->Delete("abc");
index = RadioGroup1->Items->Indexof("abc");
RadioGroup1->Items->Count;

◆◆ スレッドの使い方 ◆◆


「ファイル」→「新規作成」→「その他」→「スレッドオブジェクト」
でスレッド用のユニットが作成される。

スレッド用ユニットが Unit2.cpp でメインフォームのコードが Unit1.cpp だと
すると、お互いに #include "Unit1.h" のようにインクルードしあうことが
必要である。

TThread を継承して新しいスレッドクラスが作成される。
名前を TMyThread とすると

__fastcall TMyThread::TMyThread(bool CreateSuspended)
    : TThread(CreateSuspended)
{
}

となり、コンストラクタの引数である bool 型変数を TThread の
コンストラクタに渡す。

false にすると new の瞬間に開始する。
true にすると Resume() メソッドで明示的に開始する。

false にするとコンストラクタが終了する前に Execute が開始するので、
true を推奨するとヘルプに書いてある。

私は以下のようにしている。
コンストラクタの引数を変更したときは、Unit2.h も書き換えることを
忘れないように。

__fastcall TMyThread::TMyThread( 初期化用の引数 )
    : TThread(true)
{
  初期化処理
}

コンストラクタは以下のようになる。

TMyThread *thread;
thread = new TMyThread( 初期化用の引数 );
thread->Resume();

スレッド本体を関数 Execute() の中に記述する。
しかし、Execute() は呼ぶことが出来ない。Resume() で開始させる。

Terminate() メソッドを呼ぶと、デフォルトで用意されている
変数 Terminated が true となる。
Execute の中で無限ループを組む場合は、以下の処理を入れる。

    if ( Terminated == true ) return;

Execute() から return すると、スレッドのインスタンスは消滅するようだ。
もう一度、Resume() しても Execute() は実行されない。

一時停止させるには Suspend() メソッドを呼ぶ。

以下のことは大変重要である。
ボタンを押して呼ばれるコールバック関数の中で、
スレッドを new して Resume() したとき、そのコールバック関数
から return するまで、スレッドは開始しない。例えば、
以下のような場合、thread->Execute() が
開始するのは Sleep(1000) の後である。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    thread = new TestThread(Memo1);
    thread->Resume();

    Sleep(1000);
}

スレッドからメインフォームのメソッドを呼ぶとき、
Symchronize メソッドを使わないと、効かないことがある。
以下のように記述する。

-------- Unit2.h ----------
protected:
    void __fastcall Execute();
    void __fastcall Form_Hide();

-------- Unit2.cpp ---------
void __fastcall TMyThread::Form_Hide()
{
    Form1->Hide();
}

void __fastcall TMyThread::Execute()
{
    // TODO : スレッドとして実行したいコードを以下に記述 */

    Synchronize(Form_Hide);

----------- ここまで -----------

◆◆ グローバル変数の使い方 ◆◆


Unit1.cpp とUnit2.cpp で共通の変数を使うとき、
Unit1.h の中で int abc; と
宣言して Unit1.cpp と Unit2.cpp において #include "Unit1.h" とするのは
ダメである。別々の変数と見なされる。