Opening COM port with CreateFile causes memory leak

Colibri VF61 IT 256
Win CE6

I am outputting log data to UARTB on a Colibri VF61. Because the parameters may change, I open the port with CreateFile() before each WriteFile() and CloseHandle() afterwards. This appears to allocate a block of memory about every three or four writes, eventually returning an OUT_OF_MEMORY error (14) when calling CreateFile(). I have written a test program which writes every 100 milliseconds to cause the error quicker.

The code below is from the Windows MFC TestCOMleak:

// ChildView.cpp : implementation of the CChildView class
//

#include “stdafx.h”
#include “TestCOMleak.h”
#include “ChildView.h”

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CChildView

CChildView::CChildView()
{
m_bTimerStarted = FALSE;
m_hPort = INVALID_HANDLE_VALUE;
}

CChildView::~CChildView()
{
KillTimer(1);
CloseRS232Port();
}

BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_TIMER()
END_MESSAGE_MAP()

// CChildView message handlers

BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CWnd::PreCreateWindow(cs))
return FALSE;

cs.style &= ~WS_BORDER;
cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, 
	::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);

return TRUE;

}

void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting

if ( !m_bTimerStarted )
{
	OpenRS232Port();
	SetTimer(1, 100, NULL);
	m_bTimerStarted = TRUE;
}

}

HRESULT CChildView::OpenRS232Port()
{
static DCB dcb;
static COMMTIMEOUTS cto;
BOOL bConnected;
DWORD dwError;
TCHAR szPort[8] = _T(“COM2:”);

CloseRS232Port();
bConnected = FALSE;

m_hPort= CreateFile(szPort, 
							GENERIC_WRITE, 
							0, 
							NULL, 
							OPEN_EXISTING, 
							0,
							NULL);

if (m_hPort != INVALID_HANDLE_VALUE)
{
	dcb.DCBlength = sizeof(DCB);
	GetCommState(m_hPort, &dcb);

	dcb.BaudRate = CBR_19200;
	dcb.fParity = FALSE;
	dcb.fOutxCtsFlow = FALSE;
	dcb.fOutxDsrFlow = FALSE;
	dcb.fDtrControl = DTR_CONTROL_DISABLE;
	dcb.fDsrSensitivity = FALSE;
	dcb.fTXContinueOnXoff = FALSE;
	dcb.fOutX = FALSE;			// Dont use XON/XOFF
	dcb.fInX = FALSE;				// Dont use XON/XOFF
	dcb.fErrorChar = FALSE;
	dcb.fNull = FALSE;
	dcb.fRtsControl = RTS_CONTROL_DISABLE;
	dcb.fAbortOnError = FALSE;
	dcb.fDummy2 = FALSE;
	// dcb.wReserved = 0;
	// dcb.XonLim = 0;
	// dcb.XoffLim = 0;
	dcb.ByteSize = 8;
	dcb.Parity = NOPARITY;
	dcb.StopBits = ONESTOPBIT;
	// dcb.XonChar = 0;
	// dcb.XoffChar = 0;
	// dcb.ErrorChar = 0;
	// dcb.EofChar = 0;
	// dcb.EvtChar = 0;
	// dcb.wReserved1 = 0;

	if (SetCommState(m_hPort, &dcb))
	{
		// Disable read and write timeouts.
		cto.ReadIntervalTimeout = 0;
		cto.ReadTotalTimeoutMultiplier = 0;	
		cto.ReadTotalTimeoutConstant =0;
		cto.WriteTotalTimeoutMultiplier = 0;
		cto.WriteTotalTimeoutConstant = 0;

		SetCommTimeouts(m_hPort, &cto);
	}
	else
	{
		DWORD dwError = GetLastError();
		ATLTRACE(_T("Error setting I/O Port.\n"));
	}
	bConnected = TRUE;
}
else
{
	dwError = GetLastError();
	ATLTRACE(_T("Failed to open %s\n"), szPort);
}

return S_OK;

}

void CChildView::CloseRS232Port()
{
if ( m_hPort != INVALID_HANDLE_VALUE )
{
BOOL bRes = CloseHandle(m_hPort);
if (!bRes)
{
DWORD dwError = GetLastError();
ATLTRACE(_T(“CloseRS232Port error: %ld\n”), dwError);
}
m_hPort = INVALID_HANDLE_VALUE;
}
}

void CChildView::OnTimer(UINT_PTR nIDEvent)
{
static DWORD dwStartAvailMem = 0;
static DWORD dwLastAvailMem = 0;
MEMORYSTATUS ms;
DWORD dwCount = 0;
char szLog[128] = “\nBW0329,805,PAEC/A,0,2.63,151.66,0.00,0.00,5.84,79,9fe\r”;

OpenRS232Port();

if ( m_hPort != INVALID_HANDLE_VALUE )
{
	BOOL bRes = WriteFile(m_hPort, szLog, strlen(szLog), &dwCount, NULL);
	Sleep(4*CBR_115200/CBR_19200);		// other operations truncate/break transmission!  Must add delay for baud under 115,200.
	if ( !bRes )
	{
		TCHAR szMessage[256];
		DWORD dwError = GetLastError();
		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, LANG_USER_DEFAULT, szMessage, 256, NULL);
		ATLTRACE(_T("COM: Write Error\n%s"), szMessage);
	}
}

CloseRS232Port();

GlobalMemoryStatus(&ms);
if ( dwStartAvailMem == 0 )
	dwStartAvailMem = ms.dwAvailPhys;

if ( ms.dwAvailPhys != dwLastAvailMem )
{
	long lTotal = (long)dwStartAvailMem - (long)ms.dwAvailPhys;
	long lNew = (dwLastAvailMem > 0) ? (long)dwLastAvailMem - (long)ms.dwAvailPhys : 0;
	ATLTRACE(_T("Total Blocks: %d\tNew Blocks: %d\n"), lTotal/4096, lNew/4096);
	if ( lNew < (-4096 * 2) )
		ATLTRACE(_T("Memory freed.\n"));

	dwLastAvailMem = ms.dwAvailPhys;
}

CWnd::OnTimer(nIDEvent);

}

Could you please create minimal but compliable console app project which will demonstrate the memory leak?

TestCOMleak.zip (22.4 KB)

This should do it.

Sorry, that’s an MFC app, not a console app but it does compile and run.

To isolate COM port related memory leak from other possible source of leaks I’d prefer to have a console application (it still can be an MFC based) to start an investigation. Do you mind to create one?

TestCOMleak2.zip (6.0 KB)

This one is a console app.

Has there been any progress on this issue?