ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  Memory leak in MFC (Chartdir 7.0)
Posted by Alex on Aug-31-2025 04:42
Attachments:
Hello, dear support community! Hope Peter you are doing well!!!
We have a project with the following environment: VS 2022, C++14, MFC, Chart Director 7.0. And during the "stress tests" Deleaker software found the memory leaks

1. See Deleaker_snapshot attachement.

2. Angular meter code we are using (code running in separate thread, not in main thread, see attachment AngularMeter code).

3. Thread running code (see attachment Thread Running Code).



Also we tried two different versions of ChartViwer (also atteached the files 6.0 and 7.0) and behavior is completely different, code from 7.0 version draws the angular meter very very slow, code from 6.0 version draws the meter pretty fast, but finally both of them show the heap memory leak in these lines (lines are shown on screenshot):

RoboX.exe!CStaticHelper::displayChart Line 1826 + 0x10 bytes 00d6295f D:\VSProjects\ARC\ChartViewer.cpp, line 1826
RoboX.exe!CChartViewer::commitUpdateChart Line 921 + 0x8 bytes 00d61be7 D:\VSProjects\ARC\ChartViewer.cpp, line 921
RoboX.exe!CChartViewer::setChart Line 547 + 0x4 bytes 00d615c0 D:\VSProjects\ARC\ChartViewer.cpp, line 547

Could you please help us to find a solution how to avoid memory leaks. Thank you!!!
Deleaker_snapshot.JPG
ChartViewer 6.0.zip
ChartViewer 6.0.zip

14.58 Kb
ChartViewer 7.0.zip
ChartViewer 7.0.zip

17.82 Kb
   
AngularMetercode.txt
void drawSpeedMeter(CChartViewer* viewer, double pointer_value, CStringA label1)
{
	try
	{
		int chartIndex = 3;// индекс в массиве mainColor, устанавливаем основной цвет

		// The main color of the four meters in this example. The other colors and gradients are derived
		// from the main color.
		int colorList[] = { 0x007700, 0x770077, 0x0033dd, RGB(environment_b, environment_g, environment_r) };
		//int colorList[] = { 0x007700, 0x770077, 0x0033dd, Chart::Transparent };
		int mainColor = colorList[chartIndex];

		//
		// In this example, we demonstrate how to parameterized by size, so that the chart size can be
		// changed by changing just one variable.
		//
		float scale = min(scalefactor.x, scalefactor.y);
		int size = 500 * scale;

		// The radius of the entire meter, which is size / 2, minus 2 pixels for margin
		int outerRadius = size / 2 - 2;

		// The radius of the meter scale
		int scaleRadius = outerRadius * 92 / 100;

		// The radius of the inner decorative circle
		int innerRadius = scaleRadius * 40 / 100;

		// The width of the color scale
		int colorScaleWidth = scaleRadius * 10 / 1000;

		// Major tick length
		int tickLength = scaleRadius * 10 / 100;

		// Major tick width
		int tickWidth = scaleRadius * 1 / 100 + 1;

		// Label font size
		int fontSize = scaleRadius * 13 / 100;

		//
		// Create an angular meter based on the above parameters
		//

		// Create an AngularMeter object of the specified size. In this demo, we use black (0x000000) as
		// the background color. You can also use transparent or other colors.
		//m = new AngularMeter(size, size, 0x000000);
		AngularMeter *m = new AngularMeter(size, size, Chart::Transparent, Chart::Transparent);
		m->setWallpaper("data/matrix.jpg");
		

		// Set the default text and line colors to white (0xffffff)
		m->setColor(Chart::TextColor, RGB(text_b, text_g, text_r));
		m->setColor(Chart::LineColor, RGB(environment_b, environment_g, environment_r));


		// Set meter center and scale radius, and set the scale angle from -180 to +90 degrees
		m->setMeter(size / 2, size / 2, scaleRadius, -140, 140);

		// Background gradient with the mainColor at the center and become darker near the border
		//double bgGradient[] = { 0, (double)(mainColor), 0.5, (double)(m->adjustBrightness(mainColor, 0.75
			//)), 1, (double)(m->adjustBrightness(mainColor, 0.15)) };

		/*transparent*/
		double bgGradient[] = { 0, Chart::Transparent, 0.4, mainColor, 1.1, Chart::Transparent };
		const int bgGradient_size = (int)(sizeof(bgGradient) / sizeof(*bgGradient));


		// Fill the meter background with the background gradient
		//m->addRing(0, outerRadius, m->relativeRadialGradient(DoubleArray(bgGradient, (int)(sizeof(
			//bgGradient) / sizeof(bgGradient[0]))), outerRadius * 0.66), Chart::Transparent);

		/*transparent*/
		m->addRing(innerRadius, outerRadius, m->relativeRadialGradient(DoubleArray(bgGradient, bgGradient_size),
			outerRadius * 0.8));

		// Fill the inner circle with the same background gradient for decoration
		//m->addRing(0, innerRadius, m->relativeRadialGradient(DoubleArray(bgGradient, (int)(sizeof(
			//bgGradient) / sizeof(bgGradient[0]))), innerRadius * 0.8));

		/*transparent*/
		m->addRing(0, innerRadius, m->relativeRadialGradient(DoubleArray(bgGradient, bgGradient_size), innerRadius * 0.8));

		// Gradient for the neon backlight, with the main color at the scale radius fading to
		// transparent
		double neonGradient[] = { 0.89, Chart::Transparent, 1, (double)(mainColor), 1.07,
			Chart::Transparent };
		m->addRing(scaleRadius * 85 / 100, outerRadius, m->relativeRadialGradient(DoubleArray(
			neonGradient, (int)(sizeof(neonGradient) / sizeof(neonGradient[0])))));

		// The neon ring at the scale radius with width equal to 1/80 of the scale radius, creating
		// using a brighter version of the main color
		m->addRing(scaleRadius, scaleRadius + scaleRadius / 80, m->adjustBrightness(mainColor, 2));

		// Meter scale is 0 - 100, with major/minor/micro ticks every 10/5/1 units
		m->setScale(0, 100, 10, 5, 1);

		// Set the scale label style, tick length and tick width. The minor and micro tick lengths are
		// 80% and 60% of the major tick length, and their widths are around half of the major tick
		// width.
		m->setLabelStyle("data/arialunicodems.ttf", fontSize);
		m->setTickLength(-tickLength, -tickLength * 80 / 100, -tickLength * 60 / 100);
		m->setLineWidth(0, tickWidth, (tickWidth + 1) / 2, (tickWidth + 1) / 2);
		m->setMeterColors(RGB(text_b, text_g, text_r), RGB(text_b, text_g, text_r), RGB(text_b, text_g, text_r));

		// Demostrate different types of color scales and glare effects and putting them at different
		// positions.
		double smoothColorScale[] = { 0, 0x0000ff, 25, 0x0088ff, 50, 0x00ff00, 75, 0xdddd00, 100,
			0xff0000 };
		double stepColorScale[] = { 0, 0x00dd00, 30, 0xddaa00, 70, 0xdd0000, 100 };
		double highColorScale[] = { 70, Chart::Transparent, 100, 0xff0000 };

		if (chartIndex == 1) {
			// Add the smooth color scale just outside the inner circle
			m->addColorScale(DoubleArray(smoothColorScale, (int)(sizeof(smoothColorScale) / sizeof(
				smoothColorScale[0]))), innerRadius + 1, colorScaleWidth);
			// Add glare up to the scale radius, concave and spanning 190 degrees
			m->addGlare(scaleRadius, -190);
		}
		else if (chartIndex == 2) {
			// Add the high color scale at the default position
			m->addColorScale(DoubleArray(highColorScale, (int)(sizeof(highColorScale) / sizeof(
				highColorScale[0]))));
			// Add glare up to the scale radius
			m->addGlare(scaleRadius);
		}
		else {
			// Add the step color scale just outside the inner circle
			m->addColorScale(DoubleArray(stepColorScale, (int)(sizeof(stepColorScale) / sizeof(
				stepColorScale[0]))), innerRadius + 1, colorScaleWidth);
			// Add glare up to the scale radius, concave and spanning 190 degrees and rotated by -45
			// degrees
			//m->addGlare(scaleRadius, -190, -45);
		}

		if (pointer_value <= 31)
		{
			// Add a red (0xff0000) pointer at the specified value
			m->addPointer2(pointer_value, 0x00dd00);
		}
		if (pointer_value >= 31 && pointer_value <= 71)
		{
			// Add a red (0xff0000) pointer at the specified value
			m->addPointer2(pointer_value, 0xddaa00);
		}
		if (pointer_value >= 71 && pointer_value <= 101)
		{
			// Add a red (0xff0000) pointer at the specified value
			m->addPointer2(pointer_value, 0xdd0000);
		}

		// Set the cap background to a brighter version of the mainColor, and using black (0x000000) for
		// the cap and grey (0x999999) for the cap border
		m->setCap2(m->adjustBrightness(mainColor, 1.1), Chart::Transparent, Chart::Transparent);

		// Add a text label коробка скоростей centered at (250, 410) with 20pt Arial Bold font
		if (label1 == TEXT("L"))
		{
			m->addText(250 * scale, 410 * scale, label1, "data/arialunicodems.ttf", 20 * min(scalefactor.x, scalefactor.y), 0x00dd00, Chart::Center);
		}
		if (label1 == TEXT("D"))
		{
			m->addText(250 * scale, 410 * scale, label1, "data/arialunicodems.ttf", 20 * min(scalefactor.x, scalefactor.y), 0xddaa00, Chart::Center);
		}
		if (label1 == TEXT("T"))
		{
			m->addText(250 * scale, 410 * scale, label1, "data/arialunicodems.ttf", 20 * min(scalefactor.x, scalefactor.y), 0xdd0000, Chart::Center);
		}

		// Add a text label еденицы измерения centered at (250, 320) with 15pt Arial Italic font
		if (label1 == TEXT("L"))
		{
			m->addText(250 * scale, 320 * scale, "km/h", "data/arialunicodems.ttf", 15 * min(scalefactor.x, scalefactor.y), 0x00dd00, Chart::Center);
		}
		if (label1 == TEXT("D"))
		{
			m->addText(250 * scale, 320 * scale, "km/h", "data/arialunicodems.ttf", 15 * min(scalefactor.x, scalefactor.y), 0xddaa00, Chart::Center);
		}
		if (label1 == TEXT("T"))
		{
			m->addText(250 * scale, 320 * scale, "km/h", "data/arialunicodems.ttf", 15 * min(scalefactor.x, scalefactor.y), 0xdd0000, Chart::Center);
		}


		// Output the chart
		viewer->setChart(m);
		
		//free up resources		
		delete m;

	}
	catch (...) {}
}
Thread Runningcode.txt
void CsdkDemoDlg::SPEEDANIMATION(CStringA gear_mode)
{
          for (i = _last_speed; i < 31.0; i++)
	 {
		drawSpeedMeter(&m_Speedometer, i, gear_mode);
		_last_speed = i;
		// Sleep one milisecond
		boost::this_thread::sleep(boost::posix_time::milliseconds(1));
		// Make sure we can be interrupted
		boost::this_thread::interruption_point();
	}
}

  Re: Memory leak in MFC (Chartdir 7.0)
Posted by Peter Kwan on Aug-31-2025 14:00
Hi Alex,

It seems your code is multi-threaded. Are you aware in general you should not call GUI code from another thread (other than the GUI thread)? This applies to all common GUI frameworks, including MFC.

The GUI is "event driven". That means GUI code handle events, such as mouse events, display update events and hundreds of other internal events. (MFC provides default implementation for many of the events.) The event driver code runs in the GUI thread (typically the startup thread).

If your code calls the GUI from another thread, they will conflict with the code running in the GUI thread.

In ChartDirector, the CChartViewer is a MFC control and it is a GUI object. The chart objects themselves (eg. XYChart, PieChart, AngularMeter) are not GUI objects.

The following is an example of how to implement a multi-threaded charting program:

https://www.advsofteng.com/tutorials/real_time_chart_multithread/real_time_chart_multithread.html

Best Regards
Peter Kwan

  Re: Memory leak in MFC (Chartdir 7.0)
Posted by Peter Kwan on Aug-31-2025 18:50
Hi Alex,

I noted your SPEEDANIMATION is just a loop calling the charting code.

Instead of using a loop running in another thread, you can use the MFC SetTimer instead. This will trigger a timer event periodically. You code can handle this event (ON_WM_TIMER) and draw the chart. As the event occurs in the GUI thread, it is safe to update the GUI display.

Best Regards
Peter Kwan

  Re: Memory leak in MFC (Chartdir 7.0)
Posted by Alex on Sep-01-2025 11:25
Hi, Peter! Thank you very much for your time and help. I confirm there is NO MEMORY LEAK in Chart Director 7.0 after we made the changes you suggested!!!

Best regards!!!