Hacking into Xamarin Forms

As the title of this article suggests, we will dig deep into the internals of Xamarin Forms by using ILSpy to inspect and ildasm/ilasm to modify its source code.  This is not guide, how to learn or use Xamarin Forms. If you want such a guide, I highly recommence the Xamarin.Forms Kickstarter.

Xamarin Forms is a really young project, it was introduced in May 2014 by Xamarin, therefore, as of writing these lines, it’s about one year old. While it has already an impressive set of features, there are still certain features missing. Most of the parts, that a modern app requires, is covered by Xamarin Forms. Things, that are not covered, can usually be added by using a custom renderer. But to squeeze the last bits out of it, you’ll want to modify the internals.

Digging into the Code

There are different ways to inspect the code of .Net assemblies. I prefer the free and open source tool ILSpy. The only downside: It runs only on Microsoft Windows, so if you are developing on OS X, like I do, you will need a Windows VM or another Computer with Windows installed.

After downloading, unzipping and starting ILSpy, you will see the main window. It is split into two parts, on the left side, you’ll see a list of assemblies including their namespaces and classes with their methods and properties. On the right side, you will see the source code of a class or method, that you selected. In the toolbar, you can switch the source code language, currently available options are C#, VB and IL.

ILSpy

To inspect the code of Xamarin.Forms, you will have to add its assemblies to ILSpy. If you are using nuget packages (which is the default, if you create a project in Xamarin Studio), the assemblies will be located in the packages folder in your main directory (where the solution file is usually located). The location e.g. for the iOS assemblies of Xamarin.Forms version 1.4.2. is:

yourappname/packages/Xamarin.Forms.1.4.2.6359/lib/Xamarin.iOS10/

Now, get these files somehow onto your Windows PC and drag and drop the assemblies onto ILSpy. They will show on the left side in the assembly list. You can now inspect the code of Xamarin.Forms.

If you ever wanted to know, where the UriImageSource saves the cache files, check out Xamarin.Forms.Core.dll/Xamarin.Forms/UriImageSource. After reading the code, you will know, that the files are stored in the Isolated Storage in a folder called “ImageLoaderCache”.

ILSpy

Modifying the Assembly and “fixing” InternalsVisibleToAttribute

The next step is to modify the Xamarin Forms assemblies. Two tools are required for this to work and both are provided directly by Microsoft. To disassemble the code into bytecode, ildasm is used, which is part of the Windows SDK. To convert the bytecode back into an assembly, ilasm is used, which is part of the .Net runtime.

I put together ildasm and two helper batch files as a download. The batch files are using the default locations of ilasm/ildasm. You can simply edit them and adjust the file path. The archive also contains a tool called “internalsvisibleto”, which outputs the bytecode required to make your own app assembly a friend of Xamarin Forms, so that you can access everything, which uses the internal access modifier.

Let’s start by adding code to be able to access the internal stuff. Download the mentioned zip archive, extract it and put the Xamarin.Forms.Core.dll into the directory, open the PowerShell and navigate to the directory. You can now disassemble it using the provided batch file.

PowerShell

Disassamble the assembly into bytecode

> disassemble.bat Xamarin.Forms.Core.dll

This will create the two files Xamarin.Forms.Core.il and Xamarin.Forms.Core.res, where the first file contains the bytecode. The other file can be ignored.

Generate the bytecode for the InternalsVisibleToAttribute

> internalsvisibleto.exe tickerfeed.iOS

The parameter is the name of your assembly. I am normally using shared projects, so that the shared code is directly compiled into the different platforms, therefore, I need to inject different assembly names, depending on the currently used platform. So, on iOS, I have to use “assemblyname.iOS”, on Android “assemblyname.Droid”, etc. (or whatever you specified in your project settings) If you’re using a PCL project, you should use the assembly name of the PCL.

Insert the output into the bytecode

IL Code

Convert the bytecode back into an assembly

> assemble.bat Xamarin.Forms.Core.dll

If everything worked, this creates the file Xamarin.Forms.Core.modified.dll. To check, if our modification worked, load the new assembly into ILSpy and look for the new InternalsVisibleTo attribute.

ILSpy

That’s all. You can now copy the modified assembly back into the correct packages folder, rename it and compile your project.

Editing the access modifiers

One more easy but powerful option is to edit the access modifiers of fields, properties, methods or classes. So, a field of a class, which is private, can easily be changed to protected, to be accessible in a subclass, or into internal, so you can access this field from anywhere in your code.

But be aware, the C# equivalent of protected and internal in bytecode are family and assembly.

Get a file stream for a cached UriImageSource

Let’s end this article with a simple example on how to use the knowledge of this article. For whatever reason, we want to get a Stream from an image downloaded via UriImageSource.

When you inspect the code of UriImageSource in ILSpy, you will probably code an extension method like this  to solve the problem.

	static class UriImageSourceExtension
	{
		public static Task GetImageStream(this UriImageSource imageSource)
		{
			string cacheKey = UriImageSource.GetCacheKey (imageSource.Uri);
			string path = Path.Combine (UriImageSource.cacheName, cacheKey);
			return UriImageSource.store.OpenFileAsync (path, Xamarin.Forms.FileMode.Open, Xamarin.Forms.FileAccess.Read);
		}
	}

But there are  two problems with this code. UriImageSource.GetCacheKey and UriImageSource.cacheName are private. You can’t access them and the code won’t compile.

Let’s change this. So, open again the bytecode of the Xamarin.Forms.Core.dll, add the InternalsVisibleTo attribute and change the modifiers of GetCacheKey and cacheName from private to assembly in the bytecode.

Assembly

Assembly

Assembly

Compile the bytecode and check the changes in ILSpy.

ILSpy

Now, when you copy over the newly created assembly, the code of the extension method compiles fine and is ready to be used.

 

This was, hopefully, a short and simple article to show you how to improve your Xamarin Forms knowledge by accessing the source code and modify it to fit your needs. I will add a few more examples and insights on how to improve the Xamarin Forms experience on my blog, so stay tuned.

Leave a Reply