Вызов унаследованного IUnknown::Release() в деструкторе

Почему вызов унаследованной функции IUnknown::Release() для объекта IWICImagingFactory в деструкторе приводит к тому, что "CXX0030: Ошибка: выражение не может быть оценено" отображается для каждой записи в таблице виртуальных функций объекта (__vfptr)?

Это относится к предыдущему вопросу, который я опубликовал, но с тех пор я понял, что проблема возникает только в деструкторе. Таблица виртуальных функций кажется действительной везде, где я проверял. Однако, как только в деструкторе все записи показаны с ошибкой CXX0030, и попытка вызвать унаследованный IUknown::Release() не удалась.

Изменить: Вот код для демонстрации:

HRESULT DemoApp::CreateDeviceIndependentResources()
{
HRESULT hr;


hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &mpDirect2DFactory);


if (SUCCEEDED(hr))
{
    hr = CoCreateInstance(
        CLSID_WICImagingFactory,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&mpWICFactory)
        );
}

//CoCreateInstance returns S_OK.
//Other unrelated code here.
}

HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr;
//Other unrelated code here for creating device-dependant resources.
//mpBackgroundBitmap is a ID2D1Bitmap*.
    if(SUCCEEDED(hr))
    {
        hr = LoadBitmapFromFile(
            mpRenderTarget,
            mpWICFactory,
            L".\\background.png",
            0,
            0,
            &mpBackgroundBitmap);
    }
}


//The below LoadBitmapFromFile() code is taken directly from an MSDN sample.
//I didn't write it.

HRESULT DemoApp::LoadBitmapFromFile(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri,
UINT destinationWidth,
UINT destinationHeight,
ID2D1Bitmap **ppBitmap
)
{
IWICBitmapDecoder *pDecoder = NULL;
IWICBitmapFrameDecode *pSource = NULL;
IWICStream *pStream = NULL;
IWICFormatConverter *pConverter = NULL;
IWICBitmapScaler *pScaler = NULL;

HRESULT hr = pIWICFactory->CreateDecoderFromFilename(
    uri,
    NULL,
    GENERIC_READ,
    WICDecodeMetadataCacheOnLoad,
    &pDecoder
    );

if (SUCCEEDED(hr))
{
    // Create the initial frame.
    hr = pDecoder->GetFrame(0, &pSource);
}
if (SUCCEEDED(hr))
{

    // Convert the image format to 32bppPBGRA
    // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED).
    hr = pIWICFactory->CreateFormatConverter(&pConverter);

}


if (SUCCEEDED(hr))
{
    // If a new width or height was specified, create an
    // IWICBitmapScaler and use it to resize the image.
    if (destinationWidth != 0 || destinationHeight != 0)
    {
        UINT originalWidth, originalHeight;
        hr = pSource->GetSize(&originalWidth, &originalHeight);
        if (SUCCEEDED(hr))
        {
            if (destinationWidth == 0)
            {
                FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
                destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
            }
            else if (destinationHeight == 0)
            {
                FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
                destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
            }

            hr = pIWICFactory->CreateBitmapScaler(&pScaler);
            if (SUCCEEDED(hr))
            {
                hr = pScaler->Initialize(
                        pSource,
                        destinationWidth,
                        destinationHeight,
                        WICBitmapInterpolationModeCubic
                        );
            }
            if (SUCCEEDED(hr))
            {
                hr = pConverter->Initialize(
                    pScaler,
                    GUID_WICPixelFormat32bppPBGRA,
                    WICBitmapDitherTypeNone,
                    NULL,
                    0.f,
                    WICBitmapPaletteTypeMedianCut
                    );
            }
        }
    }
    else // Don't scale the image.
    { 
        hr = pConverter->Initialize(
            pSource,
            GUID_WICPixelFormat32bppPBGRA,
            WICBitmapDitherTypeNone,
            NULL,
            0.f,
            WICBitmapPaletteTypeMedianCut
            );
    }
}
if (SUCCEEDED(hr))
{

    // Create a Direct2D bitmap from the WIC bitmap.
    hr = pRenderTarget->CreateBitmapFromWicBitmap(
        pConverter,
        NULL,
        ppBitmap
        );
}

SafeRelease(&pDecoder);
SafeRelease(&pSource);
SafeRelease(&pStream);
SafeRelease(&pConverter);
SafeRelease(&pScaler);

return hr;
}

//Now I call SafeRelease() in my destructor and the virtual function table entires are showing the error.
DemoApp::~DemoApp()
{
SafeRelease(&mpDirect2DFactory);
SafeRelease(&mpWICFactory); //here is the problem apparently
SafeRelease(&mpDWriteFactory); 
SafeRelease(&mpRenderTarget);
SafeRelease(&mpBackgroundBitmap);

}

//SafeRelease is defined as:
template<class Interface>
inline void SafeRelease(Interface** ppInterfaceToRelease)
{
if(*ppInterfaceToRelease != NULL)
{
    (*ppInterfaceToRelease)->Release();
    (*ppInterfaceToRelease) = NULL;
        }

}

Проблема заключается в том, что когда я вызываю SafeRelease() для объекта WICFactory, я получаю: Исключение первого шанса в 0x0024e135 в DemoApp.exe: 0xC0000005: Место чтения нарушения доступа 0x6d5c28f0. Необработанное исключение в 0x0024e135 в DemoApp.exe: 0xC0000005: расположение чтения нарушения доступа 0x6d5c28f0.

2 ответа

Я боролся с этой проблемой в последнее время тоже. Проблема в том, что создание и выпуск IWICImagingFactory зависит от CoInitialize/CoUninitialize вызывается до и после, соответственно. В вашем приложении вполне вероятно, что CoUninitialize() называется до IWICImagingFactory выпущен в деструкторе, который вызывает сбой. Обратите внимание, что ID2D1RenderTarget и такие, кажется, не затрагиваются и могут быть освобождены после CoUninitialize() называется.

Удалить звонок CoUninitialize() от RunMessageLoop() или где бы он ни был, и поместите его после вызова освобождения в деструкторе, и сбой должен исчезнуть.

Вызов виртуальных функций внутри конструктора или деструктора не вызывает функцию, которую вы предполагаете вызвать. Это всегда приводит к вызову функций того же класса.

Вы можете предположить, что виртуальная диспетчеризация отключена в конструкторе и деструкторах.

Более подходящий способ сказать это:

Во время выполнения конструктора или деструктора виртуальные вызовы объекта, для которого запускается конструктор или деструктор, ведут себя так, как если бы динамический тип выражения объекта, используемого в вызове, был равен классу конструктора или деструктора.

Вежливость: длительная дискуссия в C++ Lounge, где, наконец, @JohannesSchaublitb придумал это подходящее определение, с которым большинство из нас, похоже, согласились.

Другие вопросы по тегам