CBuilder

C++Builder VCL 脆弱性への対応完了

私が開発・公開しているソフトウェアの中には、開発環境として C++Builder を用いているものがあります(ありました)。

C++Builder(VCL)には JVNVU#97910946 という脆弱性があり、悪意のある画像ファイルの影響を受けます。2015 年 5 月現在、C++Builder 開発元のエンバカデロ社からは、未だに完全な対応策は出されておらず(一部は対応されています)、C++Builder で開発されたアプリケーションもこの影響を受ける恐れがありました。

そこで私のほうでは、ソフトウェアごとに以下の確認・対応を取ることにより、現行品についてはすべて、脆弱性の影響を受けないと考えており、安心してご利用頂くことができるようになりました。

一般向けソフトウェア

  • 唄詠……外部画像ファイルを読み込む部分を Visual Studio(C#)で開発するように切り替えました。Ver 7.00 以降、脆弱性の影響を受けません。
  • うたりす……外部画像ファイルを読み込まないため、脆弱性の影響を受けません。
  • すっぴんプラグイン……外部画像ファイルを読み込まないため、脆弱性の影響を受けません。
  • 鼻歌採譜プラグイン……外部画像ファイルを読み込まないため、脆弱性の影響を受けません。
  • プログレスバー素材メーカー……Visual Studio(C#)で開発するように切り替えました。Ver 3.00 以降、脆弱性の影響を受けません。

開発者向けソフトウェア

  • Lib UTAU……ライブラリ自体は特定の開発環境に依存しないため、そもそも脆弱性の影響を受けません。また、サンプルプログラムは C++Builder で開発していますが、外部画像ファイルを読み込まないため、脆弱性の影響を受けません。
  • UTAU Tool Proxy……外部画像ファイルを読み込まないため、脆弱性の影響を受けません。
  • 唄詠調声プラグイン SDK……もともと Visual Studio(C++)で開発しているため、脆弱性の影響を受けません。
  • ちょちょいと自動更新……Visual Studio(C#)で開発するように切り替えました。Ver 3.00 以降、脆弱性の影響を受けません。

補足

旧バージョン/旧ソフトウェアについては脆弱性の影響を受ける場合がありますので、特別な理由が無い限り、使用しないことをお勧めします。


C++Builder 脆弱性への対応方法を検討中

既報の通り、C++Builder(および Delphi)にはバッファーオーバーフロー脆弱性がある。C++Builder XE6 で報告されているが、実際には、すべてのバージョンで影響がある。

対応策が公開されているように見えるが、実は、俺が使っている C++Builder XE3 Starter Edition はその方法では修正されない。エンバカデロのサポートからは XE7 を買ってくれと言われたので、つまりは修正するつもりがないということだ。1 年半前まで販売していた製品の欠陥を放置するなんて、ずいぶん酷い話である。

今後どうするか。いくつかの選択肢を検討しているが、どれも一長一短で困った。

選択肢 1:XE7 を買う

CBuilder32 万円の費用がかかるが、今までと同様の開発環境を使えるのがメリット。プロジェクトの修正が最小限で済むため、労力的には一番楽。

しかし、1 年半後に XE7 の欠陥が出た場合は、今回同様放置されることが予想されるため、一時しのぎにしかならないとも言える。一時しのぎに数万円は割に合わないだけでなく、欠陥が出る度に費用がかさむ構図となる。短期的には良い選択肢だが、長期的には下策。

そもそも、欠陥が嫌なら新しいの買えというのは、悪徳商法の香りがするが、消費者庁的にはアリなのだろうか。C++Builder は 15 年以上愛用してきたシリーズなので(初めて使ったのはたぶん初代か XE じゃないほうの 3)、思い入れはあるのだが……。

選択肢 2:Visual Studio Professional を買う

Visual Studio Express は C++ での RAD 開発ができないが、Professional なら RAD 開発ができるようである。

C++Builder と同じく、言語が C++ なので、比較的乗り換えやすいと推測される。

しかし、価格が 6 万円と高いだけではなく、ライブラリが MFC というのがまた使いづらそうである。というかきっと使いづらい。そもそも Visual Studio(当時は別の名前だったと思うが)が使いやすかったら、C++Builder 使ってなかったし……。

選択肢 3:Qt を使う

Linux 由来の GUI ライブラリである Qt を使うというのも一つの手。

Linux の GUI というと重くてダサいという悪いイメージしかなかったが、最近の Qt は、Windows API を直接制御しているので軽くなってきているようだ。

フリーなのでお金がかからず、また、言語も C++ が使えて良い。

難点は、統合開発環境にイマイチ RAD 感がないこと。また、ライセンスが LGPL なので、(ダメだと確定している訳でもないようだが)ライブラリと静的リンクしない方が無難となり、配布ファイルがやたら増えて分かりづらくなる。日本語のドキュメントも少ない。

選択肢 4:C# を使う

Visual Studio Express に含まれるため、フリーの RAD という意味では最有力候補の 1 つ(もちろん VB の方がメジャーだが、さすがに Basic はちょっと)。

豊富なライブラリを擁する .NET 環境において主役に位置づけられているので、やれることは多いだろう。

問題は、俺が C# を全く知らないことで、1 から学習しなくてはならないので、習得までの労力が多大であること。

また、ユーザー環境に .NET が必要となるので、ユーザーさんに .NET をインストールしてもらう手間が生じる。

というわけで

手放しでこれだ、といえる選択肢がない。

そんな中、気持ちとしては C# に傾きつつある。これを機に新しい言語に挑戦してみようか的な感じで。

いろいろ検証しなくてはいけないので時間かかるけど……。

C++Builder バッファーオーバーフロー脆弱性について(VCL BMP)

私 SHINTA が公開している Windows 用ソフトウェアは、主に C++Builder を用いて開発されています。

C++Builder に脆弱性(JVNVU#97910946)が確認され、悪意のある BMP ファイルの影響を受けることが判明しました。

C++Builder の提供元であるエンバカデロは 2014/10/10 に対応策を公開しましたが、残念ながら、私が使っている C++Builder XE3 Starter Edition はこの方法では対応できません。

したがいまして、当面の対応策は以下となります。
  • ユーザーのみなさまが使用する際、悪意のある BMP ファイルを読み込まない
みなさまにはご不便をおかけいたしますが、注意してご利用下さいますよう、よろしくお願いします。

2015/05/28 追記:対応完了しました

Visual Studio の .lib を C++Builder にリンクするのは無理っぽい

Visual Studio (Visual C++) 2013 で作成したスタティックライブラリ(.lib)を、C++Builder XE3 のプロジェクトに組み込みたいのだが、無理のようだ。

VC は COFF というオブジェクトフォーマットを採用しており、一方で、C++Builder は OMF を採用している。このため、単純に、VC の .lib を C++Builder に組み込んでリンクしようとすると、

[ilink32 エラー] Error: '...\HOGE.LIB' には無効な OMF レコード (タイプ 0x21) が含まれています (COFF の可能性もあります)
というエラーとなってリンクできない。

形式が違うのなら変換すればいいじゃない、ということで、C++Builder に付属の coff2omf.exe を使おうとするものの、これは通常のスタティックライブラリを変換するためのツールではなく、インポートライブラリを変換するためのものなので、うまくいかない。

サードパーティーのツールはどうか。objconv を試してみた。

ojbconv -fomf in.lib out.lib
のようにして使ってみたものの、警告がたくさん表示された。結果の .lib を C++Builder に組み込むと、

[ilink32 エラー] Fatal: EXTDEF フィックスアップ インデックス (モジュール 'xxx_00.obj' 内) は無効です
というエラーになってうまくいかない。

DLL を使うしかないか……。




C++Builder XE3 で BMP/JPEG/PNG 画像を合成する

C++Builder は、様々なフォーマットの画像を扱うのも簡単だ。……が、まとまった情報が見つからなくて苦労したので、ここにまとめておく。動作確認は C++Builder XE3。

例として、JPEG 画像を背景画像として、その上に、BMP 画像と透過 PNG 画像を重ねる。合成結果を PNG 画像として保存する。

背景 JPEGBMP透過 PNG
TestBG Test1
※Web 用に PNG に変換してあるが、元は BMP
Test2


準備として、ビットマップ形式以外の画像ファイルを読み書きしたい場合は、対応するヘッダをインクルードしておく。

#include <Jpeg.hpp>
#include <PNGImage.hpp>

ヘッダをインクルードしてしまえば、読み込みに関しては、画像フォーマットを気にせずに読み込めるようになる。書き込みは、対応する画像形式を扱うクラスに任せる。コードは以下。

void __fastcall TFormTest01::Button1Click(TObject *Sender)
{
    unique_ptr<TImage>        aImageMerge(new TImage(this));
    unique_ptr<TImage>        aImageBG(new TImage(this));
    unique_ptr<TImage>        aImage1(new TImage(this));
    unique_ptr<TImage>        aImage2(new TImage(this));
    unique_ptr<TPngImage>    aPng(new TPngImage());

    // 画像ロード
    aImageBG->AutoSize = true;
    aImageBG->Picture->LoadFromFile(L"R:\\TestBG.jpg");    // JPEG
    aImage1->AutoSize = true;
    aImage1->Picture->LoadFromFile(L"R:\\Test1.bmp");    // BMP
    aImage2->AutoSize = true;
    aImage2->Picture->LoadFromFile(L"R:\\Test2.png");    // PNG

    // 合成画像のサイズを背景画像に合わせる
    aImageMerge->Width = aImageBG->Width;
    aImageMerge->Height = aImageBG->Height;

    // 合成
    aImageMerge->Canvas->Draw(0, 0, aImageBG->Picture->Graphic);
    aImageMerge->Canvas->Draw(100, 200, aImage1->Picture->Graphic);
    aImageMerge->Canvas->Draw(300, 200, aImage2->Picture->Graphic);

    // PNG 保存
    aPng->Assign(aImageMerge->Picture->Graphic);
    aPng->SaveToFile(L"R:\\Result.png");
}

Result結果は右の画像のようになる。

シンプルなコードだが、透過もきちんと自動処理されている。

ポイントは、TImage を使うこと(ここでは動的生成しているが、もちろんフォームに貼り付けても良い)。TImage のローダーは、画像形式に関わらず読み込める。TBitmap を使うと PNG を読み込めなかった。

古いバージョンの C++Builder/Delphi だと、PNG の読み込みには TPngImage で画像の読み込みを行わなければならなかったようだが、現在は、TImage を使えば良い。

逆に、書き込みは、明示的に TPngImage を使っている。

aImageBG->Picture->SaveToFile(L"R:\\Result.png");

のように拡張子で自動的に PNG に変換してくれるかなーと期待したが、そんなことは無かった。



文字コードを指定してメモリから文字列を読み込む(C++Builder XE3/TMemoryStream)

C++Builder は、ファイルの読み込みがとても簡単で、TStringList::LoadFromFile() により、文字コードを適切に処理して読み込んでくれる。

LoadFromFile() は見ただけで使い方が分かる。しかし、その親戚である、メモリからの読み込みを行う LoadFromStream() については、まとまった説明が見当たらなかったので試行錯誤。

で、以下が結果のコード。メモリに格納されている文字列を、文字コードを指定して TStringList に読み込む。

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    const char                  UTF8_STR[] = { 0xE6, 0x98, 0xA5 };
    unique_ptr<TMemoryStream>   aMemStream(new TMemoryStream());
    unique_ptr<TStringList>     aStrList(new TStringList());

    aMemStream->Write(UTF8_STR, 3);
    aMemStream->Seek(static_cast<long>(0), soBeginning);
    aStrList->LoadFromStream(aMemStream.get(), TEncoding::UTF8);
    ShowMessage(aStrList->Text);
}


UTF8_STR が文字列が格納されているメモリ領域。今回は定数にしてあるが、通常は、どこかから持ってきた内容を格納しているバッファになる。

TStringList::LoadFromStream() は、TStream 型(の派生クラス)からしかデータを読み出せない。メモリ領域をそのまま読むことはできないので、仲介役の TMemoryStream が必要になる。

TMemoryStream にメモリ領域の内容を書き込んでいるのが aMemStream->Write(UTF8_STR, 3); TMemoryStream にメモリ領域の内容を読み込むというイメージだったので、最初 Read() を使いそうになってしまったが、Write() なので注意。

Write() 後に、読み書き位置を先頭に戻しておく。

あとは、LoadFromStream() に TMemoryStream へのポインタを渡せば、文字列が読み込まれる。

文字コードの指定は、
  • UTF-8……TEncoding::UTF8
  • UNICODE(UTF-16)……TEncoding::Unicode
  • 指定無し……自動判別
など。TEncoding についてはここが詳しい。

以上、コードを実行すると、メモリ領域から 3 バイトを読み取って、「春」を取得できる。

LoadFromStream

C++Builder XE3 のインストール方法まとめ

目次


概要

CBuilderXE3メイン画面Windows(など)のアプリをビジュアルに開発できる C++ 開発環境、エンバカデロ社の C++Builder XE3 のインストールのやり方をまとめておく。

今回使用したのは C++Builder XE3 の、Starter ESD バージョンアップ版(約 14,000 円)だが、他のエディションでも参考になるのではないか。

Starter 版は、数あるエディションの中でも一番機能が少ない代わりに安い。比較表はこちら。通常のアプリを開発する分には困らないが、データベース接続やマルチプラットフォーム(Windows & Mac)機能などが省かれている。

ESD 版はいわゆるダウンロード版のこと。Starter には ESD 版しかない。他のエディションにはパッケージ版もあるが、ESD 版の方が安い。

ダウンロード

C++Builder XE3 の ESD 版を購入すると(今回は SE Shop.com にて購入)、(購入先からではなく)エンバカデロからメールでダウンロード情報が送られてくる。「Order Confirmation: C++Builder XE3 Starter Upgrade/Competitive Upgrade」というような英語の件名だ。

メール本文に C++Builder XE3 のネットインストーラ(約 51MB)のダウンロード先が記載されているのでダウンロードする。

インストール

インストールは簡単だが、それなりに手間暇がかかる。

ダウンロードしたネットインストーラ(cbuilder_xe3_win_esd.exe)を起動すると、言語選択画面になるので「Japanese」を選んで OK ボタンをクリック。

CBuilderXE3JSharpC++Builder のインストールには、JSharp のインストールが前提となる。JSharp がインストールされていない場合は、最初に JSharp をインストールすることになる。

CBuilderXE3DownloadFolderファイルのダウンロード場所を指定する画面になるので、空のフォルダを指定する。このフォルダにインストールされるわけではなく、あくまでもダウンロードしたファイル群を保存するだけのフォルダだ。

前提ソフトウェアのインストールが完了すると、いよいよ C++Builder XE3 のインストール。

CBuilderXE3Regist製品登録画面の Serial Number の欄には、エンバカデロから送られてきたメールに記載されている「インストール番号」をコピー&ペーストする。

言語選択画面では既に Japanese が選択されているので、デフォルトのまま進んで良い。

CBuilderXE3Func続いて、プログラム機能選択の画面なども基本的にデフォルトのままで良い。必要に応じて変更する。

CBuilderXE3InstallDownloadインストール準備完了画面で「次へ」ボタンをクリックすると、ファイル群のダウンロードとインストールが始まるのでしばらく待つ。

CBuilderXE3DocExplorerC++Builder XE3 本体のインストールが完了すると、続いてヘルプのインストール。Microsoft Document Explorer が無い環境では、さきにそちらをインストールすることになる。

本体のインストール時と同様、機能やダウンロードフォルダ、インストール先フォルダなどを指定すれば、ダウンロードとインストールが行われる。

ヘルプのインストールが完了すれば、ひとまずインストール完了だ。

初回起動と製品登録

CBuilderXE3Regist2初めて C++Builder XE3 を起動する際は、かなり待たされた後で、製品登録画面になる。

Serial Number はすでに入力されているので、Developer Network のアカウント情報を入力する。アカウントをまだ持っていない場合は、ここで登録する。

CBuilderXE3FirstStart登録に成功すると、C++Builder XE3 が起動する。

アップデートのインストール

デフォルトの設定であれば、C++Builder XE3 起動時に、自動的にアップデートのチェックが行われる。アップデートがある場合は、タスクトレイのアイコンで通知が来るので、クリックしてアップデートマネージャーのウィンドウを開く。

CBuilderXE3UpdateDownload適用したいアップデートを選択して「Download」ボタンをクリックすると、ネットインストーラのダウンロードが始まる。

ダウンロードが完了すると、再びタスクトレイのアイコンで通知が来るので、クリックしてアップデートマネージャーのウィンドウを開く。自動でインストールされるわけではないことに注意。

インストールしたいアップデートを選択して「Install」ボタンをクリックすると、インストールが始まる。

CBuilderXE3HelpUpdateアップデートのインストール方法も、C++Builder XE3 本体のインストール方法とほぼ同じ。ファイルダウンロード用のフォルダ等を指定してインストールすれば良い。

CBuilderXE3Aboutアップデートのインストールが完了したら、C++Builder XE3 を起動し、バージョン情報を確認する。「インストール済みの更新」の欄にアップデート内容が記載されていれば OK。

また、アップデートマネージャーには表示されないものの、こちらの登録ユーザーページには各種 Hotfix 等が公開されているので、インストールしたいものがあれば、ダウンロードしてインストールする。

Hotfix はインストーラではなく zip ファイル。zip 内のドキュメントで指定されているパスに zip を解凍するだけで良い。

以上で、すべてのインストールが完了する。



感想

インストールは簡単なのだが、まどろっこしい。

CBuilderXE3DownloadFail一番の原因はネットインストーラ。インストーラを起動してから各種ファイルをダウンロードするので時間がかかる。しかも、ダウンロードが途中で失敗すると、ダイアログが出て止まる。インストールの間放置しておいて、そろそろ終わったかなと画面を見てみると、途中で止まっているのはがっくりくる。

ネットインストーラがあるのは良いが、フル版も置いておいて欲しい。

C++ Builder XE Tips

C++ Builder XE (Update 1) を使ってく上での Tips を書き足していく予定。

boost ヘッダをインクルードするとエラーになる


#include <boost/algorithm/string.hpp>
のように boost のヘッダをインクルードすると、
[BCC32 エラー] tuple.hpp(58): E2209 インクルード ファイル 'boost/fusion/include/tuple.hpp' をオープンできない
というようなエラーになる。

解決策は、ここに書いてあるとおり、[ツール→オプション]メニューの「C++ オプション」「パスとディレクトリ」において、インクルードパスの、「$(CG_BOOST_ROOT)\boost\tr1\tr1\」を「$(CG_BOOST_ROOT)\boost\tr1」に修正する。複数ある場合は 1 つを残して削除する。

ウィンドウを透明・半透明にする


こちらの記事参照。

Delphi 用 RS-232C コンポーネントを使う


こちらの記事参照。





Delphi 用 RS-232C コンポーネントを C++Builder XE で使う

Delphi(および C++Builder の旧バージョン)で手軽に RS-232C(COM ポート)を使うためのコンポーネントに ComPort Library(TComPort)がある。C++Builder XE でも何とか動作したので、やり方を整理しておく。

【実行時パッケージの作成】

  • すべてのプロジェクトを閉じた状態で、[ファイル→プロジェクトを開く]メニューにて、ComPort Library の Source フォルダにある CPortLibCB6.bpk(C++Builder 6 用)を開く。
  • 自動的にプロジェクトが C++Builder XE 用に変換される。
  • ビルド構成を Release にする(右側のプロジェクトマネージャーペインで選択)。
  • メイクすると、CPortLibCB6.bpl ができあがる。


【設計時パッケージの作成】

  • [ファイル→プロジェクトを開く]メニューにて、DsgnCPortCB6.bpk を開く。
  • 実行時パッケージと同様にメイクし、DsgnCPortCB6.bpl を得る。

【パッケージのインストール】

  • CPortLibCB6.bpl を Windows のシステムライブラリフォルダ(Windows 7 64bit なら C:\Windows\SysWOW64)にコピーする。
  • [コンポーネント→パッケージのインストール]メニューにて、追加ボタンをクリックして DsgnCPortCB6.bpl を追加する。デフォルトにチェックを入れると毎回設定しなくて良いだろう。

【ComPort Library を利用するアプリケーションのメイク】

  • ツールパレットペインに「CPortLib」タブが増えていて、その中の TComPort をフォームに貼り付ければ RS-232C にアクセスできるようになる。
  • [プロジェクト→オプション]メニューで、ディレクトリと条件定義→ライブラリパスに、CPortLibCB6.bpl/DsgnCPortCB6.bpl のパスを追加する。
  • [プロジェクト→オプション]メニューで、ディレクトリと条件定義→条件定義に DONT_USE_WINSPOOL_SETPORTA を追加する。
  • winspool.h(include\windows\sdk 等にある)を開き、SetPortA/SetPortW を宣言している箇所を、#ifndef DONT_USE_WINSPOOL_SETPORTA~#endif で挟む
  • CPort.hpp をインクルードする。
  • CPort.hpp を開き、inline じゃない方の EComPort() の宣言 2 つをコメントアウトする。

以上でビルドができる。



≪参考≫

C++Builder XE でウィンドウを透明・半透明にする

Normalウィンドウの透明化・半透明化あれこれ。

元々は右のような普通の(不透明な)ウィンドウの透過度を、いくつかの方法で変化させてみる。

動作は Windows 7 (64bit) / C++Builder XE で行っているが、別の環境では別の動作になるかも。

* クライアント領域のみ透明化

クライアント領域のみを透明化させるには、フォームの TransparentColor/TransparentColorValue プロパティを使う。
void	TFormTest01::Test01()
{
	TransparentColor = true;
	TransparentColorValue = TColor(0x000001);
	Canvas->Brush->Color = TColor(0x000001);
	Canvas->FillRect(TRect(0, 0, 300, 300));
	Panel2->Color = TColor(0x000001);
}
TransparentColorTransparentColor プロパティを true にすることで透過モードとなり、フォーム上の TransparentColorValue プロパティで指定した色(コンポーネント同士の重なりに関係なく、指定した色で塗られている部分はすべて)が完全透過となる。半透明にすることはできない。

Windows API で言えば、SetLayeredWindowAttributes() を LWA_COLORKEY でコールした状態だと思われる。ただし、Windows API の場合、透過部分ではマウスイベント等を受け取れない(透明と言うよりはウィンドウに穴が空いて、背後のデスクトップ等にイベントが行く)ようだが、C++Builder の場合はイベントを受け取れる。

* ウィンドウ全体を半透明化

ウィンドウ全体を半透明にするには、フォームの AlphaBlend/AlphaBlendValue プロパティを使う。
void	TFormTest01::Test02()
{
	AlphaBlend = true;
	AlphaBlendValue = 128;
}
AlphaBlendAlphaBlendValue = 0 で完全透明、AlphaBlendValue = 255 で完全不透明、その中間で半透明となる。

コンポーネントも含め、ウィンドウ内がすべて一律で透明になる。

Windows API で言えば、SetLayeredWindowAttributes() を LWA_ALPHA でコールした状態だと思われる。

* グラスフレーム(ガラス効果)の拡張

Windows Vista から導入された Aero において、グラスフレーム(タイトルバーのもやもやした半透明)をクライアント領域にまで広げるには、GlassFrame プロパティを使う。
void	TFormTest01::Test07()
{
	GlassFrame->Enabled = true;
	GlassFrame->SheetOfGlass = true;
}
GlassFrameGlassFrame->Enabled を true にするとグラスフレーム拡張モードとなり、GlassFrame->SheetOfGlass も true にすることで、クライアント領域全体でガラス効果を得られる。GlassFrame->SheetOfGlass を false にして、GlassFrame->Bottom などのプロパティで任意の範囲でガラス効果を得るようにすることもできる。

Windows API で言えば、Desktop Window Manager の DwmExtendFrameIntoClientArea() をコールした状態と思われる。
月別アーカイブ
記事検索
最新コメント
  • ライブドアブログ