I have a fairly large C++ application (on Windows, no other platforms planned), which currently stores all settings (even some kind of addresses) in the Windows registry.
Sometimes this is inconvenient, because the users have difficulties changing entries in the registry. I would like to have settings versioned, so settings always match the current code. At the moment we version reg-files, but you are never sure, if all reg-files have been added on the target machines. With C# you can define default values in app.config, but don't overwrite existing settings. I don't know, if such a mechanism or library exists for C++.
I would like to have the following "features":
Settings can be versioned
Simple update on target machines (can be done by user)
Ensure that on update only new settings are added and no existing settings are overwritten with default values
Simple change of settings for user
Same workflow under Win XP and Win 7
As far as I see it, there are 3 possibilities to store settings on Windows:
Registry
Ini file
XML file
Only one application of our suite uses Qt at the moment, but Boost would be available.
For addresses, we will put them in some kind of XML address book, but for the other settings we are not sure, what's the best practise.
As comments mention, tree-based key/value structures are a common solution and libraries are easy to find.
Boost's property_tree is an excellent choice, as it is well-tested and can easily be exported as XML or JSON
Regarding your requirements:
Settings can be versioned
Yes! Make "version" a top-level key. Make it easily comparable with other versions.
You can also categorize your settings into various tree nodes and give each node a version.
Simple update on target machines (can be done by user)
Have your application do that when it runs. See below.
Ensure that on update only new settings are added and no existing settings are overwritten with default values
Simple change of settings for user
Same workflow under Win XP and Win 7
As settings change from one version to another, usually these changes fall into three categories. New properties are needed, old settings are abandoned, and some settings change their expected format. E.g. "32 Fahrenheit" becomes "0 Celsius"
When your application initializes:
Load the existing configuration file, regardless of its version.
If the version does not match what's current for the application:
Create a new blank property tree for the new configuration
For each node in the tree, have a set of expected property names, and a function pointer or similar to get this setting if it's absent in the old file's tree. If a setting changes its format, give it a new name.
Search for each setting in the old tree, copying it if it's found, and using the result of the supplied function if it's not.
Save your new settings file.
Your "missing setting" functions can:
Return a constant default value.
Query the tree for a different setting and convert it (with a default value if the old setting isn't found either)
Ask the user
Related
Recently I was trying to put a SQLite database into a QT 5 application I'm writing. I want it to be universally accessible - that is on all systems regardless of where it's installed. I put it as a resource then found out that evidently you can't put databases in resources as the string for the database path passed to setDatabaseName doesn't get translated to the resource system so the database can't be found.
So where can I put it? I don't want to just put it at the root of the drive like C:\repo.db or D:\repo.db as many people hate files cluttering their root directories (like me). I was going to put it just in the source folder and access it as "repo.db" or as I tried "./resources/database/repo.db" but even QFile doesn't see that. Where can I put it and how to access it there? My settings file was going to be in my resources but I wasn't sure if I could update the file then. I need a place that is available from the moment the application is installed on any system including my own so that it can be accessed both while coding it and when it's built.
I'm not asking for opinions - I want a place that is not in the root, somewhere universal like the installation directory (but how do I find that with code?) or a settings directory (but how do I set that somewhere so I can find it later??)
For such purposes Qt provides a list of QStandardPaths functions that return platform specific standard paths, such as a path to desktop, temp directory etc.
For your particular case you might put your database in the directory that corresponds to the QStandardPaths::AppDataLocation key.
You can use QSettings to save path,settings and restore them.
QSettings m_Arhive("Company", "app_name");
//Set DB path
m_Arhive.setValue("DBPath", "c:/somewhere/database");
//Get DB path
m_Arhive.value("DBPath").toString()
I have following scenario:
The main software I wrote uses a database created by a simulator. This database is around 10 GB big at the moment, so I want to keep only one copy of that data per system.
Assuming I have following projects:
Main Software using the data, located at /SimData
DLL using the data for debugging, searching for data at /SimData
Debugging tool to parse the image database, searching for the data at /SimData
Since I do not want to have all those programs have their own copy of SimData (not only to decrease place used, but also to ensure that all Simulation data used is always up to date for all programs).
I created for the DLL and Debugging Utility a link named SimData to MainSoftware/SimData, but when opening a file with "SimData\MyFile.data" it cannot find it, only the MainSoftware with the ACTUAL SimData folder can find it.
How can I use the MainSoftware/SimData folder without setting absolute paths?
This is on Windows 7 x64
I agree with Peter about adding the DB location as a configurable parameter. A common place to store that is in the registry.
however, If you want to create links that will be recognized by your software, try hardlinks. . fsutil should do the trick as described here.
You need a way to configure the database location. You could use an INI or other configuration file, or a registry setting, or a command-line input, or an environment variable. Or You could write your program to search a directory hierarchy... for example, if the various modules are usually siblings of each other in your directory tree, you could search for SimData/MyFile.data, ../SimData/MyFile.data, ../../MainSoftware/SimData/Myfile.data, and use the first one found.
Which answer is the "right one" depends on your situation.
I have several (15 or so) builds which all reference the same string of text in their respective build process templates. Every 90 days that text expires and needs to be updated in each of the templates. Is there a way to create a central variable or argument
One solution would be to create an environment variable on your build machine. Then reference the variable in all of your builds. When you needed to update the value you would only have to set it in one place.
How to: Use Environment Variables in a Build
If you have more than one build machine then it could become too much of a maintenance issue.
Another solution would involve using MSBuild response files. You create an .rsp file that holds the property value and the value would be picked up and set from MSBuild via the command line.
You need to place it into somewhere where all your builds can access it, then customize your build process template to read from there (build definitions - as you know - do not have a mechanism to share data between defs).
Some examples would be a file checked into TFS, a file in a known location (file share), web page, web service, etc.
You could even make a custom activity that knew how to read it and output the result as an OutArgument (e.g. Custom activity that read the string from a hardcoded URL).
In our project we got an application that uses an external configuration file (say server.xml). Now we need to design a setup tool GUI in C++/QT to read/edit such configuration file and it should be able to handle all the different versions of such file. The user will choose the file version and then proceed with the editing. From one version to another doesn't change too much, maybe there is a new xml tag, a tag with a different name or in a different position.
What's the best design approach to do so? We are planning to go for a standard MVC design pattern but how to deal with all the different configuration versions without rewriting the same GUI code again n again?
Here the sample config file:
<?xml version="1.0" encoding="utf-8"?>
<Server_configuration ver="11">
<core>
<enable-tms>true</enable-tms>
<enable-gui-messages>true</enable-gui-messages>
<waiting-for-config-timeout>10000</waiting-for-config-timeout>
<remoting>
<port>50000</port>
<join-timeout>5000</join-timeout>
<ismultithread>true</ismultithread>
<maxconcurrentrequests>20</maxconcurrentrequests>
</remoting>
</core>
<content>
<ftp>
<ip>192.168.0.227</ip>
<port>21</port>
<userid>******</userid>
<passwd>******</passwd>
</ftp>
<library>
<ip>192.168.0.227</ip>
<port>50023</port>
</library>
<local>
<asset-root>/assetroot</asset-root>
<kdm-expiration-warning>172800000</kdm-expiration-warning>
</local>
<hula-store-daemon>
<ip>127.0.0.1</ip>
<port>5567</port>
</hula-store-daemon>
</content>
</Server_configuration>
This is no means a drop in solution but I here are some things to do/consider. Every situation will differ.
Have an explicit version identifier in your config files. Fingerprinting them is a real (error prone) pain.
Consider having a tool that will update from version to version. It will be easier than reading old versions and trying to apply them.
I may be easier to do every version step individually but this can make the conversions less "lossless". A happy hybrid is to do minor updates from version to version but have "checkpoint" major upgrades that will jump right to the latest (or the latest "checkpoint"). This is kinda like incremental backups with full backup snapshots every once and a while.
Keep the user informed. A sysadmin won't be happy if you are changing his settings. You might want to make the process interactive or put comments into the file of every added/moved/removed setting. I would also recommend keeping removed settings in some section of the file for user reference. (Put a note why they are there as well).
Backup the old file. Your script will crash and it will eat data. Do something like naming the current file ${oldname}.old-${ver}~. Saving the settings in a different section of the file won't always be enough and this will save your users a lot of heartache.
Versioning should always be designed as robust and as simple as possible. It is crucial for you to determine whether each version of your application must be compatible with each version of the setup tool (which is rare), or whether you can, for example, meet your needs if any newer setup tool works with any same or older application, but not vice versa.
One way compatibility
One possibility to design for the latter is to add a version attribute to the XML file but try to keep it at the same fixed value forever by always only changing the structure and semantics of the XML file in backward compatible ways. For example, adding an element is backward compatible as long as the setup tool can interpret its absence the same way both the old setup tool and the application would behave. It does not hurt that the new setup tool always writes an (equivalent) value to the new element, because two-way compatibility with the old application is not required.
Once the day comes when you cannot maintain backward compatibility on input, you just change the value of the version attribute and start special casing it in the setup tool.
If you validate the XML against an XSD, notice that XSD can actually do one frequently useful thing for you: assign default attribute values. This way, your setup tool's source code may not even actually notice that the underlying document was missing a recently added attribute!
Two way compatibility
Strict versioning is needed. A schema definition (XSD, RelayNG,...) should be defined for each version of the XML file and the file should be validated against it both when it is read by the setup tool, written by the setup tool, or read by the application. The schema definition may be identical for several consecutive versions, if the interpretation of the same XML has changed, so when in doubt, always increase the version number.
Do what you can educating everyone that they cannot just edit the latest schema and do away with that. Unreliable versioning is worse than no versioning.
I am thinking about adding configurable settings to an application, and I think the easiest ways are an external file or win registry (its a win only app).
Which way would be better?
I was wondering, an user with not enough permissions may not be able to create/write the config file. And in the case of the registry, would todays antivirus allow me to add/edit/remove keys? Or they only monitor certain keys?
Also, if someone knows a class/lib to manage config settings (in pure win32) in vc++ please post it.
As far as I know:
an user with not enough permissions may not be able to create/write the config file
You should be able to make files inside user's "home directory" or "application data" directory, regardless of permissions. Normally those directories should be writeable.
would todays antivirus allow me to add/edit/remove keys?
Haven't ever seen my antivirus interfere with registry manipulation. You probably will be fine as long as you aren't doing anything suspicious in registry.
Which way would be better?
It is matter of taste. I think that text file is better - allows easier migration of settings. Just don't leave junk behind after uninstall.
Also, if someone knows a class/lib to manage config settings in vc++
QSettings in Qt 4. But using entire Qt for just saving settings is definitely an overkill. You could also check configuration languages like JSON, use lua for settings (less overkill than using Qt 4) or get any XML library. Also, working with registry directly or writing configuration files using iostreams or stdio shouldn't be hard. And you can always write your own configuration library - if you feel like it.
Is "Windows-only" a restriction or a restriction-relief? If you don't mind being cross-platform then I suggest you give boost::program_options a go. The library supports program options through commandline, through evironment-variables and through INI files. Boost's program_options also integrates and glues the various parsers very nicely with variables_map, which you can view as a map between options and their value.
For simple stuff, you might as well just use the registry. However, there are many benefits to a config file... you can save/load several different configs for different uses of your app, it's easier to share or migrate settings between users or machines, etc.
If you end up going the file route, I would recommend Boost's Property Tree library:
http://www.boost.org/doc/libs/1_41_0/doc/html/property_tree.html
It has a pretty nice syntax:
boost::property_tree::ptree properties;
std::string name = properties.get<std::string>("blah.name");
int score = properties.get<int>("blah.score");
properties.put("blah.name", "Inverse");
properties.put("blah.score", 1000);
It also supports reading and writing to various formats, like xml and others.
I think the new default thing is to write a configuration file in the user's "AppData" folder under the user's folder which should be safe to write/read from.
We're using a simple XML formatted file to store settings; but you could use a INI file type formatting.
If you save your configuration file in the Application Data directory SHGetFolderPath() with CSIDL_COMMON_APPDATA all users will be able to see the configuration. If you use CSIDL_LOCAL_APPDATA then only the one user will be able to see the configuration. The registry is not necessarily the place to save all configuration data.