今朝、フォールダーに入っているGDI+が書き込み処理可能な画像ファイル(注)のサイズをi一定の条件に沿って一括変換するResizerというソフトを書きあげました。
注:"image/bmp", "image/jpeg", "image/gif", "image/tiff", "image/png"の5つですね。
が、
昨日以来今朝に至るまで「Imageクラスインスタンスを作った段階でプログラムが落ちる」トラブルで頭を悩ませていました。そしてその原因が(頭の悪い私の)おかしな思い込みによるものであることが分かりました。
トラブルというのは、SkeltonWizardでつくったダイアログベースのスケルトンで、GDI+を使おうとして何の迷いもなく、
CMyWnd::CMyWnd() { //コンストラクター
//ワイド文字使用のためロケールの初期化(日本語)
setlocale(LC_CTYPE, "JPN");
//GDI+の開始
GdiplusStartup(&m_gdiToken, &m_gdiSI, NULL);
}
CMyWnd::~CMyWnd() { //デストラクター
//GDI+の終了
GdiplusShutdown(m_gdiToken);
}
とやってテストをすると落ちる、落ちる、落ちまくります。この為、色々なところに私特有のMessageBoxトラップをかけて全てGDI+関連のところで落ちていることを確認しますが、(自分ではちゃんと初期化を行っているつもりなので)「何故!?!?!?!」と考えてしまいます。最後にImageクラスポインターにnewでImageオブジェクトを作るところのファイル名(ワイド文字)を標示したら「setscaleを実施しているにもかかわらず」ファイル名がおかしい!ということで↑のコンストラクターとデストラクターにトラップを仕掛けると、デストラクターは働いていますが、コンストラクターはスルーされています。
???............!!!
BCCSkeltonの、SkeltonWizardで作成されるウィンドウ(ダイアログ)クラスは、宣言部で
class CMyWnd : public CDLG
{
public: //以下はコールバック関数マクロと関連している
//////////////
//メンバー変数
//////////////
//2重起動防止用のMutex用ID名称
CMyWnd(char* UName) : CDLG(UName) {}
.
.
.
と「文字列を渡すコンストラクター(注)」を宣言しています。
注:このコンストラクターは、派生元のCDLGクラスやCSDIクラス等のミューテックスを利用した排他処理の可否を問うIsOnlyOne関数の為にユニークな名称を文字列として与えるコンストラクターになっています。
従って、↑のような引数を与えないデフォルトコンストラクターを作っても、インスタンスを作るSkeltonWizardのコードは、
CMyWnd Resizer("Resizer"); //ウィンドウクラスインスタンスの生成
と「文字列を引数に渡すコンストラクター」を使っており、実は全く呼ばれないのです。(20年も経ったので、自分で作ったものの、もはや忘れかけており、恥ずかしい限りです。)
ということで、教訓。
SkeltonWizardでつくったBCCSkeltonのコードを使う場合、(GDI+の「GdiplusStartup」や「GdiplusShutdown」のような)始めとお終いに必ず行う処理はOnCreateやOnInit、OnClose等のウィンドウ処理の「必ず通る関数」(注)に仕掛けた方がよいでしょう。
注:なおダイアログの場合、OnIdokやEscキーを押したときも想定してOnIdcancelに仕掛けても、ダイアログの「X]ボタンを押した際にはスルーされるので、必ずOnCloseに仕掛けるよう気を付けましょう。
追補:【ちょっといい話】
この話を書く際にウェブをチェックしていて、
という記事を見つけました。ありがちな話ですよね?(忘れずに一番下のAkira Takahashiさんのアドバイスをお読みください。)
BCC55はC++11以前、BCC102はC++11適合なので実験してみても面白いと思います。
追々補:【ちょっといい話】
Resizerで、料理の写真(一辺が2~4000ピクセルで1枚5MB、全部で260MB程度)を纏めて「長い方の1辺を960ピクセルにしてオリジナルの縦横対比を変えない」ように縮小したら、解像度は十分ありながら1枚650KB、全部で24MB程度にダウンサイジングができました。おかげさまでAlbumもサクサク動いています。