Introduction
This article comes to solve the issue presented by David Betz in his article Reusing .NET Assemblies in Silverlight by creating a simple Console application that can be run in the Post-Build commands.
Background
Now that Silverlight has entered its second version and is starting to be implemented by a larger number of developers, more advanced applications are being developed on the framework.
One of the powerful features of Silverlight is its connectivity with WCF Services. As most architects choose to implement the contract classes in a public assembly that is then referenced by both the Service Implementor and the Client, an issue occurs when attempting to reference this public assembly through a Silverlight application.
I won't go into too much detail as to why Visual Studio doesn't allow you to add non Silverlight assemblies to a Silverlight project since this is presented in great depth in the article posted by David Betz on his NeFX harmonics site. But, in a nutshell, the assemblies that Silverlight uses (System.dll, mscorlib.dll etc.) are not the same as the basic .NET assemblies although many of the functions exist in both. You could say that the Silverlight assemblies are a slim-down version of the full .NET assemblies, but are almost identical in the functions both expose.
So, basically, if we could modify our public assembly to reference the Silverlight version of these assemblies without modifying the code, we could re-use our public assembly in our Silverlight app.
MSIL Assemblies
Basically, when you write you code in any .NET language (C#, VB.NET), the compiler compiles the code to what is known as MSIL or Microsoft Intermediate Language. This code is then assembled into a .NET Assembly that gets loaded and JIT'ed at runtime. Since assembly references are only validated at runtime, we can safely modify the reference of the problematic assemblies to the correct Silverlight assemblies in the IL code, and when the Assembly is loaded through Silverlight, the correct assemblies will already be in memory and the JIT compiler will compile our code against it.
Our Target
So, to keep things simple, what we want to do basically is once the assembly is built, disassemble it back into MSIL, make a few changes in the references (maybe remove some attributes that are not supported by the Silverlight version of the references), and re-assemble the code back into a new assembly that Silverlight can use.
Note: the new assembly we will create can only be used from a Silverlight project, but all the Type signatures will be identical to our original assembly, and so we will be able to share the WCF contracts between the two.
Setting up the Application
The code included is a simple Console application written in C#. It is accompanied by an XML file containing all the settings it needs to run.
Basically, the application needs to know the following information:
- The location of the MSIL Assembler and Disassembler (ilasm.exe and ildasm.exe).
- A list of assemblies that alter between their Silverlight implementation and their .NET counterparts.
- A list of any non-supported attributes located in the original .NET assemblies and that do not exist in the Silverlight ones that must be removed from the IL code.
The XML file looks something like this:
="1.0" ="utf-8"
<SLAsm.Settings xmlns="urn:Silverlight-Assmblies/SLAsmSettings.xsd"
ILAssembler="C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe"
ILDisassembler="C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe">
<ExternAssembly Name="System" Version="2.0.5.0"
PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="mscorlib" Version="2.0.5.0"
PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="System.Core" Version="2.0.5.0"
PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="System.Net" Version="2.0.5.0"
PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="System.Runtime.Serialization"
Version="2.0.5.0" PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="System.Windows"
Version="2.0.5.0" PublicKeyToken="7C EC 85 D7 BE A7 79 8E" />
<ExternAssembly Name="System.Json"
Version="2.0.5.0" PublicKeyToken="31 BF 38 56 AD 36 4E 35" />
<ExternAssembly Name="System.ServiceModel"
Version="2.0.5.0" PublicKeyToken="31 BF 38 56 AD 36 4E 35" >
<UnsupportedAttribute>
System.ServiceModel.FaultContractAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.DeliveryRequirementsAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.CallbackBehaviorAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.MessageHeaderAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.MessageHeaderArrayAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.MessagePropertyAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.OperationBehaviorAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.ServiceBehaviorAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.XmlSerializerFormatAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.PeerHopCountAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.TransactionFlowAttribute
</UnsupportedAttribute>
<UnsupportedAttribute>
System.ServiceModel.Activation.AspNetCompatibilityRequirementsAttribute
</UnsupportedAttribute>
</ExternAssembly>
</SLAsm.Settings>
Remember to set the correct paths to the ILAsm.exe and ILdasm.exe files
I have pre-populated the file with all the Silverlight assembly references and added a list of unsupported attributes to the System.ServiceModel
assembly.
Note: this is not a complete list of unsuported attributes, rather the ones I have run into myself. Feel free to add any attributes you find that are not working with Silverlight. (You know when your Silverlight app throws a TypeLoadException
when it attempts to load your assembly.)
Using the Code
Now that you have set up the XML file, you will probably want to use the code. The application is a simple console app that takes the following arguments:
<AssemblyPath.dll> [/Key=<StrongNameKeyPath.snk>] [/Output=<TargetPath.dll>]
where AssemblyPath.dll is the path to your public .NET assembly. The optional /Output switch specifies where you want to store your new Silverlight assembly. (If you leave this blank, the output file will be AssemblyPath.SL.dll.)
And finally, since we messed all the original IL code, if the original assembly was signed with a strong name key, the newly created assembly will not be properly signed, so you can include the path to the original .snk file you used to sign your assembly, and the app will put it all back together
Setting up as a Post-Build Event
The best way to make sure your public assembly gets converted into a Silverlight compatible one and stays updated whenever you modify it is to add a Post-Build command to your project. To do this, right click on your project and click on the 'Project Properties' menu item.
Navigate to the 'Build Events' tab:
Now, click on the 'Edit Post-build' command, and a dialog opens:
Enter the path to our converter application followed by the "$(TargetPath)" macro to specify the built assembly as the file that will be converted. Add any extra arguments (like the /Output= and /Key=) you need and click on 'OK'.
In the 'Build Events' tab, make sure the option for 'Run the post-build event:' is set to 'On successful build'. Save the project. From here on, whenever your project builds successfully, Visual-Studio will run our app and create a Silverlight compatible assembly of the project.
Note: if the creation of the compatible assembly fails, the entire project will be marked as 'Build Failed', and you will see the error message returned by ilasm.exe in your output window.
Conclusion
I hope this is useful to anyone out there that may have hit the same issue, and if it did help, be sure to drop me a line...
History
- 2009 01 22 - Updated source to load ilasm.exe from the .NET installation folder, also included ildasm.exe in the solution for those of you who don't have the .NET SDK installed.
- 2009 03 03 - Updated the Remove unsupported attributes feature to support methods with more than one attribute (as pointed out by Boris Modylevsky)