Thursday, 10 October 2013

Custom Installer And XML Serialization There was an error generating the XML Document - FileNotFoundException

I've recently had an issue that's wasted an entire day of my life that I'd like to share.

We have some classes that serialize to XML. These work correctly in all scenarios. Or so I thought.

We have a .NET Custom Installation Action written in C#. During the installation process the custom action opens one of these classes, configured the properties of the class and then saves it as XML. The code works correctly when executed from a test Windows Form application however fails with the following error when it is executed from the installer or from InstallUtil.exe.

System.InvalidOperationException: There was an error generating the XML document. ---> System.IO.FileNotFoundException: Could not load file or assembly 'filename' or one of its dependencies. The system cannot find the file specified.

Very strange as the file specified is in the same directory as the custom action.

Looking at the code in the custom action it seems that the codebase is being set to C:\windows\Microsoft.NET\Framework\v2.0.50727 rather than the directory where the custom action dll is found. This must be an action by Windows Installer / InstallUtil.exe.

If you place the dlls in the C:\windows\Microsoft.NET\Framework\v2.0.50727 directory then everything works correctly.

This is obviously not ideal so the workaround we have implemented is to use the AssemblyResolve event - this is fired when .NET can't find an assembly and you can helpfully point it in the right direction, in this case we simply point it back to the directory in which the executing assembly is located and everything is back to normal!

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

/// <summary>
/// Workaround for the location of assemblies in the current directory
/// </summary>
/// <returns>The assembly loaded from the current directory.</returns>
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    String AssemblyShortName = args.Name.Split(new string[] { "," }, StringSplitOptions.None)[0];
    String RootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    String AssemblyPath = String.Format(@"{0}\{1}.dll", RootPath, AssemblyShortName);
    return Assembly.LoadFrom(AssemblyPath);

Take a look at XIA Configuration - Server Documentation

No comments:

Post a Comment