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.
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. When you have your INI content in a string buffer, then you can use CINIContent to handle the INI content the same way.
This source code also uses our C_Handle and C_Unicode modules. You can download these modules 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 copy the content of an INI file into a CINIContent object. You can in turn use the CINIContent object 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. You can also retrieve all sections seperately in turn by using the GetSection method.
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.
Each file belonging to this source code module is listed below.
/*******************************************************************************
Version: 6
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
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 <vector>
#include <map>
#include "../C_Unicode/Unicode.h"
// Single setting
class TINISetting {
public:
// Con- & destructor
TINISetting(LPCWSTR name);
TINISetting(const std::wstring& name);
virtual ~TINISetting();
// The name
std::wstring GetName() const;
// Gets the value
std::wstring GetText(LPCWSTR defValue = L"") const;
std::wstring GetText(const std::wstring& defValue) const;
bool GetBool(bool defValue = false) const;
unsigned long GetUnsigned(unsigned long defValue = 0) const;
template <typename TEnum>
TEnum GetEnum(TEnum defValue = TEnum(0)) {
return (TEnum)GetUnsigned((unsigned long)defValue);
}
long GetSigned(long defValue = 0) const;
unsigned long long GetUnsigned64Bit(unsigned long long defValue = 0) const;
long long GetSigned64Bit(long long defValue = 0) const;
double GetDouble(double defValue = 0) const;
// Sets the value
void SetText(LPCWSTR value);
void SetText(const std::wstring& value);
void SetUnsigned(unsigned long value);
template <typename TEnum>
void SetEnum(TEnum value) {
SetUnsigned((unsigned long) value);
}
void SetSigned(long value);
void SetUnsigned64Bit(unsigned long long value);
void SetSigned64Bit(long long value);
void SetBool(bool value);
void SetDouble(double value);
private:
// The name
std::wstring m_name;
// The value
std::wstring m_value;
};
// Section serialization styles
enum class TINISectionSerStyle {
api, // Windows API style (NULL char separators)
text, // Textual style (newline separator)
textWithHeader // Textual style with section header in brackets
};
// Section of settings
class TINISection {
public:
// Con- & destructor
TINISection(LPCWSTR name);
virtual ~TINISection();
// Gets our name
std::wstring GetName() const;
// Whether the given setting is present
bool HasSetting(LPCWSTR name) const;
bool HasSetting(const std::wstring& name) const;
// Gets the given setting
TINISetting* GetSetting(LPCWSTR name, bool createIfMissing = true);
TINISetting* GetSetting(const std::wstring& name, bool createIfMissing = true);
// Clears the entire section
void Clear();
// Serializes the section
std::wstring Serialize(TINISectionSerStyle style);
private:
// Our name
std::wstring m_name;
// All our settings by name
typedef std::map<std::wstring, TINISetting*> TSettingPtrsByName;
TSettingPtrsByName m_settingPtrsByName;
// Searches for the given setting
TSettingPtrsByName::iterator SearchSetting(const std::wstring& searchableName);
TSettingPtrsByName::const_iterator SearchSetting(const std::wstring& searchableName) const;
};
// INI content
class TINIContent {
public:
// Con- & destructor
TINIContent();
virtual ~TINIContent();
// Sets the INI content to the given string
void SetTo(const wchar_t* content);
void SetTo(const std::wstring& content);
// Gets the complete content as a string
std::wstring GetAll() const;
// The number of sections defined
size_t NumSections() const;
// Whether the given section is present
bool HasSection(LPCWSTR name) const;
bool HasSection(const std::wstring& name) const;
// Gets the given section
TINISection* GetSection(size_t sectionNr);
TINISection* GetSection(LPCWSTR name, bool createIfMissing = true);
TINISection* GetSection(const std::wstring& name, bool createIfMissing = true);
private:
// All our sections
typedef std::vector<TINISection*> TSectionPtrs;
TSectionPtrs m_sectionPtrs;
typedef std::map<std::wstring, TINISection*> TSectionPtrsByName;
TSectionPtrsByName m_sectionPtrsByName;
// Searches for the given section
TSectionPtrsByName::iterator SearchSection(const std::wstring& searchableName);
TSectionPtrsByName::const_iterator SearchSection(const std::wstring& searchableName) const;
};
// Entire INI file
class TINIFile {
public:
// Con- & destructor
TINIFile();
virtual ~TINIFile();
// Reads from the given INI file
// The format of the file is deducted from any found BOM, but you
// can specify the format in case no BOM is found
bool Read(LPCWSTR filePath, TUnicodeFormat format = TUnicodeFormat::ascii);
// Writes to the last used or the given INI file in the specified format
// Note that UTF8 INI files cannot be read by the rest of Windows
bool Write(LPCWSTR filePath = nullptr);
bool Write(TExternalUnicodeFormat format, LPCWSTR filePath = nullptr);
// The file currently in use
const std::wstring& FilePath() const;
// The format of the file currently in use
const TExternalUnicodeFormat& Format() const;
// Gets the content
TINIContent& Content();
const TINIContent& Content() const;
private:
// The file/path of the INI file to use
std::wstring m_filePath;
// The format of the INI file in use
TExternalUnicodeFormat m_format;
// Our content
TINIContent m_content;
};
#endif // INCLUDE_TWOLOGS_COMMON_INIFILE_H
/*******************************************************************************
Version: 6
Author: Carl Colijn, TwoLogs
Contact: c.colijn@twologs.com
Source: https://www.twologs.com/sourcecode
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.
*******************************************************************************/
#include "INIFile.h"
#include "../C_Handle/Handle.h"
#include <list>
#include <cstdio>
// Trims the given string
void Trim(std::wstring& text) {
DWORD startPos = text.find_first_not_of(L" \t");
DWORD endPos = text.find_last_not_of(L" \t");
if (startPos == text.npos) {
text = L"";
} else {
text = text.substr(startPos, endPos - startPos + 1);
}
}
// Splits the given string into separate lines
typedef std::list<std::wstring> TStringList;
TStringList SplitText(const std::wstring& text) {
// Start at the start of the string
TStringList lines;
DWORD startLinePos = 0;
bool isLastLine;
do {
// Get the end of the next line
DWORD endLinePos = text.find_first_of(L"\r\n", startLinePos);
isLastLine = endLinePos == text.npos;
if (isLastLine) {
// There isn't -> take all chars to the end of the string
endLinePos = text.size() + 1;
}
// Extract the next line
std::wstring nextLine = text.substr(startLinePos, endLinePos - startLinePos);
// Look it it contains any data
Trim(nextLine);
if (nextLine.size() > 0) {
// Yes -> add it
lines.push_back(nextLine);
}
// And find the start of the next line
startLinePos = text.find_first_not_of(L"\r\n", endLinePos);
if (startLinePos == text.npos) {
// There is none -> we're done
isLastLine = true;
}
} while(!isLastLine);
// And return the found lines
return lines;
}
// Transforms the given name to a searchable form
std::wstring MakeSearchable(LPCWSTR name) {
std::wstring searchableName = name;
wcslwr(const_cast<wchar_t*>(searchableName.c_str()));
return searchableName;
}
// Con- & destructor
TINISetting::TINISetting(LPCWSTR name):
m_name(name),
m_value(L"") {
}
TINISetting::TINISetting(const std::wstring& name):
m_name(name),
m_value(L"") {
}
TINISetting::~TINISetting() {
}
// The name
std::wstring TINISetting::GetName() const {
return m_name;
}
// Gets the value
std::wstring TINISetting::GetText(LPCWSTR defValue) const {
std::wstring result = m_value;
if (m_value.size() == 0) {
result = defValue;
}
return result;
}
std::wstring TINISetting::GetText(const std::wstring& defValue) const {
return GetText(defValue.c_str());
}
bool TINISetting::GetBool(bool defValue) const {
bool result = defValue;
if (m_value.size() > 0) {
switch (m_value.at(0)) {
case L't':
case L'T':
case L'y':
case L'Y':
case L'1':
result = true;
break;
case L'f':
case L'F':
case L'n':
case L'N':
case L'0':
result = false;
break;
}
}
return result;
}
unsigned long TINISetting::GetUnsigned(unsigned long defValue) const {
unsigned long result = defValue;
if (m_value.size() > 0)
result = wcstoul(m_value.c_str(), nullptr, 10);
return result;
}
long TINISetting::GetSigned(long defValue) const {
long result = defValue;
if (m_value.size() > 0)
result = _wtol(m_value.c_str());
return result;
}
unsigned long long TINISetting::GetUnsigned64Bit(unsigned long long defValue) const {
unsigned long long result = defValue;
if (m_value.size() > 0)
result = wcstoull(m_value.c_str(), nullptr, 10);
return result;
}
long long TINISetting::GetSigned64Bit(long long defValue) const {
long long result = defValue;
if (m_value.size() > 0)
result = wcstoll(m_value.c_str(), nullptr, 10);
return result;
}
double TINISetting::GetDouble(double defValue) const {
double result = defValue;
if (m_value.size() > 0) {
wchar_t* endPos;
result = wcstod(m_value.c_str(), &endPos);
}
return result;
}
// Sets the value
void TINISetting::SetText(LPCWSTR value) {
m_value = value;
}
void TINISetting::SetText(const std::wstring& value) {
m_value = value;
}
void TINISetting::SetUnsigned(unsigned long value) {
wchar_t valueText[30];
_ultow(value, valueText, 10);
m_value = valueText;
}
void TINISetting::SetSigned(long value) {
wchar_t valueText[30];
_ltow(value, valueText, 10);
m_value = valueText;
}
void TINISetting::SetUnsigned64Bit(unsigned long long value) {
wchar_t valueText[60];
ulltow(value, valueText, 10);
m_value = valueText;
}
void TINISetting::SetSigned64Bit(long long value) {
wchar_t valueText[60];
lltow(value, valueText, 10);
m_value = valueText;
}
void TINISetting::SetBool(bool value) {
m_value = value? L"y": L"n";
}
void TINISetting::SetDouble(double value) {
wchar_t valueText[128];
swprintf(valueText, L"%.8f", value);
m_value = valueText;
}
// Con- & destructor
TINISection::TINISection(LPCWSTR name):
m_name(name) {
}
TINISection::~TINISection() {
Clear();
}
// Gets our name
std::wstring TINISection::GetName() const {
return m_name;
}
// Whether the given setting is present
bool TINISection::HasSetting(LPCWSTR name) const {
std::wstring searchableName = MakeSearchable(name);
auto foundSettingPtrPtr = SearchSetting(searchableName);
return foundSettingPtrPtr != m_settingPtrsByName.end();
}
bool TINISection::HasSetting(const std::wstring& name) const {
return HasSetting(name.c_str());
}
// Gets the given setting
TINISetting* TINISection::GetSetting(LPCWSTR name, bool createIfMissing) {
// Find the setting
std::wstring searchableName = MakeSearchable(name);
TINISetting* foundSettingPtr;
auto foundSettingPtrPtr = SearchSetting(searchableName);
bool settingFound = foundSettingPtrPtr != m_settingPtrsByName.end();
if (settingFound) {
// Done -> return it
foundSettingPtr = foundSettingPtrPtr->second;
} else {
// Not found -> look if we may create it
if (createIfMissing) {
// Yes -> do so
foundSettingPtr = new TINISetting(name);
m_settingPtrsByName[searchableName] = foundSettingPtr;
} else {
// No -> no setting
foundSettingPtr = nullptr;
}
}
// And return the found setting
return foundSettingPtr;
}
// Gets the given setting
TINISetting* TINISection::GetSetting(const std::wstring& name, bool createIfMissing) {
return GetSetting(name.c_str(), createIfMissing);
}
// Clears the entire section
void TINISection::Clear() {
// Kill all our settings
for (auto nextSettingPtrPtr: m_settingPtrsByName) {
delete nextSettingPtrPtr.second;
}
m_settingPtrsByName.clear();
}
// Serializes the section
std::wstring TINISection::Serialize(TINISectionSerStyle style) {
// Start with the section header, if needed
std::wstring serialized;
bool someSerialized = false;
if (style == TINISectionSerStyle::textWithHeader) {
serialized = L"[";
serialized += m_name;
serialized += L"]";
someSerialized = true;
}
// Add all settings
for (auto nextSettingPtrPtr: m_settingPtrsByName) {
// Add this setting
TINISetting* nextSettingPtr = nextSettingPtrPtr.second;
if (someSerialized) {
if (style == TINISectionSerStyle::api) {
serialized.append(1, L'\0');
} else {
serialized += L"\r\n";
}
}
serialized += nextSettingPtr->GetName();
serialized += L"=";
serialized += nextSettingPtr->GetText();
someSerialized = true;
}
// Finish off the serialisation, if needed
if (style == TINISectionSerStyle::api) {
serialized.append(1, L'\0');
}
// And return the serialized content
return serialized;
}
// Searches for the given setting
TINISection::TSettingPtrsByName::iterator TINISection::SearchSetting(const std::wstring& searchableName) {
return m_settingPtrsByName.find(searchableName);
}
TINISection::TSettingPtrsByName::const_iterator TINISection::SearchSetting(const std::wstring& searchableName) const {
return m_settingPtrsByName.find(searchableName);
}
// Con- & destructor
TINIContent::TINIContent() {
}
TINIContent::~TINIContent() {
// Kill all our sections
for (TINISection* nextSectionPtr: m_sectionPtrs) {
delete nextSectionPtr;
}
}
// Sets the INI content to the given string
void TINIContent::SetTo(const std::wstring& content) {
SetTo(content.c_str());
}
void TINIContent::SetTo(const wchar_t* content) {
// Split the content into seperate lines
TStringList lines = SplitText(content);
// And process each line
TINISection* currentSectionPtr = nullptr;
for (const std::wstring& nextLine: lines) {
// Look what we have here
if (nextLine.size() > 2 && *nextLine.begin() == L'[' && *nextLine.rbegin() == L']') {
// A section header -> extract the name of the section
std::wstring name = nextLine.substr(1, nextLine.size() - 2);
Trim(name);
// Look if this section is already present
std::wstring searchableName = MakeSearchable(name.c_str());
auto foundSectionPtrPtr = SearchSection(searchableName);
if (foundSectionPtrPtr != m_sectionPtrsByName.end()) {
// Yes -> just recycle it
currentSectionPtr = foundSectionPtrPtr->second;
} else {
// No -> create it
currentSectionPtr = new TINISection(name.c_str());
m_sectionPtrsByName[searchableName] = currentSectionPtr;
m_sectionPtrs.push_back(currentSectionPtr);
}
} else if (nextLine.size() > 0 && currentSectionPtr != nullptr) {
// A line with something else on it in a section -> look if it is a setting
DWORD settingSepPos = nextLine.find(L'=');
if (settingSepPos != nextLine.npos && settingSepPos > 0) {
// Yes -> extract it
std::wstring name = nextLine.substr(0, settingSepPos);
Trim(name);
std::wstring value = nextLine.substr(settingSepPos + 1);
Trim(value);
// And set the setting
currentSectionPtr->GetSetting(name, true)->SetText(value);
}
}
}
}
// Gets the complete content as a string
std::wstring TINIContent::GetAll() const {
// Serialize all sections
bool firstSectionProcessed = false;
std::wstring serialized;
for (TINISection* nextSectionPtr: m_sectionPtrs) {
// Add this section
if (firstSectionProcessed) {
serialized += L"\r\n\r\n";
}
serialized += nextSectionPtr->Serialize(TINISectionSerStyle::textWithHeader);
firstSectionProcessed = true;
}
// And return the result
return serialized;
}
// The number of sections defined
size_t TINIContent::NumSections() const {
return m_sectionPtrs.size();
}
// Whether the given section is present
bool TINIContent::HasSection(LPCWSTR name) const {
std::wstring searchableName = MakeSearchable(name);
auto foundSectionPtrPtr = SearchSection(searchableName);
return foundSectionPtrPtr != m_sectionPtrsByName.end();
}
bool TINIContent::HasSection(const std::wstring& name) const {
return HasSection(name.c_str());
}
// Gets the given section
TINISection* TINIContent::GetSection(size_t sectionNr) {
TINISection* foundSectionPtr;
if (sectionNr < m_sectionPtrs.size()) {
foundSectionPtr = m_sectionPtrs[sectionNr];
} else {
foundSectionPtr = nullptr;
}
return foundSectionPtr;
}
// Gets the given section
TINISection* TINIContent::GetSection(LPCWSTR name, bool createIfMissing) {
// Find the section
std::wstring searchableName = MakeSearchable(name);
TINISection* foundSectionPtr;
auto foundSectionPtrPtr = SearchSection(searchableName);
bool sectionFound = foundSectionPtrPtr != m_sectionPtrsByName.end();
if (sectionFound) {
// Done -> return it
foundSectionPtr = foundSectionPtrPtr->second;
} else {
// Not found -> look if we may create it
if (createIfMissing) {
// Yes -> do so
foundSectionPtr = new TINISection(name);
m_sectionPtrsByName[searchableName] = foundSectionPtr;
m_sectionPtrs.push_back(foundSectionPtr);
} else {
// No -> no section
foundSectionPtr = nullptr;
}
}
// And return the found section
return foundSectionPtr;
}
// Gets the given section
TINISection* TINIContent::GetSection(const std::wstring& name, bool createIfMissing) {
return GetSection(name.c_str(), createIfMissing);
}
// Searches for the given section
TINIContent::TSectionPtrsByName::iterator TINIContent::SearchSection(const std::wstring& searchableName) {
return m_sectionPtrsByName.find(searchableName);
}
TINIContent::TSectionPtrsByName::const_iterator TINIContent::SearchSection(const std::wstring& searchableName) const {
return m_sectionPtrsByName.find(searchableName);
}
// Con- & destructor
TINIFile::TINIFile() {
}
TINIFile::~TINIFile() {
}
// Reads from the given INI file
// The format of the file is deducted from any found BOM, but you
// can specify the format in case no BOM is found
bool TINIFile::Read(LPCWSTR filePath, TUnicodeFormat format) {
// Note which file we should use
m_filePath = filePath;
// Open the source INI file
TFileHandle file = CreateFile(
filePath,
GENERIC_READ,
FILE_SHARE_READ,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
bool allOK = file.IsSet();
if (allOK) {
// Done -> look how big the INI is
DWORD numBytesToRead = GetFileSize(file, nullptr);
allOK = numBytesToRead != INVALID_FILE_SIZE;
if (allOK) {
// Done -> read out the INI content
std::string fileContentRaw;
fileContentRaw.resize(numBytesToRead);
DWORD numBytesRead;
allOK = 0 != ReadFile(
file,
const_cast<char*>(fileContentRaw.c_str()),
numBytesToRead,
&numBytesRead,
nullptr
);
if (allOK) {
// Done -> look if all of the content is read
allOK = numBytesRead == numBytesToRead;
if (allOK) {
// Yes -> look what type of content we have
m_format = GetExternalStringFormat(fileContentRaw, format);
// Decode it
TDecodeToUnicodeResult decodeResult = DecodeToUnicode(fileContentRaw, m_format);
allOK = decodeResult.allOK;
if (allOK) {
// Done -> set the content
m_content.SetTo(decodeResult.result);
}
}
}
}
}
// And return if successful
return allOK;
}
// Writes to the last used or the given INI file in the specified format
bool TINIFile::Write(LPCWSTR filePath) {
return Write(m_format, filePath);
}
bool TINIFile::Write(TExternalUnicodeFormat format, LPCWSTR filePath) {
// Look which file to use
bool writeToOtherFile = filePath != nullptr;
if (!writeToOtherFile) {
// The previously used one -> do so
filePath = m_filePath.c_str();
}
// Open the destination INI file
TFileHandle file = CreateFile(
filePath,
GENERIC_WRITE,
0,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
bool allOK = file.IsSet();
if (allOK) {
// Done -> get the INI content to write
std::wstring iniContent = m_content.GetAll();
// Possibly encode the INI content before writing it
TEncodeFromUnicodeResult encodeResult = EncodeFromUnicode(iniContent, format);
allOK = encodeResult.allOK && !encodeResult.unknownCharsReplaced;
if (allOK) {
// Done -> write out the INI content
DWORD numBytesToWrite = encodeResult.resultRaw.size();
DWORD numBytesWritten;
allOK = 0 != WriteFile(
file,
encodeResult.resultRaw.c_str(),
numBytesToWrite,
&numBytesWritten,
nullptr
);
if (allOK) {
// Done -> look if all of the content is written
allOK = numBytesToWrite == numBytesWritten;
if (allOK) {
// Done -> remember which file we used, if needed
if (writeToOtherFile) {
m_filePath = filePath;
}
}
}
}
}
// And return if successful
return allOK;
}
// The file currently in use
const std::wstring& TINIFile::FilePath() const {
return m_filePath;
}
// The format of the file currently in use
const TExternalUnicodeFormat& TINIFile::Format() const {
return m_format;
}
// Gets the content
TINIContent& TINIFile::Content() {
return m_content;
}
const TINIContent& TINIFile::Content() const {
return m_content;
}