Skip navigation links
IT services and product development
Menu
TwoLogs
IT services and product development

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.  When you have your INI content in a string buffer, then you can use CINIContent to handle the INI content the same way.

Information

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.

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: 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

INIFile.cpp

/*******************************************************************************

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