All posts tagged Configuration

One of the bigger annoyances dealing with programming in Silverlight is the deployment of XAP files. In order to properly update a XAP file you typically:

  1. Rename XAP file to a ZIP file.
  2. Extract the ServiceReferences.ClientConfig file.
  3. Update the configuration file with the proper IP information.
  4. Update the ZIP file and save.
  5. Rename ZIP file back to XAP.

Doesn’t that sound stupid? I think so. What the hell was Microsoft thinking?

Q: So, how do we do that with code so we can skip this frustration?
A: Let’s first look at a few factors:

  • We are using the .Net 4.0 Framework.
  • Don’t bother using System.IO.Packaging.ZipPackage. It thinks XAP files are always corrupt. It’s annoying.
  • We are just updating the IP information.

To solve the ZIP file library issue, I turned to DotNetZip: http://dotnetzip.codeplex.com/. It’s by far one of the most convenient libraries out there and it’s free.

First, let’s look at how we update the configuration file if it was committed to a MemoryStream. In this snippet we:

  1. Grab all the contents from the MemoryStream.
  2. Replace the IP information in the content.
  3. Clear the MemoryStream.
  4. Overwrite the stream contents with the new content.
  5. Reset the position in the stream to 0.

/// <summary>
/// Updates the configuration file.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateConfigurationFile(MemoryStream stream,
    string replacementIp, string destinationIp)
{
    bool isSuccessful = false;

    try
    {
        // Read current file
        var reader = new StreamReader(stream);
        stream.Position = 0;
        var contents = reader.ReadToEnd();
                
        // Update IP information
        var newContents = contents.Replace(replacementIp, destinationIp);

        // Reset contents of stream
        stream.SetLength(0);

        // Overwrite original configuration file
        var writer = new StreamWriter(stream);
        writer.Write(newContents);
        writer.Flush();

        // Set position in stream to 0.
        // This allows us to start writing from the beginning of the stream.
        stream.Seek(0, SeekOrigin.Begin);

        // Success
        isSuccessful = true;
    }
    catch (Exception)
    {
    }

    // return
    return isSuccessful;
}

Our main method below does this:

  1. Extract the ServiceReferences.ClientConfig file.
  2. Call the UpdateConfigurationFile method above to revise the IP information.
  3. Update the ZIP file and commit the changes.

/// <summary>
/// Updates the silverlight configuration file.
/// </summary>
/// <param name="configFileName">Name of the config file.</param>
/// <param name="xapFilePath">The xap file path.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateSilverlightConfigurationFile(string configFileName,
    string xapFilePath, string replacementIp, string destinationIp)
{
    // Check file path
    if (!File.Exists(xapFilePath)) { return false; }

    // Open XAP and modify configuration
    using (var zip = ZipFile.Read(xapFilePath))
    {
        // Get config file
        var entry = zip[configFileName];
        var stream = new MemoryStream();
        entry.Extract(stream);

        // Manipulate configuration
        var updated =
            UpdateConfigurationFile(stream, replacementIp, destinationIp);

        // Evaluate
        if (updated)
        {
            // Replace existing configuration file in XAP
            zip.UpdateEntry(configFileName, stream);
            zip.Save();
        }
    }

    // return
    return true;
}

So, let’s look at the code in it’s entirety so that we get an implementation example as well as the needed includes:


using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ionic.Zip;

namespace XAPFileUpdaterTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            // Intialize UI
            InitializeComponent();

            // Parameters
            string configFile = "ServiceReferences.ClientConfig";
            string xap = @"MyAwesomeApp.xap";
            string replacementIp = "127.0.0.1";
            string destinationIp = "12.23.45.67";

            // Check
            var isClientConfigUpdated =
                UpdateSilverlightConfigurationFile(
                   configFile, xap, replacementIp, destinationIp);

            // Setup message
            var message =
                (isClientConfigUpdated) ? "was successful" : "failed";

            // Notify user
            MessageBox.Show("The current update " + message);
        }

        /// <summary>
        /// Updates the configuration file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateConfigurationFile(MemoryStream stream,
            string replacementIp, string destinationIp)
        {
            bool isSuccessful = false;

            try
            {
                // Read current file
                var reader = new StreamReader(stream);
                stream.Position = 0;
                var contents = reader.ReadToEnd();

                // Update IP information
                var newContents = contents.Replace(replacementIp, destinationIp);

                // Reset contents of stream
                stream.SetLength(0);

                // Overwrite original configuration file
                var writer = new StreamWriter(stream);
                writer.Write(newContents);
                writer.Flush();

                // Set position in stream to 0.
                // This allows us to start writing from the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);

                // Success
                isSuccessful = true;
            }
            catch (Exception)
            {
            }

            // return
            return isSuccessful;
        }

        /// <summary>
        /// Updates the silverlight configuration file.
        /// </summary>
        /// <param name="configFileName">Name of the config file.</param>
        /// <param name="xapFilePath">The xap file path.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateSilverlightConfigurationFile(string configFileName,
            string xapFilePath, string replacementIp, string destinationIp)
        {
            // Check file path
            if (!File.Exists(xapFilePath)) { return false; }

            // Open XAP and modify configuration
            using (var zip = ZipFile.Read(xapFilePath))
            {
                // Get config file
                var entry = zip[configFileName];
                var stream = new MemoryStream();
                entry.Extract(stream);

                // Manipulate configuration
                var updated =
                    UpdateConfigurationFile(stream, replacementIp, destinationIp);

                // Evaluate
                if (updated)
                {
                    // Replace existing configuration file in XAP
                    zip.UpdateEntry(configFileName, stream);
                    zip.Save();
                }
            }

            // return
            return true;
        }
    }
}

With the help of DotNetZip we were able to update the XAP file with our revised IP information.

That’s all for now. I hope I helped you with this annoyance.