2022年9月16日:大変お騒がせしましたが、本件は本日解決し、↓に詳しく述べております。なお、下にあるコードは修正箇所を青太字で明記しておきますので、ご確認ください。

 

【オリジナルブログ】

現在新しいCSTRのFind、Replace、ReplaceAll関数を使ったツール、BCC2ECCを開発しています。

そんな中で、

怪談「bcc32c(bcc102)でコンパイルすると意図通り正常動作するのに、bcc32(bcc55)でコンパイルすると突然落ちる」

という怪奇現象に遭遇しました。

 

何を大げさな。あんたのバグでしょ?」とおっしゃるあなた、以下をよく読んで何が原因か教えてください。

 

1.現象

(1)CSTRの新しいReplaceAll関数で「, "」という文字列(ToFind 文字列、", \"")を「, L"」という文字列(ToReplace 文字列、", L\"")に置き換える処理を行っている際、

(2)最初の「, "(MS 明朝")」、次の「, "(STATIC")」は意図通りの動作をするが、三番目の「, "(EDIT")」で、','の位置に0(NULL)を代入できない。

(3)その結果文字列のメモリー領域を超えて文字列が付加され落ちる。

 

2.該当箇所のコード(デバッグ用のメッセージボックス付)

//文字列の置換
char* CSTR::Replace(char* ToFind, char* ToReplace, char* sp = 0) {

    //CSTRのデータ長、検索文字長、置換文字長を計算する
    int OrgLen = lstrlen(m_str);
    int ToFindLen = lstrlen(ToFind);
    int ToReplaceLen = lstrlen(ToReplace);
    //spの指定が無ければCSTRデータの先頭から検索する
    if(!sp)
        sp = m_str;
    //検索文字列ポインターの取得
    char* Found_at = strstr(sp, ToFind);        //検索文字列が無ければNULLを返す
    //検索文字があれば
    if(Found_at) {
        DWORD dspl = Found_at - m_str;            //Found_atのm_str内の相対位置
        //置換後の文字列データバッファー(NULL終端付)の確保
        char* buff = new char[OrgLen - ToFindLen + ToReplaceLen + 1];

MessageBox(0, Found_at, "Test - Found_at", MB_OK);    //①

        *Found_at = NULL;                        //NULL終端
        lstrcpy(buff, m_str);                    //検索文字列(ToFind)までをbuffにコピー

MessageBox(0, m_str, "Test - m_str", MB_OK);    //②

        lstrcat(buff, ToReplace);                //検索文字列(ToFind)の代わりに置換文字列をコピー

MessageBox(0, buff, "Test - buff", MB_OK);    //③

        lstrcat(buff, Found_at + ToFindLen);    //検索文字列(ToFind)以降の文字列をコピー
        buff[OrgLen - ToFindLen + ToReplaceLen] = NULL;    //NULL終端を付ける
        delete [] m_str;                        //CSTRデータ(m_str)を削除
        m_str = new char[OrgLen - ToFindLen + ToReplaceLen + 1];    //新しいデータ領域を確保
        lstrcpy(m_str, buff);                    //置換後のデータをコピー
        Found_at = m_str + dspl;                //Found_atのm_str内の相対位置
        delete [] buff;                            //文字列データバッファーを破棄
    }
    return Found_at;    //置換できなかった場合NULL、できた場合置換文字列の先頭ポインター
}

3.正常動作(, "STATIC")の場合のメッセージ

ToFindである「, "」(STATIC"...)を探しました。

 

「,」をNULLにして、それまでのm_str文字列をbuffに入れます。

 

ToFindの前のm_strをもらったbuffは末尾にToReplaceを追加します。

 

 

4.異常動作(, "EDIT")の時のメッセージ

ToFindである「, "」(EDIT"...)を探しました。ここまでは同じです。

 

「,」をNULLにした筈なのに、m_strは末尾までになっています。

 

その結果、buffにはm_str全文が入り、末尾にToReplace「, L”」を付加せざるを得なくなっています。

 

4.結語

正直私には何故「*Found_at = NULL; 」の文が正常に働かないのか、また何故第3番目の文字列で生じるのか、また何故bcc32(bcc55)で生じ、bcc32c(bcc102)では生じないのか、全くわかりません。

 

「実に面白い」

 

というガリレオ先生、いませんか?

 

ps. 実はCSTRクラスに関してはまだ怪談があり、それはある非公開のソフトで「外部変数で宣言したCSTRインスタンスにconst char*文字列を代入すると落ちる」という現象です。それも(そしてそれは真逆で)「bcc32(bcc55)でコンパイルすると正常に動作するのですが、bcc32c(bcc102)でコンパイルすると落ちる」のです。これも原因は不明であり、現在bcc55版だけを使っています。bcc2eccについてもbcc102版だけリリースすることになりそうです。