views:

852

answers:

3

Hi,

I'm trying to call a custom action within my Setup & Deployment project to update some items within the app.config on my app. I've wrapped up the custom config section in the usual way, e.g.:

[ConfigurationProperty("serviceProvider", IsRequired = true)]
public string ServiceProvider
{
    get { return (string)base["serviceProvider"]; }
    set { base["dataProviderFactory"] = value; }
}

I've set the custom action to be called during the Install section of installation just after base.Install(stateSaver). The code is:

string exePath = string.Format("{0} MyApp.exe", Context.Parameters["DP_TargetDir"]);
SysConfig.Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);
Configuration. MyApp section = Configuration.MyApp)config.GetSection("MyApp");

When I run this, I get this error:

System.Configuration.ConfigurationErrorsException: An error occurred creating the configuration section handler for MyApp: Could not load file or assembly 'MyCompany. MyApp.Configuration' or one of its dependencies. The system cannot find the file specified. (C:\Program Files\MyCompany\MyApp\MyApp.exe.config line 5) ---> System.IO.FileNotFoundException: Could not load file or assembly 'MyCompany.MyApp.Configuration' or one of its dependencies. The system cannot find the file specified.

Line 5 in the config is:

<section name="MyApp"
    type="MyCompany.MyApp.Configuration.MyApp, MyCompany.MyApp.Configuration"
    requirePermission="false" />

The class library with the installer code (that being the only class in that library) has a reference to the config assembly.

Is there something really obvious that I'm missing here? I can't work out why the ref to the config isn't being found.

Any help/suggestions would be very much appreciated.

Thanks,

K

+1  A: 

Check that 'MyCompany.MyApp.Configuration' is marked with copy local=true.

Check also that your project also references the dll's that 'MyCompany.MyApp.Configuration' depend upon.

Shiraz Bhaiji
Copy local is set to true and all required references have been referenced as far as I can see. If I run the code outside of the installer class (through a console app) then it works fine.
Kevin Wilson
+1  A: 

The Setup Project will call all installer classes that it finds in the project assembly files.

I must admit that I tend to override the Install method within the Installer class.

Here's a direct cut and paste from some code that I have here:

public override void Install(System.Collections.IDictionary stateSaver)
{
    base.Install(stateSaver);

   // System.Diagnostics.Debugger.Break();
    string targetDirectory = Context.Parameters["targetdir"];

    string param1 = Context.Parameters["Param1"];
    string param2 = Context.Parameters["Param2"];
    string param3 = Context.Parameters["Param3"];
    string param4 = Context.Parameters["Param4"];

    string exePath = string.Format("{0}AlarmMonitor.exe", "[SystemFolder]");

    PHPCCTVClassLibrary.CctvSite site = new PHPCCTVClassLibrary.CctvSite();
    site.Name = param1;
    site.EndPoint = string.Format(@"tcp://{0}:5005", param2);
    site.Password = param4;
    site.Username = param3;
    site.AutoReconnect = true;
    site.AlarmHandling = new PHPCCTVClassLibrary.CctvSiteAlarmHandling();
    site.AlarmHandling.Audio = new PHPCCTVClassLibrary.AudioSetup();
    site.AlarmHandling.Audio.AlarmAddedFile = "alarmadded.wav";
    site.AlarmHandling.Audio.AlarmDeletedFile = "alarmdeleted.wav";
    site.AlarmHandling.Audio.AlarmUpdatedFile = "alarmupdated.wav";

    PHPCCTVClassLibrary.CctvSites sites = new PHPCCTVClassLibrary.CctvSites();
    sites.Site = new PHPCCTVClassLibrary.CctvSite[] { site };
    System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(PHPCCTVClassLibrary.CctvSites));
    //System.Diagnostics.Debugger.Break();

    System.IO.TextWriter tw = new System.IO.StreamWriter(@"CCTVAlarmConfig.xml", false);
    serializer.Serialize(tw, sites);
    tw.Close();

}

It creates an xml config file, being called by a Custom action that runs at install and has the following CustomActionData: /Param1="[EDITA1]" /Param2="[EDITA2]" /Param3="[EDITA3]" /Param4="[EDITA4]" /targetdir="[TARGETDIR] "

EDITA1 etc come from a user interface with 4 text boxes. I have deliberately included a space after [TARGETDIR] as without it you'll get some quite odd behaviour as it can't then find the installation directory.

The System.Diagnostics.Debugger.Break() comment allows you to debug from this point if the line is uncommented.

ChrisBD
Hi, thanks for the code sample. I think the problem is more to do with the reading of the existing config XML into the object type. The hint about the debugger should be a massive help though, thanks.
Kevin Wilson
+3  A: 

I managed to find some code on MSDN that provides a working (albeit hacked) way to do this. Link is here: ConfigurationManager, custom config, and installutil / path searching problem.

Wouldn't have found it if not for being able to debug with help from suggestions so thanks to both of you for that.

For reference, my final install code is:

public override void Install(IDictionary stateSaver)
{
    base.Install(stateSaver);

    string targetDirectory = Context.Parameters["DP_TargetDir"];
    stateSaver.Add("TargetDir", targetDirectory);

    string exePath = Path.Combine(targetDirectory, "MyApp.exe");

    System.Diagnostics.Debugger.Break();

    ResolveEventHandler tempResolveEventHandler =
        (sender, args) =>
            {
                string simpleName = new AssemblyName(args.Name).Name;
                string path = Path.Combine(targetDirectory, simpleName + ".dll");
                return File.Exists(path)
                    ? Assembly.LoadFrom(path)
                    : null;
            };

    try
    {
        // hook up asm resolve handler  
        AppDomain.CurrentDomain.AssemblyResolve += tempResolveEventHandler;

        System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);
        Configuration.MyApp section = (Configuration.MyApp) config.Sections["MyApp"];

        if (section != null)
        {
            // "ApplicationSettings.DefaultDatabasePath" is the custom config value I'm updating
            section.ApplicationSettings.DefaultDatabasePath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
            config.Save();
        }
    }
    finally
    {
        // remove asm resolve handler.  
        AppDomain.CurrentDomain.AssemblyResolve -= tempResolveEventHandler;
    }

}
Kevin Wilson
Thank you very much for this solution. I have an application that configures configuration sections, but through a plug-in system, i.e. they are not referenced by the application during build time. I came across that pesky FileNotFound exception upon accessing them in the configuration, but through your solution, I can now resolve them properly, and everything succeeds!
Alex