Share via


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="https://schemas.microsoft.com/client/2007/deployment"
    xmlns:x="https://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 response 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.

Originally posted by Josh Twist on 18 February 2009 here: https://www.thejoyofcode.com/How_to_download_and_crack_a_Xap_in_Silverlight.aspx