The CIni class is composed of two files: ini.h and ini.cpp. It suports reading and writing various data types, including binary data.
ini.h
#pragma once
#define SER_GET(bGet,value) SerGet(bGet,value,#value)
#define SER_ARR(bGet,value,n) SerGet(bGet,value,n,#value)
#define SER_GETD(bGet,value,default) SerGet(bGet,value,#value,NULL,default)
#define SER_ARRD(bGet,value,n,default) SerGet(bGet,value,n,#value,default)
class CIni
{
public:
// If the IniFilename contains no path,
// the module-directory will be added to the FileName,
// to avoid storing in the windows-directory
// bModulPath=true: ModulDir, bModulPath=false: CurrentDir
static void AddModulPath(CString& rstrFileName, bool bModulPath = true);
static CString GetDefaultSection();
static CString GetDefaultIniFile(bool bModulPath = true);
CIni();
CIni(CIni const& rIni);
CIni(CString const& rstrFileName);
CIni(CString const& rstrFileName, CString const& rstrSection);
virtual ~CIni();
void SetFileName(const CString& rstrFileName);
void SetSection(const CString& rstrSection);
const CString& GetFileName() const;
const CString& GetSection() const;
CString GetString(LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL, LPCTSTR lpszSection = NULL);
CString GetStringUTF8(LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL, LPCTSTR lpszSection = NULL);
CString GetStringLong(LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL, LPCTSTR lpszSection = NULL);
double GetDouble(LPCTSTR lpszEntry, double fDefault = 0.0, LPCTSTR lpszSection = NULL);
float GetFloat(LPCTSTR lpszEntry, float fDefault = 0.0F, LPCTSTR lpszSection = NULL);
int GetInt(LPCTSTR lpszEntry, int nDefault = 0, LPCTSTR lpszSection = NULL);
ULONGLONG GetUInt64(LPCTSTR lpszEntry, ULONGLONG nDefault = 0, LPCTSTR lpszSection = NULL);
WORD GetWORD(LPCTSTR lpszEntry, WORD nDefault = 0, LPCTSTR lpszSection = NULL);
bool GetBool(LPCTSTR lpszEntry, bool bDefault = false, LPCTSTR lpszSection = NULL);
CPoint GetPoint(LPCTSTR lpszEntry, CPoint ptDefault = CPoint(0,0), LPCTSTR lpszSection = NULL);
CRect GetRect(LPCTSTR lpszEntry, CRect rectDefault = CRect(0,0,0,0), LPCTSTR lpszSection = NULL);
COLORREF GetColRef(LPCTSTR lpszEntry, COLORREF crDefault = RGB(128,128,128), LPCTSTR lpszSection = NULL);
bool GetBinary(LPCTSTR lpszEntry, BYTE** ppData, UINT* pBytes, LPCTSTR lpszSection = NULL);
void WriteString(LPCTSTR lpszEntry, LPCTSTR s, LPCTSTR lpszSection = NULL);
void WriteStringUTF8(LPCTSTR lpszEntry, LPCTSTR s, LPCTSTR lpszSection = NULL);
void WriteDouble(LPCTSTR lpszEntry, double f, LPCTSTR lpszSection = NULL);
void WriteFloat(LPCTSTR lpszEntry, float f, LPCTSTR lpszSection = NULL);
void WriteInt(LPCTSTR lpszEntry, int n, LPCTSTR lpszSection = NULL);
void WriteUInt64(LPCTSTR lpszEntry, ULONGLONG n, LPCTSTR lpszSection = NULL);
void WriteWORD(LPCTSTR lpszEntry, WORD n, LPCTSTR lpszSection = NULL);
void WriteBool(LPCTSTR lpszEntry, bool b, LPCTSTR lpszSection = NULL);
void WritePoint(LPCTSTR lpszEntry, CPoint pt, LPCTSTR lpszSection = NULL);
void WriteRect(LPCTSTR lpszEntry, CRect rect, LPCTSTR lpszSection = NULL);
void WriteColRef(LPCTSTR lpszEntry, COLORREF cr, LPCTSTR lpszSection = NULL);
bool WriteBinary(LPCTSTR lpszEntry, LPBYTE pData, UINT nBytes, LPCTSTR lpszSection = NULL);
void SerGetString(bool bGet, CString& s, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, LPCTSTR lpszDefault = NULL);
void SerGetDouble(bool bGet, double& f, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, double fDefault = 0.0);
void SerGetFloat(bool bGet, float& f, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, float fDefault = 0.0);
void SerGetInt(bool bGet, int& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, int nDefault = 0);
void SerGetDWORD(bool bGet, DWORD& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, DWORD nDefault = 0);
void SerGetBool(bool bGet, bool& b, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, bool bDefault = false);
void SerGetPoint(bool bGet, CPoint& pt, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CPoint ptDefault = CPoint(0,0));
void SerGetRect(bool bGet, CRect& rc, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CRect rectDefault = CRect(0,0,0,0));
void SerGetColRef(bool bGet, COLORREF& cr, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, COLORREF crDefault = RGB(128,128,128));
void SerGet(bool bGet, CString& s, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, LPCTSTR lpszDefault = NULL);
void SerGet(bool bGet, double& f, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, double fDefault = 0.0);
void SerGet(bool bGet, float& f, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, float fDefault = 0.0F);
void SerGet(bool bGet, int& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, int nDefault = 0);
void SerGet(bool bGet, short& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, int nDefault = 0);
void SerGet(bool bGet, DWORD& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, DWORD nDefault = 0);
void SerGet(bool bGet, WORD& n, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, DWORD nDefault = 0);
void SerGet(bool bGet, CPoint& pt, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CPoint ptDefault = CPoint(0,0));
void SerGet(bool bGet, CRect& rc, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CRect rectDefault = CRect(0,0,0,0));
void SerGet(bool bGet, CString* s, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, LPCTSTR lpszDefault = NULL);
void SerGet(bool bGet, double* f, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, double fDefault = 0.0);
void SerGet(bool bGet, float* f, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, float fDefault = 0.0F);
void SerGet(bool bGet, BYTE* n, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, BYTE nDefault = 0);
void SerGet(bool bGet, int* n, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, int nDefault = 0);
void SerGet(bool bGet, short* n, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, int nDefault = 0);
void SerGet(bool bGet, DWORD* n, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, DWORD nDefault = 0);
void SerGet(bool bGet, WORD* n, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, DWORD nDefault = 0);
void SerGet(bool bGet, CPoint* pt, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CPoint ptDefault = CPoint(0,0));
void SerGet(bool bGet, CRect* rc, int nCount, LPCTSTR lpszEntry, LPCTSTR lpszSection = NULL, CRect rectDefault = CRect(0,0,0,0));
int Parse(const CString&, int nOffset, CString &rstrOut);
void DeleteKey(LPCTSTR lpszKey);
private:
void Init(LPCTSTR lpszIniFile, LPCTSTR lpszSection = NULL);
LPTSTR GetLPCSTR(LPCTSTR lpszEntry, LPCTSTR lpszSection, LPCTSTR lpszDefault);
bool m_bModulPath; //true: Filenames without path take the Modulepath
//false: Filenames without path take the CurrentDirectory
#define MAX_INI_BUFFER 256
TCHAR m_chBuffer[MAX_INI_BUFFER];
CString m_strFileName;
CString m_strSection;
static CString Read(LPCTSTR lpszFileName, LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszDefault);
static void Write(LPCTSTR lpszFileName, LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue);
};
ini.cpp (partial, key methods)
The implementation provides reading/writing for various types using Windows API functions GetPrivateProfileString and WritePrivateProfileString. Below are selected methods demonstrating the pattern:
// Read a string from the INI file
CString CIni::GetString(LPCTSTR lpszEntry, LPCTSTR lpszDefault, LPCTSTR lpszSection)
{
if (lpszDefault == NULL)
return GetLPCSTR(lpszEntry, lpszSection, _T(""));
else
return GetLPCSTR(lpszEntry, lpszSection, lpszDefault);
}
// Write an integer value
void CIni::WriteInt(LPCTSTR lpszEntry, int n, LPCTSTR lpszSection)
{
if (lpszSection != NULL)
m_strSection = lpszSection;
TCHAR szBuffer[MAX_PATH];
_itot(n, szBuffer, 10);
WritePrivateProfileString(m_strSection, lpszEntry, szBuffer, m_strFileName);
}
// Read binary data
bool CIni::GetBinary(LPCTSTR lpszEntry, BYTE** ppData, UINT* pBytes, LPCTSTR pszSection)
{
*ppData = NULL;
*pBytes = 0;
CString str = GetString(lpszEntry, NULL, pszSection);
if (str.IsEmpty())
return false;
ASSERT(str.GetLength() % 2 == 0);
INT_PTR nLen = str.GetLength();
*pBytes = UINT(nLen) / 2;
*ppData = new BYTE[*pBytes];
for (int i = 0; i < nLen; i += 2)
{
(*ppData)[i / 2] = (BYTE)(((str[i + 1] - 'A') << 4) + (str[i] - 'A'));
}
return true;
}
// Write binary data
bool CIni::WriteBinary(LPCTSTR lpszEntry, LPBYTE pData, UINT nBytes, LPCTSTR pszSection)
{
LPTSTR lpsz = new TCHAR[nBytes * 2 + 1];
UINT i;
for (i = 0; i < nBytes; i++)
{
lpsz[i * 2] = (TCHAR)((pData[i] & 0x0F) + 'A'); // low nibble
lpsz[i * 2 + 1] = (TCHAR)(((pData[i] >> 4) & 0x0F) + 'A'); // high nibble
}
lpsz[i * 2] = 0;
WriteString(lpszEntry, lpsz, pszSection);
delete[] lpsz;
return true;
}
StringConversion.h (UTF-8 helper)
#pragma once
#include <Windows.h>
#include <atlenc.h>
int utf8towc(LPCSTR pcUtf8, UINT uUtf8Size, LPWSTR pwc, UINT uWideCharSize)
{
// Implementation converts UTF-8 to wide char
// (omitted for brevity)
}
CString OptUtf8ToStr(const CStringA& rastr)
{
CStringW wstr;
int iMaxWideStrLen = rastr.GetLength();
LPWSTR pwsz = wstr.GetBuffer(iMaxWideStrLen);
int iWideChars = utf8towc(rastr, rastr.GetLength(), pwsz, iMaxWideStrLen);
if (iWideChars <= 0)
{
wstr.ReleaseBuffer(0);
wstr = rastr; // convert with local codepage
}
else
wstr.ReleaseBuffer(iWideChars);
return wstr;
}
CStringA wc2utf8(const CStringW& rwstr)
{
CStringA strUTF8;
int iChars = AtlUnicodeToUTF8(rwstr, rwstr.GetLength(), NULL, 0);
if (iChars > 0)
{
LPSTR pszUTF8 = strUTF8.GetBuffer(iChars);
AtlUnicodeToUTF8(rwstr, rwstr.GetLength(), pszUTF8, iChars);
strUTF8.ReleaseBuffer(iChars);
}
return strUTF8;
}
CStringA StrToUtf8(const CString& rstr)
{
return wc2utf8(rstr);
}
Usage Example (MFC Dialog)
void CConfigDlg::OnReadIni()
{
CIni config;
BYTE* data;
UINT size;
config.GetBinary(_T("entry-1"), &data, &size, _T("root section"));
data[size] = NULL;
::MessageBoxA(NULL, (char*)data, _T("Binary Data"), 0);
}
void CConfigDlg::OnWriteIni()
{
CIni config;
char msg[255];
sprintf(msg, "Hello World");
config.WriteBinary(_T("entry-1"), (LPBYTE)msg, (UINT)strlen(msg), _T("root section"));
config.WriteInt(_T("entry-2"), 123456, _T("root section"));
}

The class handles path resolutino (module diretcory or current directory), supports reading/writing strings, integers, floats, doubles, booleans, points, rectangles, colors, and binary data via serialization functions. The SerGet pattern allows symmetric read/write operations based on a boolean flag.