質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C++

    4426questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • DirectX

    77questions

    DirectX(ダイレクトエックス)は、 マイクロソフトが開発したゲーム・マルチメディア処理用のAPIの集合です。

【Direct2D】BitmapをReleaseするときに強制終了してしまう【WIC】

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,845

aridai1221

score 31

前提・実現したいこと

Direct2Dで画像を読み込んで描画するという処理を書いています。
WICを利用して画像を読み込み、ID2D1Bitmapを生成して、
ID2D1HwndRenderTargetのdrawBitmapで描画する
といった感じで画像の描画はできました。
しかし、ID2D1BitmapのReleaseを行うときに強制終了してします。
なんとかして強制終了をさせずに終了させたいです。

発生している問題・エラーメッセージ

例外がスローされました:読み取りアクセス違反。
this->wicImagingFactory-> が 0x6D211E90 でした。

該当のソースコード

7/4更新
(実際はファイルを分けてコードを書いてあります。)

#include <d2d1.h>
#include <wincodec.h>
#include <map>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "windowscodecs.lib")

//    ウィンドウの幅
extern int WINDOW_WIDTH;

//    ウィンドウの高さ
extern int WINDOW_HEIGHT;

//    アプリケーションのインスタンスハンドル
extern HINSTANCE applicationInstance;

//    ウィンドウハンドル
extern HWND windowHandle;

//    Direct2Dファクトリ
ID2D1Factory*direct2DFactory = NULL;

//    レンダーターゲット
ID2D1HwndRenderTarget* renderTarget = NULL;

//    ブラシ
ID2D1SolidColorBrush* brush = NULL;

//    Direct2Dの初期化処理を行う
void initializeDirect2D()
{
    //    Direct2Dファクトリを生成する
    D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &direct2DFactory);

    //    レンダーターゲットを生成する
    direct2DFactory->CreateHwndRenderTarget(
        D2D1::RenderTargetProperties(),
        D2D1::HwndRenderTargetProperties(
            windowHandle,
            D2D1::SizeU(WINDOW_WIDTH, WINDOW_HEIGHT)),
        &renderTarget);

    //    ブラシを生成する
    renderTarget->CreateSolidColorBrush(D2D1::ColorF(0, 0, 0), &brush);
}

//    Direct2Dの終了処理を行う
void finalizeDirect2D()
{
    OutputDebugString("brushをReleaseします!\n");
    if (brush != NULL)
        brush->Release();
    OutputDebugString("renderTargetをReleaseします!\n");
    if (renderTarget != NULL)
        renderTarget->Release();
    OutputDebugString("direct2DFactoryをReleaseします!\n");
    if (direct2DFactory != NULL)
        direct2DFactory->Release();
}

class Image
{
public:
    Image(const wchar_t*);
    ~Image();
    void draw(float x, float y);

private:
    IWICImagingFactory* wicImagingFactory = NULL;
    IWICBitmapDecoder* wicBitmapDecoder = NULL;
    IWICBitmapFrameDecode* wicBitmapFrame = NULL;
    IWICFormatConverter* wicFormatConverter = NULL;
    ID2D1Bitmap* bitmap = NULL;
};

Image::Image(const wchar_t* fileName)
{
    //    WICイメージングファクトリを生成する
    CoCreateInstance(
        CLSID_WICImagingFactory,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWICImagingFactory,
        (LPVOID*)&wicImagingFactory);

    //    Bitmapデコーダを生成する
    wicImagingFactory->CreateDecoderFromFilename(
        fileName,
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnLoad,
        &wicBitmapDecoder);

    //    画像からフレームを読み込む
    wicBitmapDecoder->GetFrame(0, &wicBitmapFrame);

    //    フォーマットコンバータを生成する
    wicImagingFactory->CreateFormatConverter(&wicFormatConverter);

    //    コンバータの初期化をする
    wicFormatConverter->Initialize(
        wicBitmapFrame,
        GUID_WICPixelFormat32bppPBGRA,
        WICBitmapDitherTypeNone,
        NULL,
        0,
        WICBitmapPaletteTypeCustom);

    //    Bitmapを生成する
    renderTarget->CreateBitmapFromWicBitmap(
        wicFormatConverter,
        NULL,
        &bitmap);
}

Image::~Image()
{
    OutputDebugString("bitmapをReleaseします!\n");
    if (bitmap != NULL)
        bitmap->Release();
    OutputDebugString("wicFormatConverterをReleaseします!\n");
    if (wicFormatConverter != NULL)
        wicFormatConverter->Release();
    OutputDebugString("wicBitmapFrameをReleaseします!\n");
    if (wicBitmapFrame != NULL)
        wicBitmapFrame->Release();
    OutputDebugString("wicBitmapDecoderをReleaseします!\n");
    if (wicBitmapDecoder != NULL)
        wicBitmapDecoder->Release();
    OutputDebugString("wicImagingFactoryをReleaseします!\n");
    if (wicImagingFactory != NULL)
        wicImagingFactory->Release();
}

void Image::draw(float x, float y){}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    //    ウィンドウを生成する
    initializeWin32API();

    //    Direct2Dの初期化処理を行う
    initializeDirect2D();

    //    DirectInputの初期化処理を行う
    initializeDirectInput();

    Image* image = new Image(L"hoge.png");

    //    終了メッセージが送られるまでループする
    while (checkMessage())
    {
        beginRendering();

        clearScreen(1, 1, 1);
        image->draw(0, 0);

        endRendering();
    }

    delete image;

    //    Direct2Dの終了処理を行う
    finalizeDirect2D();

    //    DirectInputの終了処理を行う
    finalizeDirectInput();

    return 0;
}

試したこと

Release行う順番が良くないかと思い、
bitmapのReleaseを最初に行うように変更してみましたが、
アクセス違反が発生していました。

7/4追記
別のマシンでコンパイル・実行したところ
特に強制終了することなく動作しました。
マシンA … 最初に試したマシン => コンパイル・実行 => 実行時に強制終了
マシンB … 次に試したマシン => コンパイル・実行 => 異常なし
マシンC … その次に試したマシン => マシンAとマシンBでビルドしたexeを実行 => どちらも異常なし
マシンD … その後試したマシン => マシンAとマシンBでビルドしたexeを実行 => どちらも実行時に強制終了

その後マシンAを再起動させてもう一度コンパイル・実行したところ
実行時に強制終了してしまいました。

補足情報(言語/FW/ツール等のバージョンなど)

Visual Studio Commnunity 2015
Direct2D (d2d1.h) (d2d1.lib) (dxguid.lib)
WIC (wincodec.h) (windowscodecs.lib)

マシンA (Windows 7 Ultimate x64)
マシンB (Windows 10 Pro x64)
マシンC (Windows 10 Home x86)
マシンD (Windows 7 Professional x64)

解決策 (by catsforepawさん)

CoInitializeCoUninitializeを呼ぶようにする。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

提示されているコードでは判りませんね。COMオブジェクトは参照カウントでインスタンスの生存管理をしているのでReleaseの順番にはさほど厳密さは要求されませんし、最終的に必要なもの(bitmap)以外は使い終わった時点でReleaseしてしまっても問題ありません。

気になるのは、

例外がスローされました:読み取りアクセス違反。
this->wicImagingFactory-> が 0x6D211E90 でした。

このアドレスが異様に大きいように思います。32bit版でのユーザーアドレス空間は0~7FFFFFFFなので、ほぼ目一杯使っていることになりますが、実際にはアクセス違反でエラーになっています。
もしかしたら、どこかでポインタの値が書き換わってしまったのかもしれません。オブジェクトを作ったところとReleaseするところでポインタの値を表示させて、書き換わっていないかどうか調べた方が良さそうです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/04 15:25

    回答ありがとうございます。
    アドレスの値を出力させるようにして確認してみます。

    キャンセル

  • 2016/07/04 21:06

    今調べてみました。
    生成したときとReleaseするときとでは
    アドレスの値は書き換わっていないようです。

    キャンセル

  • 2016/07/04 22:24

    もしかして、CoInitialize/CoUninitialize を呼んでいなかったりします?
    それなしで各インターフェイスを作ってReleaseしようとすると確かに落ちます。COMコンポーネントを使う場合は、必ずプログラムの開始時(COMインターフェイス取得前)に`CoInitialize(NULL)`を呼び出し、終了時に`CoUninitialize()`を呼んでください。

    キャンセル

  • 2016/07/04 22:52

    ありがとうございます!
    うまくいきました!

    キャンセル

0

そのソースだけじゃわからないけど,renderTarget より後に Image を破棄してませんか?

ID2D1Bitmap はデバイス (ID2D1RenderTarget) 依存リソースだから,
renderTarget より先に解放しないとまずいはず.

renderTarget の方を先に解放していたとすると,

(1) renderTarget->Release()

renderTarget から作成したデバイス依存リソース (bitmap など) が全部無効になる.

(2) Image::~Image

bitmap->Release() ← bitmap は既に無効になっているので二重解放エラーになる.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/04 04:15

    回答ありがとうございます。
    OutputDebugStringでRelease処理前に
    「○○を解放します」という出力をするようにして確かめましたが、
    ちゃんとrenderTargetより先にbitmapをReleaseしようとしています。

    キャンセル

0

こんにちは。

DirectXは触ったことがないので直接の回答はできないのですが、このように原因不明で落ちる時はコードを減らしていって、現象が発生する時としない時の境目を絞り込んでいくと結構バグが見つかります。

ご提示されたソースの場合なら、

①コンストラクタとデストラクタ以外の処理は全て中身をコメントアウトする
この時点で異常終了しないようなら、コメントアウトしたメソッドのどれかが原因ですので、1つ1つ潰していく。
②同様にコンストラクタの中身を最後の方から1つ1つコメントアウトする

という感じです。
手間はかかりますが、ほぼ必ず原因にたどり着けます。(コンパイラのバグでexeサイズが原因のようなケースでは中々大変ですが。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • C++

    4426questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • DirectX

    77questions

    DirectX(ダイレクトエックス)は、 マイクロソフトが開発したゲーム・マルチメディア処理用のAPIの集合です。