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;
strTemp.Format("%g",m_dDouble);
WritePrivateProfileString("MySection",
                          "MyDouble",
                          strTemp,
                          "C:\\MyPath\\MyIni.ini");
// load
char pBuffer[256];
GetPrivateProfileString("MySection",
                        "MyDouble",
                        "1.23",
                        pBuffer,
                        256,
                        "C:\\MyPath\\MyIni.ini");
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:

[MySection]
m_nMember=0
m_nMember=1.23
m_strMember=
m_arPoints_0=(0,0)
m_arPoints_1=(0,0)

It supports overloaded functions for these standard types:

  • CString
  • short
  • int
  • WORD
  • DWORD
  • 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,
                                    NULL,default)
#define SER_ARRD(bGet,value,n) SerGet(bGet,value,n,#value,
                                      NULL,default)

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.SetSection("MyOtherSection");

  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.

Downloads

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


Comments

  • how to delete section

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

    Originally posted by: Hardy

    Hi,

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

    Thanks a lot!


    HardyCao

    Reply
  • Great

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

    Originally posted by: vladimir

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

    Reply
  • problem with windows98

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

    Originally posted by: Dian Suharto

    Hi,
    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.


    Reply
  • 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 ?

    Reply
  • 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 )
    GetModuleFileName(NULL,strModule.GetBuffer(MAX_INI_BUFFER),MAX_INI_BUFFER);
    else
    GetCurrentDirectory(MAX_INI_BUFFER,strModule.GetBuffer(MAX_INI_BUFFER));
    strModule.ReleaseBuffer();
    _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
    adding
    strModule.ReleaseBuffer();
    strModule.TrimRight('\\');
    strModule.TrimRight('/');
    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.

    Reply
  • Stringification..cool, 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 http://gcc.gnu.org/onlinedocs/cpp_3.html#SEC17 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

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

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Relying on outside companies to manage your network and server environments for your business and applications to meet the needs and demands of your users can be stressful. This is especially true as many Managed Hosting organizations fail to meet their service level agreements. Read this Forrester total economic impact report and learn what makes INetU different and how they exceed their customers' managed hosting expectations.

Most Popular Programming Stories

More for Developers

RSS Feeds