views:

55

answers:

2

I want to change the location where my application looks for the app.config file.

I know that I can use ConfigurationManager.OpenExeConfiguration() to access an arbitrary config file - however, when the .Net Framework reads the config file (for ConnectionStrings or EventSources, for instance), it will look at the default location. I want to actually change the location, globally for the entire .Net Framework (for my application, of course).

I also know that I can use AppDomainSetup to change the location of the app.config for a new AppDomain. However, that doesn't apply to the primary AppDomain of the application.

I also know that I can override function Main() and create a new AppDomain as above and run my application in that new AppDomain. However, that has other side-effects - for instance, Assembly.GetEntryAssembly() will return a null reference.

Given how everything else works in .Net, I would expect there to be some way to configure the startup environment of my application - via a Application Manifest, or some such - but I have been unable to find even a glimmer of hope in that direction.

Any pointer would be helpful.

David Mullin

A: 

I am not sure why you want to change the location of your config file - perhaps there can be different approach for solving your actual problem. I had a requirement where I wanted to share configuration file across related applications - I had chosen to use own xml file as it had given me extra benefit of having complete control over the schema.

In your case, it's possible to externalize sections of your config file to a separate file using configSource property. See here under "Using External Configuration Files" to check how it has been done for connection strings section. Perhaps, this may help you.

VinayC
The main reason for wanting to do this are that the application is installed via ClickOnce, which effectively hides the config file. However, there are settings (like TraceSource entries) that I want to be configurable on the client machine and to persist between installations (since a ClickOnce update may replace the config file).
David Mullin
I see - perhaps you can use AppSettings (http://msdn.microsoft.com/en-us/library/k4s6c3a0.aspx) for that? They support ClickOnce scenario - see http://msdn.microsoft.com/en-us/library/ms228995.aspx.
VinayC
I can't use AppSettings, because the .Net Framework won't look there for TraceSources (or any other framework-specific configuration).
David Mullin
+2  A: 

I used the approach with starting another AppDomain from Main(), specifying the "new" location of the configuration file.

No issues with GetEntryAssembly(); it only returns null, when being called from unmanaged code - or at least it doesn't for me, as I use ExecuteAssembly() to create/run the second AppDomain, much like this:

int Main(string[] args)
{
   string currentExecutable = Assembly.GetExecutingAssembly().Location;

   bool inChild = false;
   List<string> xargs = new List<string>();
   foreach (string arg in xargs)
   {
      if (arg.Equals("-child"))
      {
         inChild = true;
      }
      /* Parse other command line arguments */
      else
      {
         xargs.Add(arg);
      }
   }

   if (!inChild)
   {
      AppDomainSetup info = new AppDomainSetup();
      info.ConfigurationFile = /* Path to desired App.Config File */;
      Evidence evidence = AppDomain.CurrentDomain.Evidence;
      AppDomain domain = AppDomain.CreateDomain(friendlyName, evidence, info);

      xargs.Add("-child"); // Prevent recursion

      return domain.ExecuteAssembly(currentExecutable, evidence, xargs.ToArray());
   }

   // Execute actual Main-Code, we are in the child domain with the custom app.config

   return 0;
}

Note that we are effectively rerunning the EXE, just as a AppDomain and with a different config. Also note that you need to have some "magic" option that prevents this from going on endlessly.

I crafted this out from a bigger (real) chunk of code, so it might not work as is, but should illustrate the concept.

Christian.K
Hmmm. In my test of this approach (that was different than yours), GetEntryAssembly did return null. But, I didn't do ExecuteAssembly - I found the "second Main" that I had written and executed that. I'll try your approach and see if it works for me.
David Mullin
I think ExecuteAssembly makes the difference. At least the Docs say that GetEntryAssembly returns the executeable, *or* the one passed to ExecuteAssembly().
Christian.K