注意:翌9月4日に↓にあるCALBUMの修正部分を再修正し、すぐにアップしなおしました。DLされた方はお気を付けください。なお、その理由詳細は末尾参照。

 

既に旧い話(6月1日付ブログ)になりますが、Album(写真閲覧ソフト)が突如(A bolt out of the blue)として「落ちる」という現象に見舞われ、

【またまた、謎】Can you hit the bolt back?

というブログを書き、MicrosoftのGDIPLUS.DLLがおかしいんじゃないの?等と匂わせています。(スミマセン、Microsoftさん!)

 

実は(お恥ずかしい話ですが)このAlbumの不具合は覚えていて、「いつか fix せにゃあかんのぅ」とは思っていたんですが、原因のヒントが思いつかず、モチベーションが下がり、「先送り、先送り」で触らずにいました。その結果...原因調査をして↑のブログを書いたことも忘れてしまい、再度全く同じ様にトラップをかけて原因を探って行って、本日目出度く「ShowImg(int x, int y, int w, int h)関数で、m_Image->GetWidth()とGetHeight()関数がゼロを返すので、Division by zeroエラーが生じている」ということを「発見」しました。

 

【CALBUM抜粋】

//イメージの表示(x, yからw幅、h高さの矩形に原寸表示比率で表示)
bool CALBUM::ShowImg(int x, int y, int w, int h) {

    //エラー対応
    if(!m_Photo[m_Selected])
        return FALSE;
    //現在選択されている写真のファイル名をワイド文字にする
    mbstowcs(m_WFName, m_Photo[m_Selected]->m_FName, MAX_PATH);

    //現在選択されている写真のファイル名でイメージクラスインスタンスを生成
    m_Image = new (Image)(m_WFName);
    
//m_Imageインスタンスの生成に失敗した場合(解説:これが成否判定の積り)
    if(!m_Image)
        
return FALSE;
    //画像を回転させる-RotateFlipType:Rotate<A>Flip<B> <A:None, 90, 180, 270>, <B:X, Y, XY>
    m_Image->RotateFlip(m_RFType);
    
//イメージの原寸を取得
    int imgw = m_Image->GetWidth();    //imgwに0が代入される
    int imgh = m_Image->GetHeight();    //imghに0が代入される
    //w、h内で原寸の縦横対比のイメージを中央に配置
    double asp1, asp2;
    asp1 = (double)imgh / (double)
imgw;    //ここでDiv by zeroエラーが発生する
    asp2 = (double)h / (double)w;
    if(asp1 > asp2) {
        imgh = h;
        imgw = h / asp1;
    }
    else {
        imgw = w;
        imgh = w * asp1;
    }
    x += (w - imgw) / 2;
    y += (h - imgh) / 2;
    //m_Imageインスタンスを表示
    m_pGraphics->DrawImage(m_Image, x, y, imgw, imgh);
    //m_Imageインスタンスを解放
    delete m_Image;
    m_Image = 0;
    //再描画を要求
    InvalidateRect(m_hWnd, NULL, TRUE);
    return TRUE;
}

 

「そんなことってあるんかいっ!」ということで、原因をググって行って↑の自分の過去ブログを「再発見」した次第。

 

ほんにおそろしい忘却力、ボケ力(ヂカラ)!

 

まぁ、それはそれとして、今回解決まで進展したのは、前回と同じように色々と調べ、色々と試した際に2020年の英文の質疑応答サイトで回答者が「『全く同じファイルパス』-というのは間違いだね。ファイルアクセスの仕方が実質的に違うんだよ。("the very same file paths" - You aren't. You are using a substantially different way to access file system objects.)」という文章を見て、「FileListのファイルパスが間違っていて、イメージファイルが読み込めないという可能性」が頭をよぎりました。

 

当時のコードはCALBUMクラスのメンバー変数m_Image(注)というImageオブジェクトへのポインターにワイド文字のファイル名からオブジェクトを生成し、その成否判定も行って(いる積り↑で)いました。

注:m_Imageは画像を表示させるときだけポインターとして使い、使わない場合は常時0を代入している。

 

しかし、この視点で「コンストラクターって値を返さないよなぁ」と思いながら調べなおすと、矢張りImage::Image(WCHAR, bool = FALSE)というコンストラクターは、

Return value

    None

と書かれています。

と、いうことはよ?ファイルが読み込まれていなくても(ゼロ)メモリー領域が確保されて、それをm_Imageが指すので「Non 0」になり、その幅と高さを求めると(当然)「0」が返る.....そいこと?

 

もう言わずもがなですが、(その時、「あっ、そうだ。OneDriveの設定からイメージデータの入っていたフォールダー名「ピクチャ」を"Pictures"フォールダーに入れ替えたんだった」という記憶が...)「落ちる」FileListのファイルパスを見ると「...\ピクチャ\...」となっていたので、一括変換で"Pictures"にすると、

 

お目出度うございま―す。きちんと表示されましたぁ。

 

では、次にどのようにして「ファイルのNot foundエラーを検知させるのか?」ということで、ImageクラスのFromFileメソッドとImage::GetLastStatus()を使って戻り値がOk (0) でない場合にエラーとし、特によくありそうな FileNotFound (10) の時はメッセージボックスで表示させようか、とか考えたのですが、実験してみるとなかなかFileNotFoundを返さないんですね。

 

そんなこんなで、(イメージファイルが読み込めなかった場合、イメージサイズが幅高さともにゼロになることを逆手に取り)現実的な対応として「(幅か高さがゼロなら)『ファイルが読み込めなかった』というエラーメッセージを出して、『絵なし』表示にすることにしました。これで少なくとも「突然落ちる」ことはなくなりました。

 

【ShowImg関数の修正部】

    //現在選択されている写真のファイル名でイメージクラスインスタンスを生成
    m_Image = new (Image)(m_WFName, FALSE);
    //画像を回転させる-RotateFlipType:Rotate<A>Flip<B> <A:None, 90, 180, 270>, <B:X, Y, XY>
    m_Image->RotateFlip(m_RFType);
    //イメージの原寸を取得
    int imgw = m_Image->GetWidth();
    int imgh = m_Image->GetHeight();
  
 //m_Imageインスタンスの生成に失敗した場合imgw、imghはゼロになる
    if(!imgw || !imgh) {
        MessageBox(m_hWnd, "ファイルが読み込めませんでした。\nファイルパスを見直してください。",
                    "エラー", MB_OK | MB_ICONERROR);

        return FALSE;
    }

 

遅ればせながら、やっと夏休みの宿題が出せました。

 

【翌9月4日追記-CALBUM再修正】

翌朝も1時半くらいに目が覚めてしまい、ぼんやりスマホをいじってこのブログの確認をしていたら、途端に目が覚めました。

「ファイルが読み込めずにエラー処理しているのに、作ったイメージオブジェクト(サイズはゼロだが、メモリーリークはメモリーリーク)を消していないぞ?」

また、アホなことに、引数が違う別のShowImageのオーバーロード関数も同じ問題があるのに未処理でした。

 

ということで、次のようにして両ShowImg関数に入れて再修正を行いました。ゴメンナサイ。

    //m_Imageインスタンスの生成に失敗した場合imgw、imghはゼロになる
    if(!imgw || !imgh) {
        MessageBox(m_hWnd, "ファイルが読み込めませんでした。\nファイルパスを見直してください。",
                    "エラー", MB_OK | MB_ICONERROR);

        //m_Imageインスタンスを解放
        delete m_Image;
        m_Image = 0;

        return FALSE;
    }