A C++ INI File Reader/Writer Class Supporting Multiple Data Types

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"));
}

INI file screenshot

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.

Tags: C++ INI file configuration MFC data serialization

Posted on Thu, 21 May 2026 20:51:41 +0000 by Matt Parsons