I hate putting stuff in the registry. It's not portable, it just adds bulk that's only needed by my application, and it's problematic to backup in case you want to move the app to another machine or easily recover from a reformat/reinstall of Windows. However, every time I mention "put it in an INI file", all of the zealots here on CP complain the INI files are obsolete, and we should always use the registry. Yeah, right, blah blah blah.
I will however admit that INI files are limited in their functionality in a number of ways. They can't be bigger than 64k in size, and data items can be no longer than 256 characters. They can also be difficult to use if you want to do anything more than simply setting/getting data from them. Finally, Microsoft could, at any time, remove the API functions that support them. These are the primary reasons that I wrote the
The CProfile class
This class reads XML files that are formatted in a style similar to INI files. In other words, give the following INI file contents:
... the XML version supported by
CProfile would look like this:
Since the class is simply trying to emulate the functionality of INI files, all top-level nodes are considered to be evquivalant to INI sections, and each "section" contains one or more child nodes (the same thig as INI "keys"). Child nodes under the keys are ignored - remember, we're just emulating INI files, not an all-encompassing XML file reader. I kept this within the context of an INI file because that's all I need. If you want to make this all-encompassing, have at it, but don't ask me to do it for you. You're a programmer - make it fit your needs.
CProfile maintains a
CTypedPtrArray of profile items. Each profile item contains three CStrings - its section, its key name, and its value. Converting to and from the desired intrinsic type is handled in a series of overloaded Set/Get functions.
The class uses MSXML.DLL and imports the typelib. This way, I don't have to worry about any weird (and often unreliable) XML parsing classes that may only partially implement what's needed to properly parse a XML file. The only thing you have to remember to do is to call
CoInitialize() before you call the
CProfile::Load() function (and remember to call
CoUninitialize() before exiting your app). I just stick a call to
CoInitialize() at the top of my
InitInstance() function, and
CoUninitialize() in my
ExitInstance() function - nice and tidy.
CProfile is a simple matter of creating a variable somewhere in your application like so:
#include <span class="code-string">"Profile.h"</span>
...and then loading the file:
When you call
CProfile::Load(), you need to supply string that represents a file name. If the filename is blank or is not a complete path/filename, the class will attempt to "normalize" the filename in the following ways:
- If the filename is empty, the class assumes that it's supposed to be myapp.xml and located in the same folder as the executable.
- If the filename is just a path, the class assumes that it's supposed to be myapp.xml and verifies that the path you specified exists. If it doesn't exist, it sets the path to the path of the executable file.
- If the filename is a path with just an extension, the class will set the filename to the app's name.
In the end, an acceptable filename should always be the result. The file does not have to exist, but the path must.
After the profile is loaded, you can access the data. If you attempt to retrieve a value whose section/key does not yet exist, that section/key/value is added to the list of values maintained within
CProfile. If you try to set a section/key/value that doesn't exist, it will be automatically created and added to the list.
CProfile will automatically save its data when it goes out of scope. If you don't want this functionality, simply include this line anytime within the current scope.
The class will not save until either you explicitly call the
Save() function, or the
CProfile object goes out of scope (and auto-save is turned on).
Getting and Setting Values
By default, if you try to get or set a value for a key that doesn't exist,
CProfile will automatically create the key in the list. This duplicates the functionality of the Get/SetPrivateProfileString API for INI files. However, you can turn that functionality on and off for sets, gets, or both by calling the appropriate toggle function:
The default value is true for both toggles.
If you want to make sure that your xml file loaded as expected, there are two built-in functions in
CProfile that show the contents of the internal list -
ByRange" function allows you to view all of the items within the specified index range. If you don't pass any range info at all, it assumes you want to see all of the items. Normally this shouldn't be a problem, but just in case you have a billion keys, you can specify a smaller range of items to view.
BySection" function shows all keys for the specified section. Again, this shouldn't normally be a problem, but if you have over 25 keys in a given section, you should consider writing an overload of this function that also accepts a non-optional set of range parameters.
I've also included some code that supports filename manipulation. It is provided only to support
CProfile and discussion of that code is not within the context of this article. Feel free to talk amongst yourselves, but I won't be fielding any questions about it.
Disclaimers and Warranties
So there it is - nothing fancy and definitely not hard to use or understand. I'm sure there's something in this code that someone won't like, so instead of complaining about it, be a programmer and change it to suit your needs. Good luck.