Jim Pike
2011-01-20 16:44:15 UTC
I am new to GdiPlus and METAFILEs. We have a Sharepoint web part,
that basically calls up an ASPX web page (C#) that calls a remote dll
web service (C++) that then calls the engine dlls (C++) that produces
an image of a chart and then displays it in the web part.
First here is the main method in question. It is in the engine/main
dll code. The reason I think I have a memory leak is this. In a
single user scenario the system works flawlessly. However when I hook
up HP LoadRunner with 25 users going I get problems. On the line HDC
hDC = CreateEnhMetaFile(hdcRef, NULL, &rect, NULL); I get back null
and when I call GetLastError() I get ERROR_NOT_ENOUGH_MEMORY. This
makes me think somewhere I am leaking memory that in a multi-user load
test scenario eventually piles up and over. Does that sound like a
reasonable conclusion?
STDMETHODIMP CChartGen::GetChart(BSTR bstrFile, imgFormat iFormat,
long lChartID, long lElemID, long lPeriodID, long lUnit, BSTR
bstrFlags, long lWidth, long lHeight)
{
if( (lElemID < -1) || // <= 0 indicates summary query
(lPeriodID < -1) || // <= 0 indicates cross-contract summary
query
(lUnit <= 0) ||
(lChartID <= 0 ) )
{
return E_INVALIDARG;
}
if( m_lExtraID != 0 )
{
if( m_lExtraID != lChartID )
return E_INVALIDARG;
m_lExtraID = 0;
}
if( (iFormat == imgFormatGIF) && !m_bEnableGIF )
{
return E_INVALIDARG;
}
// special palette for background colors
RGBQUAD pal1[] =
{
0,0,0,0, // Black
0, 0, 128, 0, // Maroon
0, 128, 0, 0, // Green
0, 128, 128, 0, // Olive
128, 0, 0, 0, // Navy
128, 0, 128, 0, // Purple
128, 128, 0, 0, // Teal
108, 108, 108, 0, // Gray
182, 182, 182, 0, // Silver
0, 0, 255, 0, // Red
128, 255, 128, 0, // Lime (modified)
128, 255, 255, 0, // Yellow (modified)
255, 0, 0, 0, // Blue
128, 128, 255, 0, // Fuschia (modified)
255, 192, 128, 0, // Aqua (modified)
255, 255, 255, 0, // White
};
// special colors for IVAC chart
RGBQUAD pal2[] =
{
0,0,0,0, // Black
0, 0, 128, 0, // Maroon
0, 128, 0, 0, // Green
0, 128, 128, 0, // Olive
128, 0, 0, 0, // Navy
128, 0, 128, 0, // Purple
128, 128, 0, 0, // Teal
108, 108, 108, 0, // Gray
182, 182, 182, 0, // Silver
0, 0, 255, 0, // Red
0, 255, 0, 0, // Lime
0, 255, 255, 0, // Yellow
255, 0, 0, 0, // Blue
255, 0, 255, 0, // Fuschia
255, 255, 0, 0, // Aqua
255, 255, 255, 0, // White
};
if( !LoadVFL() )
return E_FAIL;
m_lChartID = lChartID;
m_lElemID = lElemID;
m_lPeriodID = lPeriodID;
m_lUnitID = lUnit;
m_bstrFlags = bstrFlags;
m_lWidth = lWidth;
m_lHeight = lHeight;
CChart& ch = m_charts.GetInit(lChartID-1);
BYTE r, g, b;
LPCTSTR lpszExopt = ch.GetExopt();
// lChartID gives 1-based position in WSCUSTOM.xml
// id is the id attribute in of that chart
long id = ch.GetID();
int flags = ch.GetFlags();
bool bSetBackground = (id > CH_FUND && lpszExopt[0]);
if( (flags & chartGaugeType) == 0 )
bSetBackground = bSetBackground && (strchr((const char*)m_bstrFlags,
'o') == 0);
if( bSetBackground )
{
to_rgb(lpszExopt, r, g, b);
pal1[14].rgbRed = r;
pal1[14].rgbGreen = g;
pal1[14].rgbBlue = b;
}
CDataSource src;
CSession session;
HRESULT hr = InitDataSource(src);
if( hr == S_OK )
hr = session.Open(src);
if( hr == S_OK )
hr = LoadColumns(session);
if( hr == S_OK )
hr = CheckDateFormat(m_login.sConn.c_str(), session);
if( FAILED(hr) ) return hr;
if( m_lElemID > 0 && m_lPeriodID == 0 )
{
char fmt[256], buf[256];
dbItemID item;
LoadString(_Module.m_hInst, IDS_QRY_LATESTPER, fmt, sizeof(fmt));
wsprintf(buf, fmt, m_lElemID, m_lUserID);
hr = LoadItemID(session, buf, item);
if(FAILED(hr)) return hr;
m_lPeriodID = item.id;
}
HDC hdcRef = GetDC(NULL);
int iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);
int iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);
int iWidthPels = GetDeviceCaps(hdcRef, HORZRES);
int iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
RECT rect = { 0 };
rect.right = (m_lWidth * iWidthMM * 100)/iWidthPels;
rect.bottom = (m_lHeight * iHeightMM * 100)/iHeightPels;
HDC hDC = CreateEnhMetaFile(hdcRef, NULL, &rect, NULL);
if( hDC == NULL )
{
ReleaseDC(NULL, hdcRef);
return E_FAIL;
}
HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
if( hBrush )
{
FillRect( hDC, &rect, hBrush );
DeleteObject(hBrush);
}
m_bCanZoom = true;
id = ch.GetID();
switch( id )
{
case CH_CSVARTRND:
m_bCanZoom = false;
hr = GenerateCSVarTrendsChart( session, hDC );
break;
case CH_CONTPERF:
m_bCanZoom = false;
hr = GenerateContPerfChart( session, hDC );
break;
case CH_ELPERF_CUM:
case CH_ELPERF_CUR:
hr = GenerateElemPerfChart( session, hDC );
break;
case CH_TPANAL_CUM:
case CH_TPANAL_CUR:
hr = GenerateTimePhaseAnalysisChart( session, hDC );
break;
case CH_MANPOWER:
m_bCanZoom = false;
hr = GenerateManpowerChart( session, hDC );
break;
case CH_BULLSEYE:
hr = GenerateBullsEyeChart( session, hDC );
break;
case CH_CSVAR:
m_bCanZoom = false;
hr = GenerateCSVarChart(session, hDC);
break;
case CH_ARMYCS:
m_bCanZoom = false;
hr = GenerateArmyCSVarChart( session, hDC );
break;
case CH_OTB:
m_bCanZoom = false;
hr = GenerateOtbChart( session, hDC );
break;
case CH_AGBULL:
hr = GenerateAgBullsEyeChart( session, hDC );
break;
case CH_VAC:
m_bCanZoom = false; // controlled by WSCUSTOM, always load
hr = GenerateVacChart( session, hDC );
break;
case CH_BLANAL:
m_bCanZoom = false;
hr = GenerateBlAnalChart( session, hDC );
break;
case CH_FUND:
m_bCanZoom = false;
hr = GenerateFundChart( session, hDC );
break;
case CH_DCMA_CRITPATHLENINDX:
hr = GenerateDCMACriticalPathLengthIndexChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_BEI_COMPLETES:
case CH_DCMA_BEI_STARTS:
hr = GenerateDCMABaselineExecutionIndexChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CSVARTRENDS:
hr = GenerateDCMACSVarTrendsChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CTREACVAL:
hr = GenerateDCMAContEacValidityChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CSVARTRENDS_OTB:
hr = GenerateDCMAOtbChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CTREACVAL_OTB:
hr = GenerateDCMAContEacValidityOTBChart( session, hDC );
m_bCanZoom = false;
break;
default:
if( (flags & chartGaugeType) == 0 )
{
hr = GenerateAnalysisChart( session, hDC );
}
else
{
m_bCanZoom = false;
hr = GenerateGaugeChart( session, hDC );
}
break;
}
HENHMETAFILE hemf = CloseEnhMetaFile(hDC);
if(NULL == hemf)
{
hr = E_FAIL;
ReleaseDC(NULL, hdcRef);
return hr;
}
if( hr == S_OK )
{
RGBQUAD* pPalette = 0;
if( iFormat != imgFormatJPG )
pPalette = (id != CH_VAC) ? pal1 : pal2;
hr = GetEMFType(hemf, m_lWidth, m_lHeight, COLE2T(bstrFile),
iFormat, pPalette );
}
DeleteEnhMetaFile(hemf);
ReleaseDC(NULL, hdcRef);
return hr;
}
And here is the method "GetEMFType" that is called.
HRESULT GetEMFType(HENHMETAFILE hemf, long lWidth, long lHeight,
LPCSTR lpszFile, imgFormat iFormat, RGBQUAD* pPalette)
{
HRESULT hr = S_OK;
#ifdef _GDIPLUS
if (FAILED(InitializeGdiPlus()))
return hr;
GUID ImageType[5] = {
Gdiplus::ImageFormatEMF,
Gdiplus::ImageFormatBMP,
Gdiplus::ImageFormatJPEG,
Gdiplus::ImageFormatPNG,
Gdiplus::ImageFormatGIF
};
Gdiplus::Status status;
CLSID clsidEncoder = CLSID_NULL;
hr = GetGdiPlusImageCodec(ImageType[iFormat], clsidEncoder);
if (SUCCEEDED(hr))
{
USES_CONVERSION_EX;
LPCWSTR pwszFileName = T2CW_EX( lpszFile,
_ATL_SAFE_ALLOCA_DEF_THRESHOLD );
if ( NULL != pwszFileName )
{
Gdiplus::Metafile metafile(hemf, FALSE);
status = metafile.Save(pwszFileName, &clsidEncoder, NULL);
if( status != Gdiplus::Ok )
{
hr = E_FAIL;
}
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
Any help woudl be GREATLY appreciated. Even just a point in the right
direction.
that basically calls up an ASPX web page (C#) that calls a remote dll
web service (C++) that then calls the engine dlls (C++) that produces
an image of a chart and then displays it in the web part.
First here is the main method in question. It is in the engine/main
dll code. The reason I think I have a memory leak is this. In a
single user scenario the system works flawlessly. However when I hook
up HP LoadRunner with 25 users going I get problems. On the line HDC
hDC = CreateEnhMetaFile(hdcRef, NULL, &rect, NULL); I get back null
and when I call GetLastError() I get ERROR_NOT_ENOUGH_MEMORY. This
makes me think somewhere I am leaking memory that in a multi-user load
test scenario eventually piles up and over. Does that sound like a
reasonable conclusion?
STDMETHODIMP CChartGen::GetChart(BSTR bstrFile, imgFormat iFormat,
long lChartID, long lElemID, long lPeriodID, long lUnit, BSTR
bstrFlags, long lWidth, long lHeight)
{
if( (lElemID < -1) || // <= 0 indicates summary query
(lPeriodID < -1) || // <= 0 indicates cross-contract summary
query
(lUnit <= 0) ||
(lChartID <= 0 ) )
{
return E_INVALIDARG;
}
if( m_lExtraID != 0 )
{
if( m_lExtraID != lChartID )
return E_INVALIDARG;
m_lExtraID = 0;
}
if( (iFormat == imgFormatGIF) && !m_bEnableGIF )
{
return E_INVALIDARG;
}
// special palette for background colors
RGBQUAD pal1[] =
{
0,0,0,0, // Black
0, 0, 128, 0, // Maroon
0, 128, 0, 0, // Green
0, 128, 128, 0, // Olive
128, 0, 0, 0, // Navy
128, 0, 128, 0, // Purple
128, 128, 0, 0, // Teal
108, 108, 108, 0, // Gray
182, 182, 182, 0, // Silver
0, 0, 255, 0, // Red
128, 255, 128, 0, // Lime (modified)
128, 255, 255, 0, // Yellow (modified)
255, 0, 0, 0, // Blue
128, 128, 255, 0, // Fuschia (modified)
255, 192, 128, 0, // Aqua (modified)
255, 255, 255, 0, // White
};
// special colors for IVAC chart
RGBQUAD pal2[] =
{
0,0,0,0, // Black
0, 0, 128, 0, // Maroon
0, 128, 0, 0, // Green
0, 128, 128, 0, // Olive
128, 0, 0, 0, // Navy
128, 0, 128, 0, // Purple
128, 128, 0, 0, // Teal
108, 108, 108, 0, // Gray
182, 182, 182, 0, // Silver
0, 0, 255, 0, // Red
0, 255, 0, 0, // Lime
0, 255, 255, 0, // Yellow
255, 0, 0, 0, // Blue
255, 0, 255, 0, // Fuschia
255, 255, 0, 0, // Aqua
255, 255, 255, 0, // White
};
if( !LoadVFL() )
return E_FAIL;
m_lChartID = lChartID;
m_lElemID = lElemID;
m_lPeriodID = lPeriodID;
m_lUnitID = lUnit;
m_bstrFlags = bstrFlags;
m_lWidth = lWidth;
m_lHeight = lHeight;
CChart& ch = m_charts.GetInit(lChartID-1);
BYTE r, g, b;
LPCTSTR lpszExopt = ch.GetExopt();
// lChartID gives 1-based position in WSCUSTOM.xml
// id is the id attribute in of that chart
long id = ch.GetID();
int flags = ch.GetFlags();
bool bSetBackground = (id > CH_FUND && lpszExopt[0]);
if( (flags & chartGaugeType) == 0 )
bSetBackground = bSetBackground && (strchr((const char*)m_bstrFlags,
'o') == 0);
if( bSetBackground )
{
to_rgb(lpszExopt, r, g, b);
pal1[14].rgbRed = r;
pal1[14].rgbGreen = g;
pal1[14].rgbBlue = b;
}
CDataSource src;
CSession session;
HRESULT hr = InitDataSource(src);
if( hr == S_OK )
hr = session.Open(src);
if( hr == S_OK )
hr = LoadColumns(session);
if( hr == S_OK )
hr = CheckDateFormat(m_login.sConn.c_str(), session);
if( FAILED(hr) ) return hr;
if( m_lElemID > 0 && m_lPeriodID == 0 )
{
char fmt[256], buf[256];
dbItemID item;
LoadString(_Module.m_hInst, IDS_QRY_LATESTPER, fmt, sizeof(fmt));
wsprintf(buf, fmt, m_lElemID, m_lUserID);
hr = LoadItemID(session, buf, item);
if(FAILED(hr)) return hr;
m_lPeriodID = item.id;
}
HDC hdcRef = GetDC(NULL);
int iWidthMM = GetDeviceCaps(hdcRef, HORZSIZE);
int iHeightMM = GetDeviceCaps(hdcRef, VERTSIZE);
int iWidthPels = GetDeviceCaps(hdcRef, HORZRES);
int iHeightPels = GetDeviceCaps(hdcRef, VERTRES);
RECT rect = { 0 };
rect.right = (m_lWidth * iWidthMM * 100)/iWidthPels;
rect.bottom = (m_lHeight * iHeightMM * 100)/iHeightPels;
HDC hDC = CreateEnhMetaFile(hdcRef, NULL, &rect, NULL);
if( hDC == NULL )
{
ReleaseDC(NULL, hdcRef);
return E_FAIL;
}
HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
if( hBrush )
{
FillRect( hDC, &rect, hBrush );
DeleteObject(hBrush);
}
m_bCanZoom = true;
id = ch.GetID();
switch( id )
{
case CH_CSVARTRND:
m_bCanZoom = false;
hr = GenerateCSVarTrendsChart( session, hDC );
break;
case CH_CONTPERF:
m_bCanZoom = false;
hr = GenerateContPerfChart( session, hDC );
break;
case CH_ELPERF_CUM:
case CH_ELPERF_CUR:
hr = GenerateElemPerfChart( session, hDC );
break;
case CH_TPANAL_CUM:
case CH_TPANAL_CUR:
hr = GenerateTimePhaseAnalysisChart( session, hDC );
break;
case CH_MANPOWER:
m_bCanZoom = false;
hr = GenerateManpowerChart( session, hDC );
break;
case CH_BULLSEYE:
hr = GenerateBullsEyeChart( session, hDC );
break;
case CH_CSVAR:
m_bCanZoom = false;
hr = GenerateCSVarChart(session, hDC);
break;
case CH_ARMYCS:
m_bCanZoom = false;
hr = GenerateArmyCSVarChart( session, hDC );
break;
case CH_OTB:
m_bCanZoom = false;
hr = GenerateOtbChart( session, hDC );
break;
case CH_AGBULL:
hr = GenerateAgBullsEyeChart( session, hDC );
break;
case CH_VAC:
m_bCanZoom = false; // controlled by WSCUSTOM, always load
hr = GenerateVacChart( session, hDC );
break;
case CH_BLANAL:
m_bCanZoom = false;
hr = GenerateBlAnalChart( session, hDC );
break;
case CH_FUND:
m_bCanZoom = false;
hr = GenerateFundChart( session, hDC );
break;
case CH_DCMA_CRITPATHLENINDX:
hr = GenerateDCMACriticalPathLengthIndexChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_BEI_COMPLETES:
case CH_DCMA_BEI_STARTS:
hr = GenerateDCMABaselineExecutionIndexChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CSVARTRENDS:
hr = GenerateDCMACSVarTrendsChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CTREACVAL:
hr = GenerateDCMAContEacValidityChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CSVARTRENDS_OTB:
hr = GenerateDCMAOtbChart( session, hDC );
m_bCanZoom = false;
break;
case CH_DCMA_CTREACVAL_OTB:
hr = GenerateDCMAContEacValidityOTBChart( session, hDC );
m_bCanZoom = false;
break;
default:
if( (flags & chartGaugeType) == 0 )
{
hr = GenerateAnalysisChart( session, hDC );
}
else
{
m_bCanZoom = false;
hr = GenerateGaugeChart( session, hDC );
}
break;
}
HENHMETAFILE hemf = CloseEnhMetaFile(hDC);
if(NULL == hemf)
{
hr = E_FAIL;
ReleaseDC(NULL, hdcRef);
return hr;
}
if( hr == S_OK )
{
RGBQUAD* pPalette = 0;
if( iFormat != imgFormatJPG )
pPalette = (id != CH_VAC) ? pal1 : pal2;
hr = GetEMFType(hemf, m_lWidth, m_lHeight, COLE2T(bstrFile),
iFormat, pPalette );
}
DeleteEnhMetaFile(hemf);
ReleaseDC(NULL, hdcRef);
return hr;
}
And here is the method "GetEMFType" that is called.
HRESULT GetEMFType(HENHMETAFILE hemf, long lWidth, long lHeight,
LPCSTR lpszFile, imgFormat iFormat, RGBQUAD* pPalette)
{
HRESULT hr = S_OK;
#ifdef _GDIPLUS
if (FAILED(InitializeGdiPlus()))
return hr;
GUID ImageType[5] = {
Gdiplus::ImageFormatEMF,
Gdiplus::ImageFormatBMP,
Gdiplus::ImageFormatJPEG,
Gdiplus::ImageFormatPNG,
Gdiplus::ImageFormatGIF
};
Gdiplus::Status status;
CLSID clsidEncoder = CLSID_NULL;
hr = GetGdiPlusImageCodec(ImageType[iFormat], clsidEncoder);
if (SUCCEEDED(hr))
{
USES_CONVERSION_EX;
LPCWSTR pwszFileName = T2CW_EX( lpszFile,
_ATL_SAFE_ALLOCA_DEF_THRESHOLD );
if ( NULL != pwszFileName )
{
Gdiplus::Metafile metafile(hemf, FALSE);
status = metafile.Save(pwszFileName, &clsidEncoder, NULL);
if( status != Gdiplus::Ok )
{
hr = E_FAIL;
}
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
Any help woudl be GREATLY appreciated. Even just a point in the right
direction.