[CodeProject C#]Read/Write Config files

http://www.codeproject.com/csharp/ReadWriteXmlIni.asp

Contents

Introduction

I was developing a small Windows app using C# and I decided that it would be nice to save the window's position and size when the app exited so it would be the same the next time the app was run. Having come from the MFC world, I searched for something equivalent to GetProfile and WriteProfile methods of the CWinApp class, but found nothing. I knew there was a Registry class in the Microsoft.Win32 namespace, but there was nothing available for writing to INI files. I would need to use the Win32 APIs for those. If I wanted to use an XML file, there was a whole slew of classes available in the System.Xml namespace. I also looked into using App.config files which some people had mentioned in the forums, but those supposedly were not meant to be written to...

All these similar mechanisms, each with their own interfaces... which one to use? I just needed was a simple way to persist my window's position and size somewhere to be later retrieved.

I set my small Windows app project on the side and decided that it was time to write a new class library; one that would unify all these mechanisms into a common and simple interface: the IProfile interface. This article presents the IProfile interface and the four classes that implement it: Xml, Registry, Ini, and Config. These four classes allow reading and writing values to their respective mediums, via a common and simple interface. Enjoy!

Interface

So what does this common interface look like? Here's a simplified version of it -- see IProfile.cs or AMS.Profile.chm for the complete definition:

interface IProfile
{
  void SetValue(string section, string entry, object value);

  object GetValue(string section, string entry);
  string GetValue(string section, string entry, string defaultValue);
  int    GetValue(string section, string entry, int defaultValue);
  double GetValue(string section, string entry, double defaultValue);
  bool   GetValue(string section, string entry, bool defaultValue);

  string[] GetEntryNames(string section);
  string[] GetSectionNames ();

  void RemoveEntry(string section, string entry);
  void RemoveSection(string section);

  bool ReadOnly
  {
    get; 
    set;
  }    
  
  event ProfileChangingHandler Changing;
  event ProfileChangedHandler Changed;        
}

As you can see, it's a simple interface bearing a slight resemblance to the GetProfile and WriteProfile methods of the CWinApp class. It's all based on a simple paradigm: a piece of data is associated with an entry inside of a section. The section may contain multiple entries and there may be multiple sections available. That's it! A section could be something like "Main Window", holding entries such as "X", "Y", "Width", "Height", each one having their corresponding numeric values. If you've worked with INI files in the past, this concept will be very familiar to you.

Aside from the standard Get and Set methods used to read and write the values, the interface also allows you to retrieve the names of the available entries and sections, and to remove them if desired. It even has a ReadOnly property to prevent any changes to the profile. And to top it off, it contains two events that allow you to be notified when something in the profile (section, entry, or value) is about to change or has already changed. ( I may have gone a little overboard :-) )

Classes

When it came time to implement the interface I went with the most popular mediums available. XML was number one on the list. It's popularity is just impossible to ignore. The Registry was my second choice. Although people may be turning away from it, it's still a very efficient way to read and write data to a centralized location. Lastly, I went with INI files. I realize they're practially extinct these days but some people still use them due to their simplicity. Once I had completed my classes, I decided to add one more implementation to the list: one to handle config files. From the forums and articles here at CP, I noticed several developers needing a way to write to them. So I said, "Why not?" It's somewhat similar to the XML one so it didn't take long to write.

The result was four classes, all part of the AMS.Profile namespace: Xml, Registry, Ini, and Config. Their main objective is the implementation of IProfile based on their respective storage mediums.

So how do these classes store their Profile data? Here's a brief synopsis of how each class works:

Xml

As you probably know, XML is all about storing data inside a text file in pretty much any markup-based format. So which format would I choose to organize the data using the section/entry paradigm? After considering a couple of possibilities, I decided that the format below would be preferable, since it allows section and entry names to contain spaces. It also looks cleaner and more consistent than if I had used the section and entry names themselves to name the elements.

 
 

 
 
  
  
  
Some Value Another Value
True

Notice the root element is called "profile". This is the default root name, which you may change via the class's RootName property. When you check out the class, you'll also notice that the default name of the XML file is based on the name of the application -- program.exe.xml. This, of course, is also customizable.

Registry

For the registry I decided to default to the old familiar path, HKEY_CURRENT_USER/Software/Company/Product, with individual sections appearing as a subkeys of that. Again, this path is customizable when creating the object, or later via the RootKey and Name properties.

The above data would appear similar to this, when viewed on the Registry editor (regedit.exe):

- My Computer
  ...
  - HKEY_CURRENT_USER
    ...
    - Software
      ...
      - AMS
        - ProfileDemo
          - A Section          Name            Data
                               An Entry        Some Value
                               Another Entry   Another Value

          - Another Section    Name            Data
                               This is cool    True

Ini

INI files are pretty much self explanatory, format-wise. Here's the above data in INI format:

[A Section]
An Entry=Some Value
Another Entry=Another Value
  
[Another Section]
This is cool=True

Like the Xml file, the default file name will be based on the name of the application -- program.exe.ini. If you don't like it, it's easy enough to change via the constructor or Name property.

Config

Config files are the most complex of the bunch, format and code wise. Let me begin by illustrating how the above data would look:

 
 

  
  
   
    
   
   
      
    
    

As you can see, there's a lot more going on here than with the other formats. The profile element contains an element for each section and the values are kept as attributes of add elements. Notice that for each section, the configSections element needs to specify how to read the values contained by it. This is all standard fare, required for the framework to properly load the values and allow you to retrieve them using the System.Configuration.ConfigurationSettings class, like this:

NameValueCollection section = (NameValueCollection)
    ConfigurationSettings.GetConfig("profile/A_Section");
string value = section["An Entry"];

Notice there's also an "appSettings" element, which wasn't part of the other samples. I just threw in there just to show that it may also be accessed via the Config class. If you're familiar with the System.Configuration namespace, you'll know that appSettings is the default section used for storing application-specific settings that may be read using the ConfigurationSettings.AppSettings property:

string value = ConfigurationSettings.AppSettings["App Entry"];

Well, the Config class also allows you to read (and of course, write) to that section, as follows:

Config config = new Config();
config.GroupName = null;  // don't use the "profile" group
...
string value = config.GetValue("appSettings", "App Entry", null);
config.SetValue("appSettings", "Update Date", DateTime.Today);

One thing to keep in mind is that .NET caches the config data as it reads it, so any subsequent updates to it on the file will not be seen by the System.Configuration classes. The Config class, however, has no such problem since the data is read from the file every time.

Like the Xml and Ini classes, the default file name will be based on the name of the application -- program.exe.config. This is the name expected by the .NET framework classes, but you can still change it if you want.

Usage

So now that you've seen the classes, how do you go about using them in your code? Well, I packaged them inside their own DLL, called AMS.Profile.dll. This makes them easy to use in your various projects, without having to recompile them everywhere. Simply add the DLL your project's References, which is done as follows inside Visual Studio .NET:

  1. Open your own project inside Visual Studio .NET.
  2. Inside the Solution Explorer toolbox, right click on the References folder and select "Add Reference".
  3. Click on the Browse button and select the AMS.Profile.dll file.
  4. Click OK. You may now use the AMS.Profile namespace inside your project.

Which class should I use?

OK, so how do you actually use these classes inside the code? That's the easy part, actually. The hard part may be deciding which one of the four to use. Whereas before you may have based your decision on the amount and complexity of the code involved, now that's no longer an issue. Now you just worry about which storage medium is best for the job. And that part is basically up to your program's requirements and/or personal preferences.

Here are some of my observations to help you decide:

  • When you bring up the Demo program you'll see a Test button. It's used to test most of the functionality of the selected Profile to verify it's working as expected. Well, I added some simple timing code to the method to measure how long the test takes for the selected profile. The end result: the Registry profile is by far the fastest of the four (especially after the first time). The Ini profile came in at a distant second, followed by Xml, and then Config. Ironic, isn't it? The newer technologies are also the slowest. Granted, the difference is one or two hundred milliseconds, which is probably not noticeable, but still, it may be something to keep in mind.
  • According to many people, config files are not meant to be written at run-time. The lack of .NET methods for doing so proves that. A better alternative is the Xml class which is meant to work with a separate file.
  • INI files may be old and gray, but for storage of small data they're still hard to beat, as far as their format goes.

Still trying to decide? Here's the bottom line.

  • Use the Config class only if you absolutely need to write to the app.config file, or for your own file.
  • Use the Ini class if your program works with legacy data stored in INI files.
  • Use the Registry class if you don't want to deal with extra files, or if you require high performance.
  • Use the Xml class if you need to make it easy to transfer the whole app to another location, or to add an extra element of coolness to your app. ;-)

Even if you don't pick the proper class from the start, it's easy enough to switch to another one later. OOP rocks!

How do I use the class?

Now that you have selected the profile class for the job, how do you use them inside your code? Well, most of the time you'll just declare the class as a field of another class and then call the GetValue and SetValue methods:

Xml profile = new Xml();
...
int width = profile.GetValue("Main Window", "Width", 800);
int height = profile.GetValue("Main Window", "Height", 600);
...
profile.SetValue("Main Window", "Width", this.Size.Width);
profile.SetValue("Main Window", "Height", this.Size.Height);

As you saw from the IProfile interface, there are several other methods, properties, and events available. Those you'll find fully documented inside the code, as well in the AMS.Profile.chm help file. I recommend you double click on the help file to get the complete details on everything that's available from the DLL. Of course, if you have questions or concerns, you may address them to me personally or by posting a message below.

Demo

I wrote the demo program to illustrate the functionality of the four Profile classes, and at the same time, to test the classes. I added a Test button that calls a method of the Profile base class to verify that the most important methods and properties work correctly and consistently across all profiles. You may want to check out that code for examples of how to use these classes.

Keep in mind that this is just a demo program, not a utility. Each Profile object works with their default Names. For example, for the Xml object, the name of the file will be Demo.exe.xml and will be located in the same folder as Demo.exe. There's no provision for changing these names, since, again, it's just a demo.

To use the demo, select a Profile from the top combo box. This will cause the sections in the profile to be placed into the Section combo. Choose a section from there and you'll see all of its entries get added to the Entry combo box. Choose an entry from there and you'll see its value placed into the Value textbox. If you want to add a new Section or Entry, simply type it into the proper combo box. Press the Save button to write the value to the profile via SetValue. This should all be pretty straightforward, I hope. If not, please let me know.

History

  • Version 1.0 - Oct 23, 2003
    • Initial release.
  • Version 1.1 - Mar 15, 2004
    • Fixed small bug in the Config class that was causing multiple identical entries. Thanks to Russkie for reporting it and giving me the fix.
    • Added ProgramExe property to the Profile class to prevent the DefaultName from raising an exception when Assembly.GetEntryAssembly() is null.
    • Added Encoding property to the Config and Xml properties to allow changing of the default encoding (UTF8) for new files.
    • Corrected some minor documentation typos.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值