Tag Archives: MFC

CLastError

Small class for resolving and presenting in user readable form the codes from ::GetLastError() Win32 API function. The class has 2 static methods:

1
2
3
4
5
6
7
8
9
10
void CLastError::Get( const DWORD& dwErrorCode, CString& strErrMessage )
{
	LPTSTR lpErrorText = NULL;
 
	::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 
		0, dwErrorCode, 0, lpErrorText, MAX_PATH, 0 );
 
	strErrMessage = lpErrorText;
	::LocalFree( lpErrorText );
}

The ‘Get’ method supplied an error code will resolve the code to user readable error message and store it in the supplied ‘strErrMessage’ string!

1
2
3
4
5
6
7
8
9
10
11
12
void CLastError::Show( const DWORD& dwErrorCode, const CString& strAppName )
{
	CString strErrMessage;
	Get( dwErrorCode, strErrMessage );
 
	CString strDisplayError;
	strDisplayError.Format( 
		_T("%s encountered an error and needs to close!\n\nError was: %s"),
		strAppName, strErrMessage );
 
	AfxMessageBox( strDisplayError, MB_ICONERROR | MB_OK );
}

The ‘Show’ method supplied an error code will resolve the code to user readable error message and the message will be displayed to the user trough AfxMessageBox() function.

Class declaration:

class CLastError
{
public:
 
	CLastError( );
	virtual ~CLastError( );
 
public:
 
	static void Get( const DWORD& dwErrorCode, CString& strErrMessage );
	static void Show( const DWORD& dwErrorCode, const CString& strAppName );
};

Class implementation:

CLastError::CLastError( )
{
}
 
CLastError::~CLastError( )
{
}
 
void CLastError::Get( const DWORD& dwErrorCode, CString& strErrMessage )
{
	LPTSTR lpErrorText = NULL;
 
	::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 
		0, dwErrorCode, 0, lpErrorText, MAX_PATH, 0 );
 
	strErrMessage = lpErrorText;
	::LocalFree( lpErrorText );
}
 
void CLastError::Show( const DWORD& dwErrorCode, const CString& strAppName )
{
	CString strErrMessage;
	Get( dwErrorCode, strErrMessage );
 
	CString strDisplayError;
	strDisplayError.Format( 
		_T("%s encountered an error and needs to close!\n\nError was: %s"),
		strAppName, strErrMessage );
 
	AfxMessageBox( strDisplayError, MB_ICONERROR | MB_OK );
}

CSortArray updated

This is an update of the CSortArray class that I wrote few weeks ago. In this version the sort functionality is extended and search functionality is provided.

/// <summary> Extends CArray, providing search and sort functionality </summary>
template <class TYPE, class ARG_TYPE = const TYPE&> class CSortArray : public CArray<TYPE, ARG_TYPE>
{
// Public member functions
public:
 
	/// <summary> Sort's the array content </summary>
	/// <param name="compare"> User defined compare function </param>
	/// <param name="context"> Pointer to a user defined object that can be accessed in the compare function </param>
	void QuickSort( int ( __cdecl *compare )(const void*, const void*) )
	{
		qsort( m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
	void QuickSort( int ( __cdecl *compare )(void*, const void*, const void*), void* context )
	{
		qsort_s( m_pData, GetCount(), sizeof(TYPE), (*compare), context );
	}
 
	/// <summary> Searches element in the array </summary>
	/// <param name="oKey"> Key to search </param>
	/// <param name="compare"> User defined compare function </param>
	/// <param name="context"> Pointer to a user defined object that can be accessed in the compare function </param>
	TYPE* Search( const TYPE& oKey, int ( __cdecl *compare )(const void*, const void*) )
	{
		return (TYPE*)bsearch( &oKey, m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
	TYPE* Search( const TYPE& oKey, int ( __cdecl *compare )(void*, const void*, const void*), void* context )
	{
		return (TYPE*)bsearch_s( &oKey, m_pData, GetCount(), sizeof(TYPE), (*compare), context );
	}
};

The new thing about sorting is that and override of QuickSort() method is provided that accepts a sort context. Pretty useful when you want to sort an array of complex types, for example information about a locality consisting from city, county and community by city name, county name, etc.

#define SIZE_CITY_NAME 49
#define SIZE_COUNTY_NAME 49
#define SIZE_COMMUNITY_NAME 49
 
typedef struct LOCALITY
{
	int nId;
	TCHAR szCity[SIZE_CITY_NAME + 1];
	TCHAR szCommunity[SIZE_COMMUNITY_NAME + 1];
	TCHAR szCounty[SIZE_COUNTY_NAME + 1];
	int nContactsCount;
 
	LOCALITY()
	{
		::ZeroMemory(this,sizeof(*this));
	}
} LOCALITY;

Also in the QuickSort() override the pointer to a compare function now has the following form:

int ( __cdecl *compare )(void*, const void*, const void*)

where the first argument to the function is pointer to the context object provided as void *context.

The search method can use a compare function without a context or with a context. Below is an example application that sorts an array of localities first by city, then by county and community. Then searches the array for a particular locality, for example city or community:

// SortArrayApp.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
#include "SortArrayApp.h"
#include "SortArray.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
// The one and only application object
CWinApp theApp;
 
using namespace std;
 
#define CONTEXT_ID 1
#define CONTEXT_CITY 2
#define CONTEXT_COMMUNITY 3
#define CONTEXT_COUNTY 4
#define CONTEXT_CONTACTS 5
 
#define SIZE_CITY_NAME 49
#define SIZE_COUNTY_NAME 49
#define SIZE_COMMUNITY_NAME 49
 
typedef struct LOCALITY
{
	int nId;
	TCHAR szCity[SIZE_CITY_NAME + 1];
	TCHAR szCommunity[SIZE_COMMUNITY_NAME + 1];
	TCHAR szCounty[SIZE_COUNTY_NAME + 1];
	int nContactsCount;
 
	LOCALITY()
	{
		::ZeroMemory(this,sizeof(*this));
	}
} LOCALITY;
 
int Compare( void* context, const void* element1, const void* element2 )
{
	LOCALITY *pLocality1 = (LOCALITY*) element1;
	LOCALITY *pLocality2 = (LOCALITY*) element2;
	int *pContext = (int*) context;
 
	switch( *pContext )
	{
	case CONTEXT_ID:
		{
			if( pLocality1->nId > pLocality2->nId )
			{
				return 1;
			} else if ( pLocality1->nId < pLocality2->nId ) {
				return -1;
			} else if ( pLocality1->nId == pLocality2->nId ) {
				return 0;
			}
			break;
		}
	case CONTEXT_COMMUNITY:
		{
			return _tcscmp( pLocality1->szCommunity, pLocality2->szCommunity );
		}
	case CONTEXT_COUNTY:
		{
			return _tcscmp( pLocality1->szCounty, pLocality2->szCounty );
		}
	case CONTEXT_CITY:
		{
			return _tcscmp( pLocality1->szCity, pLocality2->szCity );
		}
	case CONTEXT_CONTACTS:
		{
			if( pLocality1->nContactsCount > pLocality2->nContactsCount )
			{
				return 1;
			} else if ( pLocality1->nContactsCount < pLocality2->nContactsCount ) {
				return -1;
			} else if ( pLocality1->nContactsCount == pLocality2->nContactsCount ) {
				return 0;
			}
			break;
		}
	}
 
	return 0;
}
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;
 
	HMODULE hModule = ::GetModuleHandle(NULL);
 
	if (hModule != NULL)
	{
		// initialize MFC and print and error on failure
		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
		{
			// TODO: change error code to suit your needs
			_tprintf(_T("Fatal Error: MFC initialization failed\n"));
			nRetCode = 1;
		}
		else
		{
			// SortArray usage
			CSortArray<LOCALITY> arrToSort;
 
			// Add locality information in the array
			LOCALITY recLocality;
 
			recLocality.nId = 2;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Varna") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Varna") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Varna") );
			recLocality.nContactsCount = 10;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 1;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Balgarene") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Lovech") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Lovech") );
			recLocality.nContactsCount = 3;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 4;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Veliko Turnovo") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Turnovo") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Turnovo") );
			recLocality.nContactsCount = 8;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 3;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Sofia") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Sofia Grad") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Sofia") );
			recLocality.nContactsCount = 8;
			arrToSort.Add( recLocality );
 
			// Display the unsorted elements
			cout << "Unsorted elements:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			int context = CONTEXT_ID;
 
			// Sort the array by ID
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by locality ID:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Sort the array by contacts count
			context = CONTEXT_CONTACTS;
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by contacts count:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Sort the array by City name
			context = CONTEXT_CITY;
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by City name:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Searching by city name
			context = CONTEXT_CITY;
			LOCALITY recSearch;
			wcout << "Enter city name: ";
			wcin >> recSearch.szCity;
 
			LOCALITY *pResult = arrToSort.Search( recSearch, Compare, &context );
			if( pResult )
			{
				wcout << pResult->nId << ".\t";
				wcout << pResult->szCity << "\t";
				wcout << pResult->szCommunity << "\t";
				wcout << pResult->szCounty << "\t";
				wcout << pResult->nContactsCount << endl;
			}
			else
			{
				wcout << recSearch.szCity << " not found!" << endl;
			}
		}
	}
	else
	{
		// TODO: change error code to suit your needs
		_tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
		nRetCode = 1;
	}
 
	return nRetCode;
}

NOTE: CSortArray uses the bsearch function to preform the search and expects a sorted array on which to preform the search. We can further implement a member of CSortArray that indicates if array is sorted or not.

Sorted MFC CArray

UPDATE: New version of this class is available here.

On a project I work in my spare time. I needed a quick way of sorting elements stored in CArray so CSortArray was born. It does nothing special. It extends the CArray class providing one public method named QuickSort who uses the qsort CRT function to do the sorting. Providing the right Compare function you can sort any type of array.

Below is the CSortArray class:

1
2
3
4
5
6
7
8
9
10
template <class TYPE, class ARG_TYPE = const TYPE&> class CSortArray : public CArray<TYPE, ARG_TYPE>
{
// Public member functions
public:
 
	void QuickSort( int (__cdecl *compare )(const void*, const void*) )
	{
		qsort( m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
};

The QuickSort member takes an address of user defined comparison function with the following declaration:

1
int Compare( const void* pElement1, const void* pElement2 );

where pElement1 is the first element used in the compare process and pElement2 is the second.

Your function must compare the elements and return one of the following:

  • < 0 if pElement1 is less than pElement2
  • 0 if pElement1 is equal to pElement2
  • > 0 if pElement1 is greater than pElement2

Below is an example usage of the CSortArray class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// User defined Compare function
int Compare( const void* element1, const void* element2 )
{
	int *pElem1 = (int*) element1;
	int *pElem2 = (int*) element2;
 
	if( *pElem1 < *pElem2 )
	{
		return -1;
	} else if ( *pElem1 == *pElem2 ) {
		return 0;
	} else {
		return 1;
	}
 
	return 0;
}
 
// Application entry point
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
        // SortArray usage
	CSortArray<int> arrToSort;
 
	// Add some unsorted elements to the array
	arrToSort.Add( 5 );
	arrToSort.Add( 2 );
	arrToSort.Add( 10 );
	arrToSort.Add( 17 );
	arrToSort.Add( 1 );
	arrToSort.Add( 8 );
	arrToSort.Add( 3 );
 
	// Display the unsorted elements
	cout << "Unsorted elements" << endl;
	for( int i = 0; i < arrToSort.GetCount(); i++ )
	{
		cout << arrToSort[i] << endl;
	}
 
	// Sort the array
	arrToSort.QuickSort( Compare );
 
	// Display the sorted elements
	cout << "Sorted elements" << endl;
	for( int j = 0; j < arrToSort.GetCount(); j++ )
	{
		cout << arrToSort[j] << endl;
	}
 
	return 0;
}

In the CSortArray class we can also implement a search method, but that’s a theme of another post.

SQL Server ODBC connection string

1
Driver={SQL Server};Server=Computer\Server;Database=DatabaseName;Uid=user;Pwd=password;Driver={SQL Server};Server=Computer\Server;Database=DatabaseName;Trusted_Connection =yes;

MFC: MDI singleton doc/view application

What singleton means? In our case it’s MDI application where the user can’t have more than one document of a given type open at any time. In order to implement this in doc/view fashion we need 2 things.

First we need a public member in our CDocument delivered class that when called will tell us whatever view of supplied type already exists. Here is the implementation of this public member function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
 * @brief Check's if given view is already loaded.
 * @param rtClass - the runtime class of the view.
 */
BOOL CVPhoneBookDoc::NeedViewCreation(CRuntimeClass *rtClass)
{
	POSITION pos = GetFirstViewPosition();
	while(pos != NULL)
	{
		CView *pView = GetNextView(pos);
		if(pView->GetRuntimeClass() == rtClass)
		{
			CMDIChildWnd * pViewFrame = (CMDIChildWnd *)pView->GetParentFrame();
			((CMDIFrameWnd*)AfxGetMainWnd())->MDIActivate(pViewFrame);
			return FALSE; // Already exists
		}
	}
 
	return TRUE; // Allow creation
}

The function iterates over the views associated with the document and try’s to find one that’s match our supplied run-time class. If match is found the view is activated and FALSE is returned. Else view creation is allowed.

Second we need to implement a custom member function, for example in the CWinApp delivered class that will be called as replacement of OnNewDocument() or OnOpenDocument(). Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void CVPhoneBookApp::OnCreateContactVeiw()
{
	POSITION curTemplatePos = GetFirstDocTemplatePosition();
 
	while(curTemplatePos != NULL)
	{
		CDocTemplate* curTemplate =
			GetNextDocTemplate(curTemplatePos);
		CString str;
		curTemplate->GetDocString(str, CDocTemplate::docName);
		if(str == _T("Contacts"))
		{
			// We have only one document for template "Contacts"
			POSITION docPos = curTemplate->GetFirstDocPosition();
			if(docPos == NULL)
			{
				curTemplate->OpenDocumentFile(NULL);
			}
			else
			{
				CVPhoneBookDoc *pDoc = (CVPhoneBookDoc*)curTemplate->GetNextDoc(docPos);
				if(pDoc)
				{
					// Check if view of this type exists
					if(pDoc->NeedViewCreation(RUNTIME_CLASS(CContactsView)))
					{
						curTemplate->OpenDocumentFile(NULL);
					}
				}
			}
		}
	}
}

When called this function first checks if document is available. If it’s not available we call CDocTemplate member function OpenDocumentFile() else, we retrieve a pointer to the document class and checks if view of requested type need’s to be created.