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.

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.

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.

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.

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.

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.

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.

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.

I came across your code and it looks really useful. One thing I am not sure about is how to handle custom data structures. I cannot use STL or MFC or ATL classes. So I make use of the ZVector class http://www.codeproject.com/cpp/zvector.asp.

Is it possible to use your code without modification to save and load settings for FILE_INFORMATION objects. If so, please could you show me how to go about this. If I need to make modifications, could you please point to where I would need to make modifications.

I added support for generic collections some month ago, but I didn't update the files here.

I just sent the updated files to codeproject, they should be available soon. Look at the "SettingsSTD" example to see how you can add with a dozen of code lines, support for your ZVector and FILE_INFORMATION.

In a word, Yes you will be able to add support for your types with less efforts.

I dont know if the Pascal Hurni won this I will be happy to send them. For the moment I have make only what I need for my program - and that is the strings and char save - but it's very easy to make if also for number, and probably I do it.

However the _DEFAULT macros and Load() function do not behave as I've expected. If you set a default values to an string item with a _DEFAULT macro and load the item that does not have an entry in the setting file with Load() function, the item ends up with an empty string. I think in this case, you should keep the default value. Does this make sense?

Just tested with the new code and found an access violation when trying to load or save an ini file.
I'm afraid you probally broke the code. Please take a look again.
By the way, you'hv dnot updated the demo files.

I downloaded the Demo archive from CodeProject, and it really seems that it was updated with the new code.

Could you run the given demo project without any access violation ? Or is your own test program that crashes with my code ? BTW, the 3 files MSettings* have changed.

Note that the WTL demo project has un bug the path to the ini file at line 73 should have a preceding dot. (was missing) Here is the updated line:
Mortimer::CSettingsStorageIniFile Storage(_T(".\\..\\AppSettings.ini"), _T("Settings"));

To test the behaviour of the _DEFAULT macro, I changed the demo projet to have the field "CityName" a default value. Could you reproduce your problem with this changes ?

Well, for its two first implementations I made the support for Registry and Ini Files framework free. This let me use exactly the same code for MFC and ATL/WTL or even plain Win32.

I don't plan to write an implementation for MFC CWinApp at the moment. But, here are two ideas to implement such a class:

1. Implement the CSettingsStorageWinApp class the way it's documented (see DocHTML in SettingsStorage_Source.zip). This is done by implementing a bunch of SaveLoadItem() functions by using the CWinApp GetProfileXXX(), WriteProfileXXX().

2. Implement a wrapper (CSettingsStorageWinApp) that creates an instance of either CSettingsStorageRegistry or CSettingsStorageIniFile depending on CWinApp::m_lpszRegistryKey (when not NULL, use registry). And then delegates all SaveLoadItem() calls to the created instance SaveLoadItem().