Skip Navigation LinksHome > View Post

How to download and crack a Xap in Silverlight

Sometimes it is useful to be able to download extra functionality into your application at runtime. It's always been possible to make a server call from Silverlight to download an extra assembly and load it into memory. But what if you'd like to download a bunch of assemblies in neat zipped up package.

As I'm sure you're already aware - this is exactly how Silverlight applications are downloaded. In a zip file called a xap.

It's easy to make another XAP that contains just libraries for use later. In Visual Studio, choose Add New Project and choose Silverlight Application. Yup, that was Application, not class library.

Now just delete the App.xaml and App.xaml.cs files to make it a Silverlight class library with the added benefit of being compiled and packed into a tiny xap file. You can also delete the MainWindow.xaml if you don't need it.

In this example I'm going to imagine I'm loading up an Administration Module into my application at runtime. It's not needed by most users so I'm just going to load it on demand. Therefore I've created a Silverlight Application as described above with the name AdminModule.

The XAP that will be generated contains a little file called AppManifest.xaml that describes the contents of the package - here's mine:

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="AdminModule" EntryPointType="AdminModule.App" RuntimeVersion="3.0.40818.0">
<Deployment.Parts>
<AssemblyPart x:Name="AdminModule" Source="AdminModule.dll" />
<AssemblyPart x:Name="System.Xml.Linq" Source="System.Xml.Linq.dll" />
</Deployment.Parts>
</Deployment>

Notice that it includes any referenced DLLs that had the 'Copy Local' property set to True.

Imagine we have an LoadAdminModuleButton on our silverlight application and it has a Click handler:


private void LoadAdminModuleButton_Click(object sender, RoutedEventArgs e)
{
    Uri secureModuleUri = GetApplicationRelativeUri("../Admin/AdminModule.xap");
    WebClient client = new WebClient();
    client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
    client.OpenReadAsync(secureModuleUri);
}

Here I'm using the WebClient to fire an HTTP request asking for the xap file. We'll need my handy GetApplicationRelativeUri method for this, that creates an absolute URI based on a relative one and the location from which the current Application was sourced:

public static Uri GetApplicationRelativeUri(string relativeUri)
{
    return new Uri(Application.Current.Host.Source, new Uri(relativeUri, UriKind.Relative));
}

When the respons comes back I need to crack open the stream and load the assembly. Once we've loaded the assembly I loop through all types looking for types that implement my IInitializable interface. Once found, I make sure they have a default public constructor so we can easily use the Activator to create an instance, before calling Initialize!

void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    Assembly assembly = LoadAssembly(e.Result, new AssemblyPart { Source = "SecureModule.dll" } );
    var moduleTypes = assembly.GetTypes().Where(t => typeof(IInitializable).IsAssignableFrom(t));

    foreach (var moduleType in moduleTypes)
    {
        var defaultConstructor = moduleType.GetConstructor(new Type[0]);
        if (defaultConstructor == null || !defaultConstructor.IsPublic)
        {
            continue;
        }
        IInitializable module = (IInitializable)Activator.CreateInstance(moduleType);
        module.Initialize();
    }
}

Oh yes, that LoadAssembly method is here:

private static Assembly LoadAssembly(Stream sourceStream, AssemblyPart assemblyPart)
{
    Stream assemblyStream = GetResourceStream(sourceStream, assemblyPart.Source).Stream;
    return assemblyPart.Load(assemblyStream);
}

And the IInitializable interface here:

public interface IInitializable
{
    void Initialize();
}

Of course, this approach is _very_ similar to that used in Prism's Xaml Module Catalogue approach. If you want to do this sort of thing - Prism is probably going to be very attractive to you.

Tags: Silverlight

 
Josh Post By Josh Twist
8:42 AM
18 Feb 2010

» Next Post: VBUG4Thought - Building an AppMarket with the Managed Extensibility Framework
« Previous Post: DotNetDevNet: Xamlathon Live '10

Comments are closed for this post.

Posted by John Schroedl @ 18 Feb 2010 10:02 AM
Sweet example! I've wondered about how to do this exact thing recently.

Posted by josh @ 18 Feb 2010 11:17 AM
Glad it's proved useful already.

In case anybody is interested - this is how you might interrogate a XAP to find out what assemblies are installed. It uses XML to parse the AppManifest.xaml file for AssemblyParts:


private static IEnumerable<AssemblyPart> GetAssemblyParts(Stream xapStream)
{
XName apName = XName.Get("AssemblyPart", "http://schemas.microsoft.com/client/2007/deployment";);

using (var resourceStream = GetResourceStream(xapStream, "AppManifest.xaml").Stream)
{
var root = XDocument.Load(resourceStream);
var parts = root.Descendants(apName)
.Select(x => new AssemblyPart { Source = x.Attribute("Source").Value });
return parts;
}
}

Posted by DotNetDrop @ 30 Mar 2010 1:05 PM
Josh,
You might want to mention that this scenario can now be covered with MEF and is pretty elegant. Take a look at gblock's Mix10 talk on this subject for more info.

Posted by Raluca @ 13 Apr 2011 9:14 AM
Hi!
Do you know if I can also load linq.dll on demand?
10x

© 2005 - 2014 Josh Twist - All Rights Reserved.