Introduction
When developing DLLs for distribution, it is often desired to have an app.config file associated with that DLL. However, the .NET 1.1 Framework only allows a single app.config and it is usually associated with the running application. This really leaves developers in a pinch as they do not know if an application is using an app.config to "merge" its entries into, or they require the application developer to include entries into their config file for DLL usage.
Background
One method commonly used is to write your own XML file and put your configuration info there. This method has several drawbacks. What if you have included a third party DLL that has configuration information that it expects to see in an app.config file? You are now stuck with the same problem. One such case of this problem arises when you use the Microsoft Enterprise Library in your DLL. The Libraries configuration tool adds a line into the app.config file that points to its own config file for internal use. When you call a library class, it looks in the app.config file for the name of its own config file. Things get ugly from here.
If you have attempted to Google an answer to this problem, you will find "can't be done". This article will explain how it can be done.
I want to start by stating the following disclaimer(s): It is not a normal practice to use reflection to "patch" back another class. This example is an extreme case to get around what I consider is a serious flaw in the .NET Framework. Portability was not a factor when developing this technique. This technique is not needed with .NET 2.0 in my understanding. All that said, let's get started.
Using the code
The .NET Framework reads the application configuration file into a static hashtable whenever the application domain is first referenced. Once it is read, it cannot be reread or modified. This is unfortunate as it does not allow us to call any methods or properties to alter this behavior. Not accepting that there is no workaround, I started to snoop into the .NET Framework System.Dll file using Lutz Roeder's .NET Reflector. What I found is that you can make the framework reread the config file if you reset its internal variables into thinking that the app.config file has not been read. Doing this is ugly, but desperate people take desperate measures.
The following code is a simple class to demonstrate how to do this. I will leave it up to the reader to put this in your own code.
The main point of all this is to change the ConfigurationSettings
class static variable "_configurationInitialized
" to false
, and null out the "_configSystem
". Doing so will make the framework read the app.config file into memory. It is very important to undo the changes made before you leave. If not, the using application will now be pointing at your DLL's config file. One method to ensure this safety is to wrap your config parameters into a class. I have added a static ConnectionString
method to this class for demonstration purposes. It uses the C# "using
" statement to make sure that our object is destroyed, and that "Dispose
" is called to undo our changes. You can wrap calls to the Enterprise Library, or another third party DLL the same way.
internal class MyDllConfig : IDisposable
{
private string _oldConfig;
private bool _libCompat;
private const string _newConfig = "mydll.dll.config";
internal MyDllConfig()
{
_libCompat = Assembly.GetAssembly(typeof(
ConfigurationSettings)).GetName().Version.ToString().
CompareTo("1.0.5000.0") == 0;
_oldConfig = AppDomain.CurrentDomain.GetData(
"APP_CONFIG_FILE").ToString();
Switch(_newConfig);
}
protected void Switch(string config)
{
if ( _libCompat )
{
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE",config);
FieldInfo fiInit = typeof(
System.Configuration.ConfigurationSettings).GetField(
"_configurationInitialized",
BindingFlags.NonPublic|BindingFlags.Static);
FieldInfo fiSystem = typeof(
System.Configuration.ConfigurationSettings).GetField(
"_configSystem",BindingFlags.NonPublic|BindingFlags.Static);
if ( fiInit != null && fiSystem != null )
{
fiInit.SetValue(null,false);
fiSystem.SetValue(null,null);
}
}
}
public void Dispose()
{
Switch(_oldConfig);
}
public static string ConnectionString()
{
string cstr;
using ( new MyDllConfig() )
{
cstr = ConfigurationSettings.AppSettings["ConnectionString"];
}
return cstr;
}
}
Points of Interest
I have place a small sanity check into this class that checks the version number of the System DLL that this was written for. If another version is detected, this patch is bypassed.
The AppDomain has a property SetupInformation
that has a ConfigurationFile
property. I found that if you set your app's config file name using this property then it is ignored. Using the SetData
method causes the class to re-initialize this variable and gives us our desired results, including adding the application path onto our filename for us!
The Visual Studio IDE will allow you to place a generic app.config file into your DLL project. Adding the following line to your post build events will copy and rename this file to the appropriate place.
Copy $(ProjectDir)app.config $(TargetPath).config