Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WTL
Article

Settings Storage - easy program settings handling

Rate me:
Please Sign up or sign in to vote.
4.67/5 (14 votes)
26 Apr 20056 min read 93.1K   2.1K   46   24
Handle your program settings easily with multiple storage schemes and for several frameworks.

Image 1

Introduction

Program settings is something that many of us have implemented. If I look at myself, I implemented this in various ways, depending on the framework (MFC, ATL, ...) and also on the data types to handle. One more element is where to put the settings? In the registry, in a file, as XML or in a database?

This article provides a single solution to handle program settings with little to no code, but taking into account the framework you work on and the storage scheme you desire.

Background

Generally, I create a class with data members (the settings) and a Load/Save pair of function. Here is what it looks like:

class CMySettings
{
public:
    bool Load(...);
    bool Save(...);

    CString WindowName;
    int Height;
    int Width;
    COLORREF BackColor;
};

Of course, the content of Load/Save has to be done by hand. For example, it could use the registry with RegCreateKey(), RegSetValue() and so on. When it comes to handling collections (arrays), the Load/Save functions receive more code with a loop for each collection. It becomes cumbersome.

In another program or even the same one but with a different settings class, I rewrite the Load/Save functions for the new data members. In fact, I copy/paste code from the previous functions and modify it a bit. It's exactly this process that I want no more. It's time consuming and error prone due to copy/paste.

Hunting for a settings class

Before re-inventing the wheel, I thought "someone out there surely already made a cool class". This started my hunting. I found classes here and there, some that did not more than the one described above, some others that were worse. And finally I found CRegSettings here at CodeProject.

CRegSettings is made by Magomed G. Abdurakhmanov. This is a really cool class. It uses the MAP mechanism like MFC or ATL maps (MSG, COM, DDX). The map is here to avoid writing the Load/Save functions. Exactly what I wanted.

Here is an example of a settings class:

class CMySettings : public CRegSettings
{
public:
    CString WindowName;
    int Height;
    int Width;
    COLORREF BackColor;

    BEGIN_REG_MAP(CMySettings)
        REG_ITEM(WindowName, "No Document")
        REG_ITEM_REQUIRE(Height)
        REG_ITEM_REQUIRE(Width)
        REG_ITEM(BackColor, 0xFF8800)
    END_REG_MAP()
};

Somewhere in the program code, you call the Load/Save functions. Example: m_Settings.Load();

You may ask: "Okay, what is this article for if you found the Grail?". Because registry is not the place I always want my settings to go to.

What's new?

The main idea of my system is to split the settings class into two classes. One which handles the data members and the load/save operations for all the fields, the second which handles single item reading and writing for a specific storage mechanism.

The goal is to have the settings class to be separated from the way items are stored. This lets you choose which storage mechanism to use at run time. With a small effort, you can also add new storage schemes to the system yourself. All the tedious work is already done, so you can concentrate on the real code.

How to simply use it?

I'll explain here the different steps to define a class that manages some settings.

Step 1

Derive your class from CSettings. Note that CSettings is encapsulated in the Mortimer namespace. Add data members that represent your settings.

Here is an example:

#include <MSettingsStorage.h>
using namespace Mortimer;

class CMySettings : public CSettings
{
public:
    CString WindowName;
    int Height;
    int Width;
    COLORREF BackColor;
    CDWordArray Measures;
};

Step 2

Add a map entry for each data member.

Each handled data type or group of data types provides three map macros, here are the macros for the simple types:

  • SETTING_ITEM(<data member name>)
  • SETTING_ITEM_REQUIRE(<data member name>)
  • SETTING_ITEM_DEFAULT(<data member name>, <expression for default value>)

The standard one simply takes the handled data member name as argument. If at CSettings::Load() time the item fails to read its data, the whole Load() process will quietly continue and could return success (if no required item generates an error).

The REQUIRE one changes this behaviour, as its name involves the item is required, so a failed read will return failure for the Load() process. Note that other items may be read depending on CSettingsStorage::ContinueOnError().

The DEFAULT one will act like the standard but when the item fails to read, the item data is replaced with the given default value.

When saving (CSettings::Save()) any of these macros may fail and will return the error status. Other items may be saved further depending on the value of the CSettingsStorage::ContinueOnError() function.

Here is the list of handled types for each macro.

Editor Note - Some words broken using hyphens to prevent scrolling
SETTING_ITEMAll integer types:
LONGLONG, ULONGLONG
long, unsigned long
int, unsigned int
short, unsigned short
char (signed), unsigned char (treated as numbers)
Simple types:
float, double, bool
Strings:
CString, std::string, std::wstring
SETTING_ITEM_SZZero terminated strings
SETTING_ITEM_BINARYAny data

SETTING_IT-EM_BINARYPTR

Any data behind a pointer
SETTING_ITEM_ARRAYCArray of CSettings subclasses
ATL CSimpleArray<>
MFC CArray<>
MFC CxxxArray
SETTING_ITEM_STLSTL collections of CSettings subclasses
std::vector
std::deque
std::list
SETTING_IT-EM_ALONE_ARRAYCArray of standalone types
ATL CSimpleArray<>
MFC CArray<>
MFC CxxxArray
SETTING_ITEM_ALONE_STLSTL collections of standalone types
std::vector
std::deque
std::list

Standalone types are all the types handled by SETTING_ITEM.

The example with the map:

class CMySettings : public CSettings
{
public:
    CString WindowName;
    int Height;
    int Width;
    COLORREF BackColor;
    CDWordArray Measures;

    BEGIN_SETTING_MAP(CMySettings)
        SETTING_ITEM_DEFAULT(WindowName, "MyApp")
        SETTING_ITEM_REQUIRE(Height)
        SETTING_ITEM_REQUIRE(Width)
        SETTING_ITEM(BackColor)
        SETTING_ITEM_ALONE_ARRAY(Measures, DWORD)
    END_SETTING_MAP()
};

Note the additional argument for SETTING_ITEM_ALONE_ARRAY, you have to pass the type of the elements.

Step 3

Now that you have your settings class defined, you have to load and save these settings. This is done somewhere in your code by calling the settings class Load() and Save() functions.

To be able to call these, you have to pass a CSettingsStorage object. This object makes the link with the underlying storage scheme.

Let's go with an example using the Registry for storage:

#include <MSettingsStorageRegistry.h>
using Mortimer::CSettingsStorageRegistry;

#define APP_REGISTRY_KEY "Software\\Company\\MyApp"

class CMyApp : public CSomeFrameworkApp
{
public:

    ...

    void OnInit()
    {
        CSettingsStorageRegistry Stg(HKEY_CURRENT_USER, APP_REGISTRY_KEY);
        m_MySettings.Load(Stg);
    }

    void OnChangeSettings()
    {
        CSettingsDialog Dlg;
        if (Dlg.DoModal() == IDOK)
        {
            CSettingsStorageRegistry Stg(HKEY_CURRENT_USER, APP_REGISTRY_KEY);
            m_MySettings.Save(Stg);
        }
    }

    ...

protected:
    CMySettings m_MySettings;

};

Any error handling has been omitted for clarity. Well, it's that simple. Nothing more is needed.

How to implement a new storage scheme?

I created three classes that manage storage schemes:

  • CSettingsStorageRegistry to handle the Registry.
  • CSettingsStorageIniFile to handle .ini files.
  • CSettingsStorageMSXMLFile to handle XML files (by using msxml.dll).

You can add your own storage schemes by deriving your new class from the abstract class CSettingsStorage and implementing the SaveLoadItem() functions for each handled type. For details, please refer to the documentation of this class. You may also investigate the supplied classes.

Conclusion

You should now be able to handle your settings more easily, even with your own types or collections. To get a real example, read the demo project, it shows you some advanced use. Three examples are present, one for the MFC framework, one for ATL/WTL and the last is a simple one for plain Win32 using STL.

Feel free to use and modify this piece of software. Suggestions and comments are also welcomed.

Stay tuned for a next article. I'll cover using this settings system tightened with dialogs to edit them, of course with minimum code.

History

  • 28 May 2004
    • Added class to handle XML: CSettingsStorageMSXML*.
  • 14 May 2004
    • Bug fixes.
    • CSettingsStorage reworked to add standard behaviour.

      Warning: There's a compatibility issue for INI files. The 'Count' value for collections is now stored under 'Root.KeyName'-'Count' instead of 'Root'-'KeyName.Count'.

    • SETTING_ITEM_ALONE_* macros added.

      Thanks to Mr. CoolVini for this idea.

  • 16 April 2004
    • Updated source code.
  • 5 April 2004
    • Article submitted.
  • 31 December 2003
    • First release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Switzerland Switzerland
Actually teaching in a technical school for IT professionals in Switzerland. Especially programming and software design.

Studdied electronic, computer science and then became IT engineer in 1997.

Started development around 1988 with C64 then Atari ST, Amiga, some early Linux and nowadays several frameworks on Windows.


Comments and Discussions

 
GeneralGood but not indented Pin
strader27-Nov-06 0:58
strader27-Nov-06 0:58 
GeneralRe: Good but not indented Pin
Pascal Hurni27-Nov-06 21:23
Pascal Hurni27-Nov-06 21:23 
GeneralRe: Good but not indented Pin
Daniel B.18-Apr-09 13:34
Daniel B.18-Apr-09 13:34 
GeneralSettingsMFC.exe crasch &quot;Load from XML File&quot; Pin
pussy127-Apr-05 20:41
pussy127-Apr-05 20:41 
GeneralRe: SettingsMFC.exe crasch &quot;Load from XML File&quot; Pin
Pascal Hurni28-Apr-05 1:49
Pascal Hurni28-Apr-05 1:49 
GeneralHandling custom data structures Pin
dynal20-Apr-05 2:59
dynal20-Apr-05 2:59 
GeneralRe: Handling custom data structures Pin
Pascal Hurni20-Apr-05 23:50
Pascal Hurni20-Apr-05 23:50 
GeneralYou are a god! Pin
Schniddel3-Jan-05 0:50
Schniddel3-Jan-05 0:50 
Hallo

I work with your Settings Storage and I have to say, that it is really excellent. It's amazing how good that work. I never had a problem with it and it worked from beginning on.

I'm really impressed about that damn good work!

greetz
Thorsten
Generalloack virtual destructors causing memory leaks in xml Pin
ewqeqeqeqeqe16-Oct-04 17:47
ewqeqeqeqeqe16-Oct-04 17:47 
GeneralRe: loack virtual destructors causing memory leaks in xml Pin
Pascal Hurni17-Oct-04 23:07
Pascal Hurni17-Oct-04 23:07 
Questionhow about saving as xml attributes Pin
ewqeqeqeqeqe15-Oct-04 16:11
ewqeqeqeqeqe15-Oct-04 16:11 
AnswerRe: how about saving as xml attributes Pin
Pascal Hurni15-Oct-04 23:26
Pascal Hurni15-Oct-04 23:26 
GeneralRe: how about saving as xml attributes Pin
ewqeqeqeqeqe16-Oct-04 14:03
ewqeqeqeqeqe16-Oct-04 14:03 
GeneralExtra options... Pin
CoolVini7-May-04 22:41
CoolVini7-May-04 22:41 
GeneralRe: Extra options... Pin
Pascal Hurni9-May-04 21:35
Pascal Hurni9-May-04 21:35 
GeneralVery good and practical code Pin
CoolVini7-May-04 9:19
CoolVini7-May-04 9:19 
Questionfor vc++6? Pin
kcp746-May-04 19:58
kcp746-May-04 19:58 
AnswerRe: for vc++6? Pin
Pascal Hurni6-May-04 20:52
Pascal Hurni6-May-04 20:52 
GeneralA piece of gem! And about the _DEFAULT macro behavior Pin
Dezhi Zhao14-Apr-04 8:34
Dezhi Zhao14-Apr-04 8:34 
GeneralRe: A piece of gem! And about the _DEFAULT macro behavior Pin
Pascal Hurni15-Apr-04 9:30
Pascal Hurni15-Apr-04 9:30 
GeneralRe: A piece of gem! And about the _DEFAULT macro behavior Pin
Dezhi Zhao3-May-04 11:19
Dezhi Zhao3-May-04 11:19 
GeneralRe: A piece of gem! And about the _DEFAULT macro behavior Pin
Pascal Hurni6-May-04 21:23
Pascal Hurni6-May-04 21:23 
GeneralExcellent! Pin
Hans Dietrich14-Apr-04 2:12
mentorHans Dietrich14-Apr-04 2:12 
GeneralRe: Excellent! Pin
Pascal Hurni15-Apr-04 9:42
Pascal Hurni15-Apr-04 9:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.