Tuesday, July 7, 2009

How to use an app.config file in a DLL plugin (External Command)

This post relates to wider .Net app.config files and linked DLL’s, but more specifically, my instance was relating to the Revit API.

For those that don’t know, an app.config file is a nice, easy way of doing configuration for your applications in .net. You can use the System.Configuration namespace to quickly access an XML config file without needing to do any manual XML reading. Unfortunately, you can only have one app.config file per executable, so if you have DLL’s linked into your application, they cannot have their own app.config files.

Seeing as the Revit API is done by external .Net DLL files which are opened within the Revit environment, this applies to them as well.

At Bornhorst + Ward, we have all of our external commands on a central network share, and each staff members ini is edited (using a tool I’ve written) to point to these external commands. I’d previously tried to use app.config files, and found they weren’t working, it seems they weren’t looking for the correct file in that network directory.

The way around it for me, was to manually locate the app.config file in my code.

   1: /// <summary>
   2: /// Get the configuration for the supplied type
   3: /// </summary>
   4: /// <param name="type">type of class</param>
   5: /// <returns>a configuration</returns>
   6: public static System.Configuration.Configuration GetConfig(Type type)
   7: {
   8:     //workout app.config lokcation
   9:     string dllLocation = type.Assembly.Location + ".config";
  10:  
  11:     if (dllLocation == null)
  12:         throw new Exception("Could not find config file, add .config in DLL location");
  13:  
  14:     //create config
  15:     ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
  16:     fileMap.ExeConfigFilename = dllLocation;
  17:     System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
  18:  
  19:     return config;
  20: }

Seeing as app.config files on DLL’s are named dllName.dll.config this gave me an easy rule for finding the config file. I then used an ExeConfigurationFileMap pointed to that location to generate the Configuration. The ‘type’ parameter, is for me to supply a type (using typeof(ClassName)) to identify the correct dll – as this method for me was written in a separate helper dll which contains some commonly used functions for all of my tools.

Then, to access the properties in that file:

   1: /// <summary>
   2: /// Gets a specific config property
   3: /// </summary>
   4: /// <param name="key">the property to get</param>
   5: /// <param name="type">type of class asking - to get right assembly</param>
   6: /// <returns>value</returns>
   7: public static string GetConfigProperty(string key, Type type)
   8: {
   9:     System.Configuration.Configuration config = GetConfig(type);
  10:     return config.AppSettings.Settings[key].Value;
  11: }

I call that method, supply the typeof(ClassName) of the class calling it, and it gets the configuration from my previous method, and then uses an access method similar to the standard AppSettings class.

Now I can have .configs for all of my revit external commands and access them easily!

2 comments:

s.pechkin said...

1. How do you expect to get an exception after this line?

string dllLocation = type.Assembly.Location + ".config";

Location returns empty string if the assembly is loaded from byte array. You will always have something in dllLocation, even if it's only a ".config". Exception will come later - ConfigurationErrorsException in this line:

System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

s.pechkin said...

Also, it would help to mention that if you want to use this way of access to DLL configurations with VSTS, NUnit or other testing environment, you should add each configuration file you want to access to the list of deployed files (for VSTS: double click on the relevant .testrunconfig, select "Deployment", click "Add file").