Source code for C_INIFile
Download
The source files for this module are listed below. You can also download the module C_INIFile as a zip archive; this archive contains all the source files and documentation.
Description
CINIFile and helper classes allow you to use INI files without using the GetProvateProfileString, WriteProvateProfileString and related API functions, and instead read and write the whole INI file in one go. This results in much faster read and write operations when you use multiple individual settings, especially when virus scanners inspect each and every INI file read and write.
Information
This source code also uses our CHandle class. You can download this module at our website.
When you use the GetProvateProfileString and WriteProvateProfileString API functions, you will cause a lot of disk reads and writes because the actual INI file has to be read (and written) for each separate setting. To improve disk access, it is a better idea to buffer all reads and writes and work with the in-memory buffer instead. You can achieve this to some degree using the GetPrivateProfileSection and WritePrivateProfileSection API functions, but these functions require you to use your own INI section parsing routines. The CINIFile however reads and writes an INI file in one go. It allows you to perform all INI setting actions from an in-memory buffer.
The CINIFile class allows you to query for and access individual sections of the INI file. If you want to create sections (or make sure a section exists), specify bCreateIfMissing as true on the call to GetSection. If the section wasn't present before, an empty section will be created first and it will be returned.
CINISection in turn allows you to access individual settings in that section. Here you can also specify to create the setting if it is missing, allowing you to create new settings on the fly. CINISection also allows you to retrieve the entire section in one string using the Serialize method; the result is a string you can use to mimic the Get/WritePrivateProfileSection API functions.
CINISetting finally allows you to retrieve the actual settings in the INI file. It has different get/set methods to read/write the setting in different formats. The Get methods also allow you to specify a default to use if the settings was missing, just like the API functions do.
Notes / todo's
- The original lay-out and section/settings order of the INI file is not purposely retained when it is written out again, and might be totally different compared to the original order.
- Comments in the read INI file are not accessible nor retained when you write the INI file again.
Files
Each file belonging to this source code module is listed below.
INIFile.h
/*******************************************************************************
Version: 2
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: http://www.twologs.com/en/resources/sourcecode.asp
This code is freely distributable, as long as this comment remains intact.
If you find this source useful, you may use this code in your own projects
free of charge, but some acknowledgement to the author of this code is always
appreciated :)
The source is however distributed 'as is' without waranty and/or support, and
may not be fit for each and every application. Use it at your own discretion
and at your own risk.
The source already has undergone testing. This doesn't mean however that all
bugs are removed from this piece of code. If you find one of them, please
contact me about it. I can however not guarantee when and if the bug will be
fixed.
More information about this module can be found in the accompanying HTML file.
*******************************************************************************/
#ifndef INCLUDE_TWOLOGS_COMMON_INIFILE_H
#define INCLUDE_TWOLOGS_COMMON_INIFILE_H
#include <windows.h>
#include <string>
#include <map>
// Single setting
class CINISetting {
public:
// Con- & destructor
CINISetting(LPCSTR sName);
CINISetting(const std::string& sName);
virtual ~CINISetting();
// The name
std::string GetName() const;
// Gets the value
std::string GetText(LPCSTR sDefault = "") const;
std::string GetText(const std::string& sDefault) const;
bool GetBool(bool bDefault = false) const;
unsigned long GetUnsigned(unsigned long nDefault = 0) const;
long GetSigned(long nDefault = 0) const;
unsigned long long GetUnsigned64Bit(unsigned long long nDefault = 0) const;
long long GetSigned64Bit(long long nDefault = 0) const;
double GetDouble(double nDefault = 0) const;
// Sets the value
void SetText(LPCSTR sValue);
void SetText(const std::string& sValue);
void SetUnsigned(unsigned long nValue);
void SetSigned(long nValue);
void SetUnsigned64Bit(unsigned long long nValue);
void SetSigned64Bit(long long nValue);
void SetBool(bool bValue);
void SetDouble(double nValue);
private:
// The name
std::string m_sName;
// The value
std::string m_sValue;
};
// Section serialization styles
enum EINISectionSerStyle {
g_ceINISectSerStyleAPI, // Windows API style (NULL char separators)
g_ceINISectSerStyleText, // Textual style (newline separator)
g_ceINISectSerStyleTextWithHeader, // Textual style with section header in brackets
};
// Section of settings
class CINISection {
public:
// Con- & destructor
CINISection(LPCSTR sName);
virtual ~CINISection();
// Gets our name
std::string GetName() const;
// Whether the given setting is present
bool HasSetting(LPCSTR sName) const;
bool HasSetting(const std::string& sName) const;
// Gets the given setting
CINISetting* GetSetting(LPCSTR sName, bool bCreateIfMissing = true);
CINISetting* GetSetting(const std::string& sName, bool bCreateIfMissing = true);
// Clears the entire section
void Clear();
// Serializes the section
std::string Serialize(EINISectionSerStyle eStyle);
private:
// Our name
std::string m_sName;
// All our settings by name
typedef std::map<std::string, CINISetting*> CSettingsByName;
CSettingsByName m_apoSettingsByName;
// Searches for the given setting
CSettingsByName::iterator SearchSetting(const std::string& sSearchableName);
CSettingsByName::const_iterator SearchSetting(const std::string& sSearchableName) const;
};
// Entire INI file
class CINIFile {
public:
// Con- & destructor
CINIFile();
virtual ~CINIFile();
// Reads from the given INI file
bool Read(LPCSTR sFilePath);
// Writes to the last used or the given INI file
bool Write(LPCSTR sFilePath = NULL);
// The file currently in use
std::string FilePath() const;
// Whether the given section is present
bool HasSection(LPCSTR sName) const;
bool HasSection(const std::string& sName) const;
// Gets the given section
CINISection* GetSection(LPCSTR sName, bool bCreateIfMissing = true);
CINISection* GetSection(const std::string& sName, bool bCreateIfMissing = true);
private:
// The file/path of the INI file to use
std::string m_sFilePath;
// All our sections
typedef std::map<std::string, CINISection*> CSectionsByName;
CSectionsByName m_apoSectionsByName;
// Searches for the given section
CSectionsByName::iterator SearchSection(const std::string& sSearchableName);
CSectionsByName::const_iterator SearchSection(const std::string& sSearchableName) const;
};
#endif // INCLUDE_TWOLOGS_COMMON_INIFILE_H
INIFile.cpp
#include "INIFile.h"
#include "..\C_Handle\Handle.h"
#include <list>
#include <cstdio>
// Trims the given string
void Trim(std::string& sText) {
DWORD nStartPos = sText.find_first_not_of(" \t");
DWORD nEndPos = sText.find_last_not_of(" \t");
if (nStartPos == sText.npos) {
sText = "";
} else {
sText = sText.substr(nStartPos, nEndPos - nStartPos + 1);
}
}
// Splits the given string into separate lines
typedef std::list<std::string> CStringList;
CStringList SplitString(const std::string& sString) {
// Start at the start of the string
CStringList asLines;
DWORD nStartLine = 0;
bool bLastLine;
do {
// Get the end of the next line
DWORD nEndLine = sString.find_first_of("\r\n", nStartLine);
bLastLine = nEndLine == sString.npos;
if (bLastLine) {
// There isn't -> take all chars to the end of the string
nEndLine = sString.size() + 1;
}
// Extract the next line
std::string sNextLine = sString.substr(nStartLine, nEndLine - nStartLine);
// Look it it contains any data
Trim(sNextLine);
if (sNextLine.size() > 0) {
// Yes -> add it
asLines.push_back(sNextLine);
}
// And find the start of the next line
nStartLine = sString.find_first_not_of("\r\n", nEndLine);
if (nStartLine == sString.npos) {
// There is none -> we're done
bLastLine = true;
}
} while(!bLastLine);
// And return the found lines
return asLines;
}
// Transforms the given name to a searchable form
std::string MakeSearchable(LPCSTR sName) {
std::string sSearchable = sName;
strlwr(const_cast<char*>(sSearchable.c_str()));
return sSearchable;
}
// Con- & destructor
CINISetting::CINISetting(LPCSTR sName):
m_sName(sName),
m_sValue("") {
}
CINISetting::CINISetting(const std::string& sName):
m_sName(sName),
m_sValue("") {
}
CINISetting::~CINISetting() {
}
// The name
std::string CINISetting::GetName() const {
return m_sName;
}
// Gets the value
std::string CINISetting::GetText(LPCSTR sDefault) const {
std::string sResult = m_sValue;
if (m_sValue.size() == 0) {
sResult = sDefault;
}
return sResult;
}
std::string CINISetting::GetText(const std::string& sDefault) const {
return GetText(sDefault.c_str());
}
bool CINISetting::GetBool(bool bDefault) const {
bool bResult = bDefault;
if (m_sValue.size() > 0) {
switch (m_sValue.at(0)) {
case 't':
case 'T':
case 'y':
case 'Y':
case '1':
bResult = true;
break;
case 'f':
case 'F':
case 'n':
case 'N':
case '0':
bResult = false;
break;
}
}
return bResult;
}
unsigned long CINISetting::GetUnsigned(unsigned long nDefault) const {
unsigned long nResult = nDefault;
if (m_sValue.size() > 0)
nResult = strtoul(m_sValue.c_str(), NULL, 10);
return nResult;
}
long CINISetting::GetSigned(long nDefault) const {
long nResult = nDefault;
if (m_sValue.size() > 0)
nResult = atol(m_sValue.c_str());
return nResult;
}
unsigned long long CINISetting::GetUnsigned64Bit(unsigned long long nDefault) const {
unsigned long long nResult = nDefault;
if (m_sValue.size() > 0)
nResult = strtoull(m_sValue.c_str(), NULL, 10);
return nResult;
}
long long CINISetting::GetSigned64Bit(long long nDefault) const {
long long nResult = nDefault;
if (m_sValue.size() > 0)
nResult = strtoll(m_sValue.c_str(), NULL, 10);
return nResult;
}
double CINISetting::GetDouble(double nDefault) const {
double nResult = nDefault;
if (m_sValue.size() > 0)
nResult = atof(m_sValue.c_str());
return nResult;
}
// Sets the value
void CINISetting::SetText(LPCSTR sValue) {
m_sValue = sValue;
}
void CINISetting::SetText(const std::string& sValue) {
m_sValue = sValue;
}
void CINISetting::SetUnsigned(unsigned long nValue) {
char sValue[30];
_ultoa(nValue, sValue, 10);
m_sValue = sValue;
}
void CINISetting::SetSigned(long nValue) {
char sValue[30];
ltoa(nValue, sValue, 10);
m_sValue = sValue;
}
void CINISetting::SetUnsigned64Bit(unsigned long long nValue) {
char sValue[60];
ulltoa(nValue, sValue, 10);
m_sValue = sValue;
}
void CINISetting::SetSigned64Bit(long long nValue) {
char sValue[60];
lltoa(nValue, sValue, 10);
m_sValue = sValue;
}
void CINISetting::SetBool(bool bValue) {
m_sValue = bValue? "y": "n";
}
void CINISetting::SetDouble(double nValue) {
char sValue[128];
sprintf(sValue, "%.8f", nValue);
m_sValue = sValue;
}
// Con- & destructor
CINISection::CINISection(LPCSTR sName):
m_sName(sName) {
}
CINISection::~CINISection() {
Clear();
}
// Gets our name
std::string CINISection::GetName() const {
return m_sName;
}
// Whether the given setting is present
bool CINISection::HasSetting(LPCSTR sName) const {
std::string sSearchableName = MakeSearchable(sName);
CSettingsByName::const_iterator ppoFoundSetting = SearchSetting(sSearchableName);
return ppoFoundSetting != m_apoSettingsByName.end();
}
bool CINISection::HasSetting(const std::string& sName) const {
return HasSetting(sName.c_str());
}
// Gets the given setting
CINISetting* CINISection::GetSetting(LPCSTR sName, bool bCreateIfMissing) {
// Find the setting
std::string sSearchableName = MakeSearchable(sName);
CINISetting* poFoundSetting;
CSettingsByName::const_iterator ppoFoundSetting = SearchSetting(sSearchableName);
bool bSettingFound = ppoFoundSetting != m_apoSettingsByName.end();
if (bSettingFound) {
// Done -> return it
poFoundSetting = ppoFoundSetting->second;
} else {
// Not found -> look if we may create it
if (bCreateIfMissing) {
// Yes -> do so
poFoundSetting = new CINISetting(sName);
m_apoSettingsByName.insert(CSettingsByName::value_type(sSearchableName, poFoundSetting));
} else {
// No -> no setting
poFoundSetting = NULL;
}
}
// And return the found setting
return poFoundSetting;
}
// Gets the given setting
CINISetting* CINISection::GetSetting(const std::string& sName, bool bCreateIfMissing) {
return GetSetting(sName.c_str(), bCreateIfMissing);
}
// Clears the entire section
void CINISection::Clear() {
// Kill all our settings
CSettingsByName::iterator ppoNextSetting = m_apoSettingsByName.begin();
CSettingsByName::iterator ppoLastSetting = m_apoSettingsByName.end();
for (; ppoNextSetting != ppoLastSetting; ++ppoNextSetting) {
CINISetting* poNextSetting = ppoNextSetting->second;
delete poNextSetting;
}
m_apoSettingsByName.clear();
}
// Serializes the section
std::string CINISection::Serialize(EINISectionSerStyle eStyle) {
// Start with the section header, if needed
std::string sSerialized;
bool bSomeSerialized = false;
if (eStyle == g_ceINISectSerStyleTextWithHeader) {
sSerialized = "[";
sSerialized += m_sName;
sSerialized += "]";
bSomeSerialized = true;
}
// Add all settings
CSettingsByName::iterator ppoNextSetting = m_apoSettingsByName.begin();
CSettingsByName::iterator ppoLastSetting = m_apoSettingsByName.end();
for (; ppoNextSetting != ppoLastSetting; ++ppoNextSetting) {
// Add this setting
CINISetting* poNextSetting = ppoNextSetting->second;
if (bSomeSerialized) {
if (eStyle == g_ceINISectSerStyleAPI) {
sSerialized.append(1, '\0');
} else {
sSerialized += "\r\n";
}
}
sSerialized += poNextSetting->GetName();
sSerialized += "=";
sSerialized += poNextSetting->GetText();
bSomeSerialized = true;
}
// Finish off the serialisation, if needed
if (eStyle == g_ceINISectSerStyleAPI) {
sSerialized.append(1, '\0');
}
// And return the serialized content
return sSerialized;
}
// Searches for the given setting
CINISection::CSettingsByName::iterator CINISection::SearchSetting(const std::string& sSearchableName) {
return m_apoSettingsByName.find(sSearchableName);
}
CINISection::CSettingsByName::const_iterator CINISection::SearchSetting(const std::string& sSearchableName) const {
return m_apoSettingsByName.find(sSearchableName);
}
// Con- & destructor
CINIFile::CINIFile() {
}
CINIFile::~CINIFile() {
// Kill all our sections
CSectionsByName::iterator ppoNextSection = m_apoSectionsByName.begin();
CSectionsByName::iterator ppoLastSection = m_apoSectionsByName.end();
for (; ppoNextSection != ppoLastSection; ++ppoNextSection) {
CINISection* poNextSection = ppoNextSection->second;
delete poNextSection;
}
}
// Reads from the given INI file
bool CINIFile::Read(LPCSTR sFilePath) {
// Note which file we should use
m_sFilePath = sFilePath;
// Open the source INI file
CFileHandle hFile = CreateFile(
sFilePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
bool bSuccess = hFile.IsSet();
if (bSuccess) {
// Done -> look how big the INI is
DWORD nNumBytesToRead = GetFileSize(hFile, NULL);
bSuccess = nNumBytesToRead != INVALID_FILE_SIZE;
if (bSuccess) {
// Done -> read out the INI content
std::string sSerialized;
sSerialized.resize(nNumBytesToRead);
DWORD nNumBytesRead;
bSuccess = 0 != ReadFile(
hFile,
const_cast<char*>(sSerialized.c_str()),
nNumBytesToRead,
&nNumBytesRead,
NULL
);
if (bSuccess) {
// Done -> look if all of the content is read
bSuccess = nNumBytesToRead == nNumBytesToRead;
if (bSuccess) {
// Yes -> process each line
CINISection* poCurrentSection = NULL;
CStringList asLines = SplitString(sSerialized);
CStringList::iterator psNextLine = asLines.begin();
CStringList::iterator psLastLine = asLines.end();
for (; psNextLine != psLastLine; ++psNextLine) {
// Look what we have here
std::string sNextLine = *psNextLine;
if (sNextLine.size() > 2 && *sNextLine.begin() == '[' && *sNextLine.rbegin() == ']') {
// A section header -> extract the name of the section
std::string sName = sNextLine.substr(1, sNextLine.size() - 2);
Trim(sName);
// Look if this section is already present
std::string sSearchableName = MakeSearchable(sName.c_str());
CSectionsByName::iterator ppoFoundSection = SearchSection(sSearchableName);
if (ppoFoundSection != m_apoSectionsByName.end()) {
// Yes -> just recycle it
poCurrentSection = ppoFoundSection->second;
} else {
// No -> create it
poCurrentSection = new CINISection(sName.c_str());
m_apoSectionsByName.insert(CSectionsByName::value_type(sSearchableName, poCurrentSection));
}
} else if (sNextLine.size() > 0 && poCurrentSection != NULL) {
// A line with something else on it in a section -> look if it is a setting
DWORD nSettingSepPos = sNextLine.find('=');
if (nSettingSepPos != sNextLine.npos && nSettingSepPos > 0) {
// Yes -> extract it
std::string sName = sNextLine.substr(0, nSettingSepPos);
Trim(sName);
std::string sValue = sNextLine.substr(nSettingSepPos + 1);
Trim(sValue);
// And set the setting
poCurrentSection->GetSetting(sName, true)->SetText(sValue);
}
}
}
}
}
}
}
// And return if successfull
return bSuccess;
}
// Writes to the given INI file
bool CINIFile::Write(LPCSTR sFilePath) {
// Serialize all sections
bool bFirstSectionProcessed = false;
std::string sSerialized;
CSectionsByName::iterator ppoNextSection = m_apoSectionsByName.begin();
CSectionsByName::iterator ppoLastSection = m_apoSectionsByName.end();
for (; ppoNextSection != ppoLastSection; ++ppoNextSection) {
// Add this section
CINISection* poNextSection = ppoNextSection->second;
if (bFirstSectionProcessed) {
sSerialized += "\r\n\r\n";
}
sSerialized += poNextSection->Serialize(g_ceINISectSerStyleTextWithHeader);
bFirstSectionProcessed = true;
}
// Look which file to use
bool bWriteToOtherFile = sFilePath != NULL;
if (!bWriteToOtherFile) {
// The previously used one -> do so
sFilePath = m_sFilePath.c_str();
}
// Open the destination INI file
CFileHandle hFile = CreateFile(
sFilePath,
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
bool bSuccess = hFile.IsSet();
if (bSuccess) {
// Done -> write out the INI content
DWORD nNumBytesToWrite = sSerialized.size();
DWORD nNumBytesWritten;
bSuccess = 0 != WriteFile(
hFile,
sSerialized.c_str(),
nNumBytesToWrite,
&nNumBytesWritten,
NULL
);
if (bSuccess) {
// Done -> look if all of the content is written
bSuccess = nNumBytesToWrite == nNumBytesWritten;
if (bSuccess) {
// Done -> remember which file we used, if needed
if (bWriteToOtherFile) {
m_sFilePath = sFilePath;
}
}
}
}
// And return if successfull
return bSuccess;
}
// The file currently in use
std::string CINIFile::FilePath() const {
return m_sFilePath;
}
// Whether the given section is present
bool CINIFile::HasSection(LPCSTR sName) const {
std::string sSearchableName = MakeSearchable(sName);
CSectionsByName::const_iterator ppoFoundSection = SearchSection(sSearchableName);
return ppoFoundSection != m_apoSectionsByName.end();
}
bool CINIFile::HasSection(const std::string& sName) const {
return HasSection(sName.c_str());
}
// Gets the given section
CINISection* CINIFile::GetSection(LPCSTR sName, bool bCreateIfMissing) {
// Find the section
std::string sSearchableName = MakeSearchable(sName);
CINISection* poFoundSection;
CSectionsByName::const_iterator ppoFoundSection = SearchSection(sSearchableName);
bool bSectionFound = ppoFoundSection != m_apoSectionsByName.end();
if (bSectionFound) {
// Done -> return it
poFoundSection = ppoFoundSection->second;
} else {
// Not found -> look if we may create it
if (bCreateIfMissing) {
// Yes -> do so
poFoundSection = new CINISection(sName);
m_apoSectionsByName.insert(CSectionsByName::value_type(sSearchableName, poFoundSection));
} else {
// No -> no section
poFoundSection = NULL;
}
}
// And return the found section
return poFoundSection;
}
// Gets the given section
CINISection* CINIFile::GetSection(const std::string& sName, bool bCreateIfMissing) {
return GetSection(sName.c_str(), bCreateIfMissing);
}
// Searches for the given section
CINIFile::CSectionsByName::iterator CINIFile::SearchSection(const std::string& sSearchableName) {
return m_apoSectionsByName.find(sSearchableName);
}
CINIFile::CSectionsByName::const_iterator CINIFile::SearchSection(const std::string& sSearchableName) const {
return m_apoSectionsByName.find(sSearchableName);
}

Products
Overview
C_INIFile