CIni, Ini-file IO with a minimum of codelines

Environment: VC6 SP4, NT4 SP6,W98 SE, W2000 SP1

You know it takes lots of code lines when you use the API for storing data into an Ini-File. E.g. for just one double it would look like this:
// store
CString strTemp;
// load
char pBuffer[256];
m_dDouble = atof(pBuffer);

With this CIni class, I tried to reduce the coding to a minimum. I usually use one function for reading and writing. (E.g. For these members of CMyClass)

int     m_nMember;
double  m_dMember;
CString m_strMember;
CPoint  m_arPoints[2];

The code for the ini file IO could be reduced to this:

void CMyClass::UpdateFromIni( bGet /*=TRUE*/ )
  CIni ini("C:\\MyPath\\MyIni.ini", "MySection" );
  ini.SER_GET( bGet, m_nMember );
  ini.SER_GETD( bGet, m_dMember, 1.23 );
  ini.SER_GET( bGet, m_strMember );
  ini.SER_ARR( bGet, m_arPoints, 2 );

The output will look like this:


It supports overloaded functions for these standard types:

  • CString
  • short
  • int
  • WORD
  • CPoint
  • CRect
  • float
  • double

and arrays of them:

  • CString*
  • short*
  • int*
  • WORD*
  • DWORD*
  • CPoint*
  • CRect*
  • float*
  • double*

Each function has the following parameters:

void CIni::SerGet(
  BOOL bGet,            // TRUE: Load from Ini, 
                        // FALSE: Save to Ini
  LPCSTR szEntry,       // The Entry
  TYPE& type            // The type from the list above
//int nCount            // only the array versions: 
                        // count of array elements to load/save
  LPCSTR szSection=NULL // An other value than NULL will effect 
                        // all following entries
  TYPE default=0);      // 0 for numeric Types, "" for Strings

And to avoid coding the entry twice, there are 4 macros:

#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) SerGet(bGet,value,#value,
#define SER_ARRD(bGet,value,n) SerGet(bGet,value,n,#value,

So my function typicaly contains a combination of this.

void CMyClass::UpdateFromIni( bGet /*=TRUE*/ )
  CIni ini( "" , "MyFirstSection" );//Default file name

  ini.SER_GETD( bGet, m_nMember, 42);
  ini.SER_GET( bGet, m_dMember );


  ini.SER_GET( bGet, m_strMeber );
  ini.SER_GETD( bGet, m_strOther, "Hello" );
  ini.SER_ARR( bGet, m_arPoints, 2 );

Using just a filename without a path, normaly causes the OS to store the Ini-file in the Windows directory. To avoid this, the modul path will be insert in front of the filename by default.


Download demo project - 17 Kb
Download source - 5 Kb
The latest versions may be found at:


  • how to delete section

    Posted by Legacy on 03/19/2003 12:00am

    Originally posted by: Hardy


    Would you like to tell how to delete the whole section?

    Thanks a lot!


  • Great

    Posted by Legacy on 04/24/2002 12:00am

    Originally posted by: vladimir

    It is very easy to use and implement.
    Thanks !!!

  • problem with windows98

    Posted by Legacy on 03/22/2002 12:00am

    Originally posted by: Dian Suharto

    I am trying to create init file to keep data info. I know, it is better to use registry, but with init file you can put init file together with data in one distribute file.

    The problem is: it is run well on XP, 2000, but windows98 (second edition).

    In windows98, when we read from init file, using GetPrivateProfileString() or GetPrivateProfileInt(), the value is somehow delayed. I must keep trying to read it (sometime up to 1 minute) until I got the correct value.
    But if I open the same directory where the init file reside, and refresh the directory, the correct value will come after refreshing the directory.

    Again, in 2000 and XP the program run well without any delay.

    Does anybody find similar problem?

    Please help.
    Thank you.
    Best regards, Dian Suharto.

  • want to use as database file

    Posted by Legacy on 10/29/2001 12:00am

    Originally posted by: Anuj

    How can I use it as the database file. I want to add 'n' no of lines. Is it possible if yes how ?

  • Comments / bug report?

    Posted by Legacy on 10/14/2001 12:00am

    Originally posted by: cpp

    1) Bug? In the function:

    /*static*/ void CIni::AddModulPath(CString& strFileName,BOOL bModulPath /*= TRUE*/)
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char fname[_MAX_FNAME];
    char ext[_MAX_EXT];

    _splitpath( strFileName, drive, dir, fname, ext );
    if( ! drive[0] )
    //PathCanonicalize(..) doesn't work with for all Plattforms !
    CString strModule;
    if( bModulPath )
    _splitpath( strModule, drive, dir, fname, ext );
    strModule = drive;
    strModule+= dir;
    strModule+= strFileName;
    strFileName = strModule;

    _splitpath removes the last subdirectory and puts it in fname,
    because GetCurrentDirectory returns the path without a "\" at the end.
    This problem can be solved by appending "\" before calling _splitpath, e.g. by
    strModule += "\\";
    to the else statement (like the author has already done in the GetDefaultIniFile method).
    Is this the reason why it "doesn't work with for all Plattforms"?

    2) I suggest to save CRects in the ini file with the parameters in the same order as in
    the CRect constructor CRect( int left, int top, int right, int bottom ).

    3) A general comment/suggestion: it is a good idea to prefix calls to global functions with "::".
    This makes it easier for other programmers (and for yourself in a few months)
    to read the code, and it prevents the compiler from calling a class method with the same name.

  •, but...

    Posted by Legacy on 10/04/2001 12:00am

    Originally posted by: Jeff Schiller

    I didn't even know about pre-processor stringification, so I'm grateful for this article for showing me that.

    However, I was confused until I looked up and understood what stringification is all about. Then I looked at your source. Your source is ok, but I think your article needs a correction :

    #define SER_GET(bGet,value) SerGet(bGet,value,#value)

    void CIni::SerGet(BOOL bGet,
    LPCSTR szEntry,
    TYPE& type,

    - the macro is correct, but the CIni::SerGet declaration here has the value and the entry arguments swapped (as I said, this is correct in code, the article just needs to be corrected).

    Jeff Schiller

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Anthony Christie, the Chief Marketing Officer for Level Communications, is responsible for customer experience, worldwide marketing and product management. In this informative asset, he shares his insights into why a private network connection to cloud-bases applications is the right decision for your enterprise. Download now to find out more.

  • Complex hybrid environments can make it difficult to track interdependencies, increasing the risk of disrupting critical business services. In this white paper by EMA, you'll learn how application discovery and dependency mapping can help you: Meet granular targets for availability, cost, and time-to-revenue for cloud services. Accelerate mean time to repair (MTTR) while communicating better with stakeholders. Manage even the most complex hybrid environments more efficiently and effectively Understand the …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date