作成中の Direct2D アプリがあり、Direct2D を簡単に使用できるようにする Direct2D ライブラリも作成しています。必要に応じて正確な問題のあるコードを投稿しますが、主な問題は、あるクラスの定義に ID2D1HwndRenderTarget があり、そのクラスを別のクラスで拡張し、子クラスにメソッドを呼び出すメソッドがあることです。レンダー ターゲットを初期化する親クラスの、次に子クラスの load メソッドを呼び出します。ただし、プログラムが子クラスのコンテンツ読み込みメソッドに到達するとすぐに、ID2D1HwndRenderTarget の IUnknown 部分にある __vfptr 変数 (それが何かはわかりません) が null になります。私がこれを理解した唯一の理由は、他のコードで、レンダー ターゲットを使用して IWicBitmapSource から ID2D1Bitmap を作成するときにアクセス違反エラーが発生したことです。レンダー ターゲットを初期化した後、初期化コードを含むメソッドが戻るとすぐに _vfptr 変数が null になるため、これがどのように発生するのかわかりません。なぜこれが起こっているのか誰にも説明できますか?私の関連コードは以下です。
このコードは、hwnd レンダー ターゲットとオフスクリーン レンダー ターゲットを作成するために 1 回呼び出されます。これは dll プロジェクトにあります。GameBase.cpp
HRESULT GameBase::Initialize(HINSTANCE hInst, HWND winHandle, struct DX2DInitOptions options)
{
this->mainRenderTarget = NULL;
this->offscreenRendTarget = NULL;
this->factory = NULL;
HRESULT result;
D2D1_FACTORY_OPTIONS factOptions;
D2D1_FACTORY_TYPE factType;
if(options.enableDebugging)
factOptions.debugLevel = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_ERROR;
else
factOptions.debugLevel = D2D1_DEBUG_LEVEL::D2D1_DEBUG_LEVEL_NONE;
if(options.singleThreadedApp)
factType = D2D1_FACTORY_TYPE_SINGLE_THREADED;
else
factType = D2D1_FACTORY_TYPE_MULTI_THREADED;
result = D2D1CreateFactory(factType, factOptions, &this->factory);
if(FAILED(result))
{
OutputDebugString(L"Failed to create a Direct 2D Factory!");
return result;
}
this->instance = hInst;
this->hwnd = winHandle;
D2D1_SIZE_U size = D2D1::SizeU(options.winWidth, options.winHeight);
this->width = options.winWidth;
this->height = options.winHeight;
result = factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(winHandle, size), &this->mainRenderTarget);
if(FAILED(result))
{
OutputDebugString(L"Failed to create a render target to draw to the window with!");
return result;
}
result = this->mainRenderTarget->CreateCompatibleRenderTarget(&this->offscreenRendTarget);
if(FAILED(result))
{
OutputDebugString(L"Failed to create an offscreen render target from the main render target.");
return result;
}
return LoadContent();
}
LoadContent の呼び出し後、mainRenderTarget の値を変更することは決してありません。
DX2DImage.cpp
HRESULT DX2DImageLoader::LoadFromResource(LPCWSTR resourceName, LPCWSTR resourceType, HMODULE progModule, DX2DImage* image)
{
if(!this->isInit)
{
OutputDebugStringA("You must call InitializeImageLoader before using this image loader!");
return E_FAIL;
}
IWICBitmapDecoder *decoder = NULL;
IWICBitmapFrameDecode *source = NULL;
IWICStream *stream = NULL;
IWICFormatConverter *converter = NULL;
HRSRC imageResHandle = NULL;
HGLOBAL imageResDataHandle = NULL;
void *imageFile = NULL;
DWORD imageFileSize = 0;
HRESULT result;
//Find the image.
imageResHandle = FindResource(progModule, resourceName, resourceType);
if(!imageResHandle)
{
OutputDebugStringA("Failed to get a handle to the resource!");
return E_FAIL;
}
//Load the data handle of the image.
imageResDataHandle = LoadResource(progModule, imageResHandle);
if(!imageResDataHandle)
{
OutputDebugStringA("Failed to load the image from the module!");
return E_FAIL;
}
//Lock and retrieve the image.
imageFile = LockResource(imageResDataHandle);
if(!imageFile)
{
OutputDebugStringA("Failed to lock the image in the module!");
return E_FAIL;
}
//Get the size of the image.
imageFileSize = SizeofResource(progModule, imageResHandle);
if(!imageFileSize)
{
OutputDebugStringA("Failed to retrieve the size of the image in the module!");
return E_FAIL;
}
//Create a stream that will read the image data.
result = this->factory->CreateStream(&stream);
if(FAILED(result))
{
OutputDebugStringA("Failed to create an IWICStream!");
return result;
}
//Open a stream to the image.
result = stream->InitializeFromMemory(reinterpret_cast<BYTE*>(imageFile), imageFileSize);
if(FAILED(result))
{
OutputDebugStringA("Failed to initialize the stream!");
return result;
}
//Create a decoder from the stream
result = this->factory->CreateDecoderFromStream(stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a decoder from the stream!");
return result;
}
//Get the first frame from the image.
result = decoder->GetFrame(0, &source);
if(FAILED(result))
{
OutputDebugStringA("Failed to get the first frame from the decoder!");
return result;
}
//Create a format converter to convert image to 32bppPBGRA
result = this->factory->CreateFormatConverter(&converter);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a format converter!");
return result;
}
//Convert the image to the new format.
result = converter->Initialize(source, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeMedianCut);
if(FAILED(result))
{
OutputDebugStringA("Failed to convert the image to the correct format!");
return result;
}
//Create the Direct2D Bitmap from the Wic Bitmap.
result = this->renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &image->bitmap);
if(FAILED(result))
{
OutputDebugStringA("Failed to create a Direct 2D Bitmap from a WIC Bitmap!");
return result;
}
image->width = static_cast<UINT>(image->bitmap->GetSize().width);
image->height = static_cast<UINT>(image->bitmap->GetSize().height);
SafeRelease(&source);
SafeRelease(&converter);
SafeRelease(&decoder);
SafeRelease(&stream);
return S_OK;
}
回線でアクセス違反例外が発生する
result = this->renderTarget->CreateBitmapFromWicBitmap(converter, NULL, &image->bitmap);
ここで、image->bitmap は現在 NULL (想定どおり) ID2D1Bitmap です。
ここで、renderTarget 変数は、上記の GameBase.cpp の mainRenderTarget 変数と同じです。行をデバッグすると、RenderTarget のすべての親が null ではありませんが、その下にある IUnknown インターフェイスに到達すると、_vfptr が null になります。これは、converter 変数、this 変数、または image 変数には当てはまりません。