Enumerating System Code Pages






4.63/5 (8 votes)
MFC and ATL wrapper classes for the EnumSystemCodePages API call
- Download source files - 5.41 KB
- Download demo MFC/WTL projects - 200 KB
(for both Visual Studio 6 and Visual Studio 2005)

Introduction
Recently I needed some code that would enumerate the installed code pages on a system, and came across the EnumSystemCodePages
API call, which does exactly what it says (you can enumerate both installed code pages, and those that Windows actually support). However, I found that this API call works slightly differently to other Enum...
type calls, which can potentially cause problems with multi-threaded applications (more on this later).
So, to address this I have created a special CCodePage
class, which you can use in your MFC/ATL/WTL/STL applications to easily:
- Enumerate installed code pages
- Enumerate system code pages
- Fetch the name of a specific code page
In order to please as many people as possible, there are three separate versions of the class:
- An MFC version which will work with both MFC42 (Visual Studio 6) and MFC8 (Visual Studio 2005)
- An ATL version that works with both Visual Studio 6 and Visual Studio 2005. This version can also be used with WTL application in both environments.
- An STL-friendly version (Note: This version relies on a component of the Boost library and will be covered separately.)
Using the Code
To use this code in your application, first include the appropriate header:
MFC Version
#include "CodePageMFC.h"
ATL Version
#include "CodePageATL.h"
Next, create an instance of the CCodePage
class - note that the sample code supplied belongs to the rec
namespace:
rec::CCodePage codepages;
or, if you prefer:
using namespace rec;
...
CCodePage codepages;
Next you need to decide if you want a list of code pages that are actually installed, or those that are supported by your Windows version. To this end, the CCodePage
class exposes the following two methods, both of which return a const
reference to a CCodePage::CCodePageObject.
For example:
using namespace rec;
CCodePage codepages;
const CCodePage::CCodePageObject installed = codepage.GetInstalled();
...
const CCodePage::CCodePageObject supported = codepage.GetSupported();
The CCodePageObject
class supports the following public
methods:
GetCount
: Returns the number of code pages availableoperator[index]
: Returns a code page ID at a specific indexIsEmpty
: Returnstrue
if the object is empty (i.e. no code pages are available)GetName(index)
: Returns the name of a code page at a specific index
A CCodePageObject
is designed to be treated like an array, so, for example, to output the ID
and name of all the installed code pages in an MFC application, simply do the following:
using namespace rec;
CCodePage codepages;
const CCodePage::CCodePageObject installed = codepages.GetInstalled();
for (int i = 0; i < installed.GetCount(); i++)
{
TRACE(_T("ID: %d, name: %s\n"), installed[i], installed.GetName(i));
}
Pretty simple.
EnumSystemCodePages
Enumerating code pages isn't rocket science - you simply call the EnumSystemCodePages
API function. However, unlike almost every other Enum...
function I have come across, you cannot pass a lParam
to the callback function. Why is this a problem? Well, the callback function simply takes a single parameter - the code page ID in string
form:
BOOL CALLBACK EnumCodePagesProc(LPTSTR lpCodePageString)
Now, this means that if you want to do something useful with lpCodePageString
- such as add the string
to a container - you need to use some static/global variables, which means you could end up with some thread safety problems. For example, if your EnumCodePagesProc
was adding the lpCodePageString
to a global container, what would happen if two threads were calling this function simultaneously?
To solve this, I use a CRITICAL_SECTION
to ensure that the EnumCodePagesProc
function cannot execute. The MFC version uses a combination of a CCriticalSection
and a CSingleLock
object, and the ATL version uses a CComAutoCriticalSection
.
Ideally, the EnumSystemCodePages
function would take a lParam
that would then be passed to the callback function. This lParam
could be, for example, the address of a suitable container - avoiding the threading issue altogether. Certainly this is how other Enum...
functions seem to operate.
Note that the code pages are not actually enumerated until you call one of the Get...
functions for the first time.
Under the Hood
The MFC header is shown here:
#pragma once
#include <afxtempl.h>
#include <afxmt.h>
namespace rec
{
class CCodePage
{
private:
// Code page ID container typedef
// You can replace this with a container implementation of your choice
typedef CArray<UINT, UINT> CodePageContainer;
public:
class CCodePageObject
{
private:
CodePageContainer s_arrCodePages; // Container of code page IDs
static CCriticalSection s_critSec; // Critical section used for thread-safety
static CCodePageObject* s_pCurrent; // Current code page object being filled
public:
// Return the number of available code pages
int GetCount() const;
// Return a code page at a specific index
int operator[](int nIndex) const;
// Returns true if the code page container is empty
bool IsEmpty() const;
// Return the name of a code page at a specific index in the container
CString GetName(int nIndex) const;
// Enumerate code pages using specific flags
static void EnumerateCodePages(CCodePageObject* pObject);
private:
// Add a new code page to the container
void Add(UINT nCodePage);
// EnumSystemCodePages callback function (Installed)
static BOOL CALLBACK EnumCodePagesProc(LPTSTR lpCodePageString);
// Pure virtual function that returns the flags to be passed
// to the EnumSystemCodePages function
virtual DWORD GetFlags() const = 0;
};
// Class used to store installed code page IDs
class CInstalled : public CCodePageObject
{
private:
virtual DWORD GetFlags() const { return CP_INSTALLED; }
};
// Class used to store supported code page IDs
class CSupported : public CCodePageObject
{
private:
virtual DWORD GetFlags() const { return CP_SUPPORTED; }
};
private:
CInstalled m_cpInstalled; // Installed code pages
CSupported m_cpSupported; // Supported code pages
public:
// Get the installed code pages
const CCodePageObject& GetInstalled();
// Get the supported code pages
const CCodePageObject& GetSupported();
};
}
The MFC source:
#include "stdafx.h"
#include "CodePageMFC.h"
namespace rec
{
// Initialize static variables
CCriticalSection CCodePage::CCodePageObject::s_critSec;
CCodePage::CCodePageObject* CCodePage::CCodePageObject::s_pCurrent = NULL;
// Return the number of available code pages
int CCodePage::CCodePageObject::GetCount() const
{
return static_cast<int>(s_arrCodePages.GetSize());
}
// Return a code page at a specific index
int CCodePage::CCodePageObject::operator[](int nIndex) const
{
return s_arrCodePages[nIndex];
}
// Returns true if the code page container is empty
bool CCodePage::CCodePageObject::IsEmpty() const
{
return s_arrCodePages.GetSize() == 0;
}
// Add a new code page to the container
void CCodePage::CCodePageObject::Add(UINT nCodePage)
{
s_arrCodePages.Add(nCodePage);
}
// Return the name of a code page at a specific index in the container
CString CCodePage::CCodePageObject::GetName(int nIndex) const
{
#if (WINVER >= 0x410)
CPINFOEX cpInfoEx = { 0 };
GetCPInfoEx(s_arrCodePages[nIndex], 0, &cpInfoEx);
return cpInfoEx.CodePageName;
#else
// GetCPInfoEx not supported on 95/NT4
return _T("");
#endif
}
// Enumerate code pages using specific flags
void CCodePage::CCodePageObject::EnumerateCodePages(CCodePageObject* pObject)
{
ASSERT(pObject != NULL);
// Lock access to this function to ensure thread safety
CSingleLock lock(&s_critSec, TRUE);
s_pCurrent = pObject;
::EnumSystemCodePages(EnumCodePagesProc, pObject->GetFlags());
}
// EnumSystemCodePages callback function
BOOL CALLBACK CCodePage::CCodePageObject::EnumCodePagesProc(LPTSTR lpCodePageString)
{
ASSERT(s_pCurrent != NULL);
// Format the code page string as an unsigned int
// and add to the current code page object
s_pCurrent->Add(_ttoi(lpCodePageString));
return TRUE;
}
// Get the installed code pages
const CCodePage::CCodePageObject& CCodePage::GetInstalled()
{
if (m_cpInstalled.IsEmpty())
CCodePageObject::EnumerateCodePages(&m_cpInstalled);
return m_cpInstalled;
}
// Get the supported code pages
const CCodePage::CCodePageObject& CCodePage::GetSupported()
{
if (m_cpSupported.IsEmpty())
CCodePageObject::EnumerateCodePages(&m_cpSupported);
return m_cpSupported;
}
}
Sample Applications
Various flavour sample applications are included, but they all do the same thing - display a dialog box containing lists of installed and supported code pages. Release builds are included in the zip file.
Windows 95/NT4
Note that the GetCPInfoEx
function (used here to obtain the code page name) isn't supported on Win95/NT4, and you will note that WINVER
is set to 0x0410
in stdafx.h accordingly. However, if you want to use this code in an application that has to run on either of these platforms, then you can dynamically look for the GetCPInfoEx
function using the following replacement for rec::CCodePage::CCodePageObject::GetText
:
// Return the name of a code page at a specific index in the container
CString CCodePage::CCodePageObject::GetName(int nIndex) const
{
typedef BOOL (WINAPI *GETCPINFOEX_FN)(UINT, DWORD, LPCPINFOEX);
GETCPINFOEX_FN pGetCPInfoEx = NULL;
HMODULE hKernel = GetModuleHandle(_T("Kernel32.dll"));
if (hKernel != NULL)
{
#ifdef _UNICODE
pGetCPInfoEx =
reinterpret_cast<GETCPINFOEX_FN>(::GetProcAddress(kernel, "GetCPInfoExW"));
#else
pGetCPInfoEx =
reinterpret_cast<GETCPINFOEX_FN>(::GetProcAddress(kernel, "GetCPInfoExA"));
#endif
if (pGetCPInfoEx != NULL)
{
CPINFOEX cpInfoEx = { 0 };
if (pGetCPInfoEx(s_arrCodePages[nIndex], 0, &cpInfoEx))
return cpInfoEx.CodePageName;
}
}
return _T("");
}
A version for STL/Boost
Also included in the source file download is an STL version. Note that in the tradition of STL-like names, the class has been renamed as rec::code_page
.
The STL version uses a std::vector
as the container for storing the code page ID
s, and for thread-safety, a boost::mutex
is used in conjunction with a boost::mutex::scoped_lock
. This version should really only be taken with a pinch of salt - I added it for completeness.
Unicode
All versions of the code are Unicode compliant, so feel free to compile for either Unicode or MBCS. I haven't tested the code using the UnicoWS library under 98/ME, but according to the MSDN, it should work.
Oh, the STL version is Unicode only (via std::wstring
), but you could easily change this using a TCHAR
-friendly string
(plenty of examples of this here on CodeProject).
FWIW, I only ever use Unicode nowadays, linking with UnicoWS if Win9x support is absolutely necessary.
Blank Code Page Names
Note that when requesting the name of a supported code page, it may well be returned empty, as the code page isn't necessarily present on your system. However, I noticed that I have two missing names from code pages that are apparently installed - 20949 and 28603 which is a mystery (I am running XP SP2).
Possible Enhancements
There are probably many other ways to approach this problem (i.e. a seemingly poorly implemented callback function for EnumSystemCodePages
), so if you can think of ways in which I can improve this code, then please let me know.