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:
- Rename XAP file to a ZIP file.
- Extract the ServiceReferences.ClientConfig file.
- Update the configuration file with the proper IP information.
- Update the ZIP file and save.
- 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:
- Grab all the contents from the MemoryStream.
- Replace the IP information in the content.
- Clear the MemoryStream.
- Overwrite the stream contents with the new content.
- 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:
- Extract the ServiceReferences.ClientConfig file.
- Call the UpdateConfigurationFile method above to revise the IP information.
- 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.
Thank you for posting this, prior to this I usually use 7zip by opening the xap file in 7zip, and modify the config file from there, it works but it will be tough when you deploy the application.