First M$ wanted us to store all of our settings in an INI file. That didn’t work well!
Then M$ wanted us to store all of our settings in the Registry. That REALLY didn’t work well!
Well now they want us to store them in a Config.XML file. I know what your thinking, isn’t that just another INI file?
I recently had a site that had UAC cranked up to the “Tin Foil Paranoid” level, and we were having trouble getting registry entries for the database connection setup. So I created a couple of functions so that I could read and write configuration information into a Config.XML file.
So this week we will take a look at the code in these two functions and a few uses for them.
For the most part I use my open source default manager class to store all the settings, defaults, and other configuration information for an application in the database. However the one thing that definitely can’t be stored in the database is the connection information for the database. So I have been storing that information in the registry, and in fact the default manager supports storing information in the registry. However with the introduction of UAC (Universal Access Control), otherwise known as the bane of existence to windows developers around the world, adding entries to the registry has become tricky, depending on the UAC settings.
In a recent project that is installed in hospitals and laboratories, we ran into a issue where we were unable to reliable store the connection information in the registry. It is typical in environments like hospitals for UAC to be cranked up to its highest settings, and sometimes even have additional security policies set via the network domain.
So after much research, otherwise known as Googling, I discovered that the latest and greatest answer from our friend in Redmond is to use a configuration file called config.XML that resides in the same directory as the executable. I won’t get into the whole how is that different than INI files from the old days of windows, I am sure there is a team of folks at Microsoft that can do that. I will say that at least XML has some structure and since WX is very good at working with XML creating a config.XML file isn’t a challenge. So on to the code.
The first example we are going to look at is a WebDev project that isn’t using my Default Manager class and is there for using a standalone version of the functions.
The first thing to talk about is current directory. WX provides us a nice function fCurrentDir and this works fine except that in WebDev when running in AWP mode, it returns the directory where the AWP engine is installed, not the current directory of the application. Since I use one project file for my projects, even when they have both windows and web components, they share a common project code. So in AWP mode I have to manually set the current directory instead of using the fCurrentDir function. So my project code ends up looking something like this.
Notice the use of the InAWPMode function, as well as the InTestMode() function so that I can set the path differently on my development machine versus the production machine as well. There are several of these functions that tell you what mode you application is running in, and they become very useful with you use a single project file like I do.
I created a Read and a Write function to read and write information from the config.XML file. Let’s look at the code that uses those functions first then we will look at the code of the actual functions.
In this example, I have some default code that sets the connection to the localhost and uses trusted mode to connect if the config.XML file does not exists, as just a fall back in case I don’t have the file for some reason.
At line 16 you will see the first use of the function XMLConfigRead. The function accepts two parameters, the name of the setting being stored, and an optional parameter that provides a default value if the setting doesn’t currently exists.
Note: the functions are using the global variable CurrentDir for the current directory information. It’s bad programming form I know, but I was being lazy, since this was a “one off use”. The methods in the default manager are more advanced and do not use global variables.
Notice on Line 20 that the password is encrypted in the XML file, we don’t want to store the password in plain sight!
Although not shown here the XMLConfigWrite function takes the exact same two parameters, the setting being stored and its value. Of course when writing the value is not optional. In this particular project I just needed to get the settings, its not something that we needed an interface to mantain, so I just manually created the config.XML file. The advantage being if anything changes in our connection, all we have to do is modify the config.XML file, instead of making programming changes.
And here is an example of what the XML file looks like
So lets look at the two functions. We will look at the write function first.
Line 3 loads the existing config.XML file into a string value.
If the file doesn’t exists or is empty, Lines 5 defines an XMLDocument. By defining an XMLDocument we are able to use some very powerful and easy to use XML functions of WX. We are storing all of the settings under a root node of “Config”, which is what Line 6 is creating.
Line 8 converts the string into an XMLDocument if the Config.XML file has current settings in it.
Line 9 uses one of those WX functions to write an entry in the XML file, using the passed in parameters.
The XML functions are only writing to an XMLDocument that is in memory. So Line 13 actually saves it back to disk. And then Line 14 closes the XMLDocument, removing it from memory.
Now lets take a look at the XMLReadConfig function.
Again Line 4 loads any existing config.XML file.
If there isn’t an existing file and a default value was supplied, then Line 7 makes a call to the XMLConfigWrite function we just looked at to create the file and store the setting. Then Line 10 returns that default value.
If there is an existing file, Line 12 converts it to an XMLDocument.
If there is an existing file, Line 14 uses the WX XMLRead function to read the value from the XMLDocument.
Line 15 checks to see if there is no value in the XML file, and a default value is provide, then again Line 16 calls our XMLConfigWrite function to write the default value into the file.
Line 22 closes the XMLDocument
Line 23 returns the value to the function.
That is all there is to the two functions, and you can of course tweak them as you need. Before we go this week, we will take a quick look at the differences between using these standalone functions and using the Default Manager Class.
One of the advantages to the Default Manager Class is it can store a structure. So instead of making individual calls for each portion of the connection settings we can make one call and load the entire structure at once.
So we declare a structure
We then declare an instance of the structure. We also need to declare an empty version so we can pass it into the default manager call. This lets the default manager examine the structure, and create all the necessary entries.
The GetDefault call on line 61 is similar XMLReadConfig call we looked at early. In this case it loads all the values of the structure into the ldconn instance. From there you will see in lines 70 – 73 where we use the individual portions of the structure to set the connection parameters similar to what we did with the standalone version.
Uncle Pete’s Corner is webinar series on all things WX, to watch the watch the Uncle Pete’s corner webinar that is the companion to this article, and many other WX related webinars go to the WinDev US YouTube Channel and be sure to also join the WxLive – US Community on Google+ to get notices about upcoming webinars. On the Google+ community you will find a active group developers discussing all things WX.