5

I want to store user preferences (colors,toolbars on/off, panel widths in pixels) and application settings (last 10 files, default save directory, default open directory) within my Delphi Win32 application. What is the best practice for doing this?

  • **Registry** (while settings do not become large binary objects, ofc :) – Premature Optimization Jul 30 '11 at 18:40
  • 1
    Registry sucks. But Ini files in the program files folder do too. Related question with good info: http://stackoverflow.com/questions/285277/where-to-store-program-settings-instead-of-hkey-local-machine – Warren P Jul 31 '11 at 02:22
  • @warren what's so bad about registry? It's one of the great features of windows that other systems lack. Big corp IT admin love the registry and hate the lack of anything comparable of Linux. hate the lack of anything comparable of Linux. – David Heffernan Jul 31 '11 at 08:05
  • 1
    @warren - Instead of pointing out what sucks and giving links to articles that use depreciated windows methods how about telling us what you do? – Michael Riley - AKA Gunny Jul 31 '11 at 12:36
  • 5
    I prefer INI files in the user's profile folder. I find that apps that use the registry become harder to support and understand, harder to backup while preserving state, harder to move to a new PC remaining intact, harder to upgrade, and harder to test. A pure text-configuration based (Ini or xml) application configuration is superior in the field, and the lack of anything like a registry on any platform but windows is in fact the biggest bad thing about the registry. It's a sloppy windows-ism that needs to die. – Warren P Aug 01 '11 at 13:23

5 Answers5

11

You have two main options:

  1. Store the settings in a file under the user profile. If your settings are simple enough then INI files work perfectly adequately.
  2. Store the settings in the registry, under HKEY_CURRENT_USER which is also part of the profile.

Personally I prefer to use the registry since it provides hierarchical storage for free. If you use a file, then you have to do that yourself which can be a bind for more complex data.

On the other hand, if you want to write a portable app, i.e. one that can live on a memory stick, then a user settings file that sits alongside the executable is the way to go.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 2
    Personally I prefer always write a portable app, and don't store anything in the registry. Delphi is just great to create no-setup applications: copy and run, from whatever location you want... It's of course a matter of personal taste, but I've seen commonly users/customers thinking about this feature as a real nice/distinctive one. About UAC and profile, even some applications now tends to put the executable within the user data, e.g. Google Chrome. ;) – Arnaud Bouchez Jul 30 '11 at 16:05
  • @arnaud there are lots of different options and different solutions are best for different situations. My app typically runs in corporate settings and thus registry is a good solution. – David Heffernan Jul 30 '11 at 16:11
  • In addition to INI files, you could also use JSON or XML format, which can be hierarchical, so could map better a master/detail information or a preference tree - I found out that preferences organized as a tree could make more sense than a list. A list is a particular kind of tree, whereas the contrary is not true. Delphi 2010 handle both JSON and XML, and you have plenty of third-party libraries around. – Arnaud Bouchez Jul 30 '11 at 16:12
  • About registry - this is a matter of taste, and does also depend on the situation. We both agree. – Arnaud Bouchez Jul 30 '11 at 16:13
  • @Arnaud: agreed. A program that can be installed with a simple XCOPY is to be preferred, IMO. -- I love the Mac for that: an .app is in fact an entire directory in one file, and you can immediately use it. All resources are simply files in the directory. You can write to the subdirectories like to any other filesystem directory. To install an app, you simply copy it to your harddisk, e.g. to the user's Applications directory. – Rudy Velthuis Jul 30 '11 at 17:27
  • Hierarchical, yes. Non-portable, hidden, and a giant source of accidental complexity in Windows. Get users in there modifying your configuration for your app and they could cripple their systems. The registry is just swell. It's where I would put things if I was a malware author trying to make your life harder. – Warren P May 26 '12 at 14:46
4

As @David points out, you can use the registry, files, or -- naturally -- a combination of those.

If you go for files, you have to store them in the current user's part of the file system. Indeed, it is a fundamental principle that one user should not affect any other user of the system, and Windows enforces this (for instance, you cannot save files in the Program Files directory when running without elevated privileges1).

For instance, my AlgoSim software could store its settings in the

C:\Users\Andreas Rejbrand\AppData\Roaming\Rejbrand\AlgoSim\2.0

folder. This is a typical example. You get the first part of the directory, that is,

C:\Users\Andreas Rejbrand\AppData\Roaming

by asking the operating system for the per-user app data folder. You can use the SHGetKnownFolderPath function to find this. Use the FOLDERID_RoamingAppData folder ID. If you need to support older versions of Windows, you can use SHGetFolderPath instead, and use the CSIDL_APPDATA constant.

The rest of the path normally follows the pattern

Manufacturer Name\Product Name\Product Version

What files to store? Well, the simplest way is to use old-fashioned INI files, but you can also use XML, plain-text files in your own format, or even binary files of your own design.

The second approach is to use the registry instead of files. This you probably already know how to do. If not, you'll learn that from examples easily. For instance, I could store my per-user settings in

HKEY_CURRENT_USER\Software\Rejbrand\AlgoSim\2.0

1 In this case, the operating system is smart enough to 'emulate' a per-user 'Program Files' folder. While the program thinks that it is reading from and writing to the Program Files folder, it is in fact reading from and writing to a folder in the current user's part of the file system. This way, old and badly behaved applications continue to work even in newer versions of the Microsoft Windows operating system, and, in addition, they begin to support per-user settings, which they should have done in the first place. I really think this is a major +1 to Microsoft, as I might have said before.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Why you are so focused on non-roaming application data? *Settings* are to be *roamed*. – Premature Optimization Jul 30 '11 at 18:45
  • @Downvoter: Ignorance, probably. I have no experience with corporate networks and computing. – Andreas Rejbrand Jul 30 '11 at 18:47
  • i do not know what exactly you are storing there, but `local app data` is for machine specific part of user profile, eg: data which can be recreated during normal operation and not worthwhile to roam with network profile. Something like that. – Premature Optimization Jul 30 '11 at 20:18
  • @Down... - What exactly is non-roaming application data? – Michael Riley - AKA Gunny Jul 30 '11 at 20:33
  • @Andreas Unless you have some strong reason otherwise, you should store settings to the roaming part of the user profile. Otherwise hotdesking employees will not have their settings follow them from machine to machine. – David Heffernan Jul 30 '11 at 21:58
  • @Cape: huge cache files, perhaps? – Andreas Rejbrand Jul 30 '11 at 22:16
  • @Cape Cod Gunny, you should ask separate question, for current one my strongest opinion is registry. Even portable apologetes will not convince me otherwise. ¿@Andreas Rejbrand, i think CSIDL is enough, as actual path varies per host system. – Premature Optimization Jul 30 '11 at 23:41
  • 1
    And thank you both, Downvoter and @David, for making me learn something new. In fact, I feel somewhat 'ashamed' that I haven't really been 'roaming aware' until now. I guess I have to rewrite some of my apps slightly... – Andreas Rejbrand Jul 31 '11 at 11:46
3

INI files always work good for me. There's TIniFile, so you can easily read/write the files without much work, you can assign default values to non-existant values and the INI file is in a human-readable format so the user can have a look at it and edit it if he wants.

Other solutions like storing values in the registry are possible, too, but often OS-dependant and a bit more complicated. I'd use such if the user shouldn't be able to see/edit the values, but the application settings you mentioned aren't much of a "secret".

schnaader
  • 49,103
  • 10
  • 104
  • 136
  • 2
    When you use ini files or anything outside the registry, it is important to use the correct folder: a folder, preferably a subfolder of the application data folder, in the **user's profile**. It is important because this way you will be compatible with roaming profiles and UAC. – Marjan Venema Jul 30 '11 at 15:08
  • @Marjan UAC is always a concern, you're perfectly right. Some programs even tend to install the executable even in the data folder, for instance I've notice that Google chrome executable in in the application data folder, not in the program files folder... I've also done this for some projects, and from the Customer POV it was worth it. I know this breaks the UAC design, but IMHO this design was broken since the beginning... what a trolling subject! ;) – Arnaud Bouchez Jul 30 '11 at 16:09
  • @arnaud chrome approach is hopeless in a multi user setting because each user needs their own installation. Hardly a good idea. – David Heffernan Jul 30 '11 at 19:38
2

I avoid storing anything in the Registry whenever possible. My apps are easily moved to another machine, by just copying a file from the user's CSIDL_APPDATA folder.

...and I'll suggest a component that helps: TrsStorage library from www.deepsoftware.ru. Besides providing easy persistence methods for a wide range of data types (including both small and large data types--strings, ints, floats, streams, buffers, etc.), they have a component you can drop onto a form, which accesses the published properties of all components on the form...you just check mark the individual properties you want to persist. No, it doesn't work for everything--like your "last 10 files" requirement. But it should work for your colors, toolbars on/off, panel widths, default save directory, default open directory, etc.

TrsStorage has lots of manual data storage & traversal methods as well. I've used those to implement a sort of inheritance tree for report settings--making it easy for "descendant" reports to store settings which override "ancestor" settings such as the default report typeface & font size. (The settings mimic the object hierarchy of my report types.) You could manually save your "last 10 files" list using one of these manual method calls; in fact, there are ReadText/WriteText procedures that should work for storing/retrieving your "last 10 files" list if you keep it in a TStrings object.

TrsStorage reads/writes from/to your choice of data file type: .INI, .XML, or its own .BIN format. And events are available for intercepting the write to/read from the file, and diverting the stream elsewhere: we use that sometimes for, say, storing per-record settings in the BLOB field of a database table.

Finally, I have no connection with these folks, but I've successfully used their TrsStorage components for years.

Mark Wilsdorf
  • 751
  • 1
  • 5
  • 18
0

If it is a database app (uses database) then store user preferences into database too. I prefer to have an BLOB in users table where I store users's preferences in INI file format. The main reason (using one BLOB per user) is that using proper normalization would likely slow down app's startup. One small caveat is that VCL's TIniFile doesn't support loading content from TStream, you have to use some thirdparty class or roll your own to avoid saving the data temporarily on disk.

ain
  • 22,394
  • 3
  • 54
  • 74
  • TMemIniFile can stream with TStream and it's better than TIniFile too. – David Heffernan Jul 30 '11 at 15:13
  • @David AFAIK not directly, but via SetStrings... so you have to use tmp StringList to load data from stream. IOW it's a hack :) – ain Jul 30 '11 at 15:24
  • 1
    According to n-Tier pattern, the "main" database is not IMHO the best location to store UI preferences, Last-Used lists, and such. A local file on the user computer could also make sense. About TIniFile (or TMemIniFile) it's not an issue to copy the BLOB content temporary on a file. – Arnaud Bouchez Jul 30 '11 at 16:02
  • 2
    Where would you store the database connection information? In the database too!? :-) – Warren P Jul 31 '11 at 02:19
  • @Warren My programs do have default connection string built in with the DB server name like "myAppDBserver" where "myApp" is of course applications "nickname". Each customer just has to define "myAppDBServ" in their DNS and it works, no need to configure each client machine manually. And of course each program can take command line parameter to override the default connection string should it be nessesary. – ain Jul 31 '11 at 16:01
  • Interesting idea. Even though you can redirect using DNS or the hosts file in some places, in other places, Hard-coding like that would get you in trouble. – Warren P Aug 01 '11 at 14:56
  • @Warren That's where the command line parameter comes into play - create shortcut with connection string as parameter and youre done. This system has been working for me for years. The advantage is that nothing has to be / saved / configured in client machine, except shortcut to exe (which itself might be on network drive ie all users share it). – ain Aug 01 '11 at 15:03
  • I like the command line parameter idea + database. I do like the effort you put into portability. – Warren P Aug 01 '11 at 23:18