Windowsアプリ開発(C++ネイティブアプリ)で以下のようなクラスを作成し、そのクラスを使用している関数を繰り返し実行していると突然アプリがクラッシュしました。
クラッシュダンプを見ると、暗黙的に作成されたと思われる代入演算子( operator= )でクラッシュしていました。
下記のコード内では、どこにも代入するコードは入っていないのに、それがなぜ、どこで呼ばれたのか分かりません。
この辺りの動作に詳しい方、アドバイスを頂けますでしょうか?
よろしくお願いいたします。
Header file in my sub dll
#pragma once
#include "wincodec.h"
#include "wincodecsdk.h"
class AFX_EXT_CLASS TestImageEncoder
{
private:
IWICImagingFactory* mpFactory;
public:
TestImageEncoder();
virtual ~TestImageEncoder();
BOOL Initialize();
BOOL EncodeData( WORD* pImage, int width, int height, BYTE** pDstData, int& dstSize, BOOL isLossless );
};
Cpp file in my app sub dll
TestImageEncoder::TestImageEncoder()
: mpFactory( NULL )
{
Initialize();
}
TestImageEncoder::~TestImageEncoder()
{
if ( mpFactory )
{
mpFactory->Release();
mpFactory = NULL;
}
CoUninitialize();
}
BOOL TestImageEncoder::Initialize()
{
// Initialize COM
HRESULT hr;
hr = CoInitialize( NULL );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed CoInitialize(): %d\n", hr );
return FALSE;
}
// Create the COM imaging factory
hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS( &mpFactory ) );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to create a WIC imaging factory: %d\n", hr );
return FALSE;
}
return TRUE;
}
BOOL TestImageEncoder::EncodeData( WORD* pImage, int width, int height, BYTE** ppDstData, int& dstSize, BOOL isLossless )
{
// Encode image data and save it as JPEG XR file
CString tempFullPath;
tempFullPath = _T( "TempImage.jxr" );
BOOL res = SaveAsJpegXR( tempFullPath, pImage, width, height, isLossless );
if ( !res )
{
// Delete the JPEG XR file
::DeleteFile( tempFullPath );
return FALSE;
}
// Load image data from the JPEG XR file
CFile jxrFile;
jxrFile.Open( tempFullPath, CFile::modeRead );
dstSize = jxrFile.GetLength();
*ppDstData = new BYTE[dstSize];
ZeroMemory( *ppDstData, dstSize );
jxrFile.Read( *ppDstData, dstSize );
jxrFile.Close();
// Delete the JPEG XR file
::DeleteFile( tempFullPath );
}
BOOL TestImageEncoder::SaveAsJpegXR( CString& filePath, WORD* pImage, int width, int height, BOOL isLossless )
{
HRESULT hr;
IWICStream* piStream = NULL;
IWICBitmapEncoder* piEncoder = NULL;
IWICBitmapFrameEncode* piBitmapFrame = NULL;
IPropertyBag2* pPropertybag = NULL;
BOOL res = FALSE;
__try
{
// Create a stream //////////////////////////////////////////////
hr = mpFactory->CreateStream( &piStream );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to create stream: %d\n", hr );
res = FALSE;
__leave;
}
piStream->InitializeFromFilename( filePath, GENERIC_WRITE );
// Create an encoder //////////////////////////////////////////////
hr = mpFactory->CreateEncoder( GUID_ContainerFormatWmp, NULL, &piEncoder );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to create encoder: %d\n", hr );
res = FALSE;
__leave;
}
// Initialize the encoder ///////////////////////////
hr = piEncoder->Initialize( piStream, WICBitmapEncoderNoCache );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to initialize encoder: %d\n", hr );
res = FALSE;
__leave;
}
// Create a frame ///////////////////////////////////////////////////
hr = piEncoder->CreateNewFrame( &piBitmapFrame, &pPropertybag );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to create a new frame: %d\n", hr );
res = FALSE;
__leave;
}
// Set lossless if it is true /////////////////////////////////////////
if ( isLossless )
{
// Enable lossless /////////////////////////////
PROPBAG2 option = { 0 };
option.pstrName = L"Lossless";
VARIANT varValue;
VariantInit( &varValue );
varValue.vt = VT_BOOL;
varValue.bVal = VARIANT_TRUE;
hr = pPropertybag->Write( 1, &option, &varValue );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to set the property for Lossless: %d\n", hr );
res = FALSE;
__leave;
}
}
// Initialize the frame ///////////////////////////////////////////////////
hr = piBitmapFrame->Initialize( pPropertybag );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to initialize the frame: %d\n", hr );
res = FALSE;
__leave;
}
// Set image size ///////////////////////////////////////////////////
hr = piBitmapFrame->SetSize( width, height );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to set size (width=%d, height=%d) : %d\n", width, height, hr );
res = FALSE;
__leave;
}
// Set the pixel format ///////////////////////////////////////////////////
WICPixelFormatGUID formatGUID = GUID_WICPixelFormat16bppGray;
hr = piBitmapFrame->SetPixelFormat( &formatGUID );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to set pixel format: %d\n", hr );
res = FALSE;
__leave;
}
// Check if the pixel format is correctly set //////////////////////////
hr = IsEqualGUID( formatGUID, GUID_WICPixelFormat16bppGray ) ? S_OK : E_FAIL;
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Unexpected pixel format (not 16bppGray): %d\n", hr );
res = FALSE;
__leave;
}
UINT cbStride = width * sizeof( WORD );
UINT cbBufferSize = height * cbStride;
hr = piBitmapFrame->WritePixels( height, cbStride, cbBufferSize, (BYTE*)pImage );
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to write image data: %d\n", hr );
res = FALSE;
__leave;
}
hr = piBitmapFrame->Commit();
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to commit the frame: %d\n", hr );
res = FALSE;
__leave;
}
hr = piEncoder->Commit();
if ( FAILED( hr ) )
{
_RPTN( _CRT_WARN, "Failed to commit the encoder: %d\n", hr );
res = FALSE;
__leave;
}
res = TRUE;
}
__finally
{
if ( piEncoder )
{
piEncoder->Release();
}
if ( piBitmapFrame )
{
piBitmapFrame->Release();
}
if ( pPropertybag )
{
pPropertybag->Release();
}
if ( piStream )
{
piStream->Release();
}
}
return res;
}
このクラスを使用して以下のような実装をしました。
下記関数内で16回繰り返し、さらにTestProcess関数自体も20回ほど繰り返し実行されるようになっています。
Method in my app exe
void TestProcess(WORD* srcData, int width, int height)
{
CFile fImage;
fImage.Open( "TestImageData.DAT", CFile::modeWrite | CFile::modeCreate );
int srcSize = wBytes_per_Pixel * dwWidth * dwHeight;
BYTE* pDst = NULL;
int dstSize = 0;
TestImageEncoder encoder;
for ( int i = 0; i < 16; i++ )
{
encoder.EncodeData( srcData, width, height, &pDst, dstSize );
fImage.Write( &dstSize, sizeof( int ) );
fImage.Write( pDst, dstSize );
delete[] pDst;
pDst = NULL;
}
}
クラッシュダンプのスタックトレース(一部)は以下の用になっていました。
mfc140u!CWnd::OnWndMsg+0xe75
mfc140u!CWnd::WindowProc+0x3f
mfc140u!AfxCallWndProc+0x11e
mfc140u!AfxWndProc+0x54
mfc140u!AfxWndProcBase+0x49
user32!UserCallWinProcCheckWow+0x2d1
user32!DispatchMessageWorker+0x1f1
mfc140u!AfxInternalPumpMessage+0x52
mfc140u!CWinThread::Run+0x81
mfc140u!AfxWinMain+0xc0
MyApp!TestImageEncoder::operator=+0x5af62 <- なぜかここで暗黙的代入演算子が呼ばれています。
kernel32!BaseThreadInitThunk+0x1d
ntdll!RtlUserThreadStart+0x28