3.03.2010

Changing and Rebuilding assemblies to update referenced assemblies

Problem

How to change and rebuild a .NET assembly without having/acessing it's source code just so you can update it's references. Either because you now wish to use a different version of the referenced assembly, or the reference's public key token has changed or because some class has changed namespaces.

Motivation

The problem described above is definitely NOT one of the usual problemns we have to deal on a daily basis. So what motivated this need?
In this case, we're dealing with third party COM libraries. The problem is that the company developing these libraries has not yet released PIA for these libraries. So at some time we felt the urge to create our own normalized interops so that every developer uses the same interop. Otherwise we would have problems deploying all the applications using the different interops. In this process we've also decided to change the namespaces (for normalization purposes).
Recently I had the need to use an additional library (for which no normalized interop had been created). As usual I started by adding a reference to that COM library to my Visual Studio project. By adding a COM reference, Visual Studio automatically creates an interop assembly for us. However in this case, as this library references the other COM libraries previously mentioned, the generated interop wouldn't work, because itself had references to the other COM libraries using the original namespaces, which cannot be found in the other "normalized" interops we're using.
Hence the need to change this interop assembly so that it complies with the other normalized references.

Solution

Knowing that we don't have the source code, the only way of changing an assembly is disassemble it, change the IL code and reassemble it. Also, we'll also re-sign the assembly (which is definetly needed if you wish to install the assembly in the GAC (Global Assembly Cache). Here's the four steps to accomplish this task:


Step 1: Disassemble the assembly

For this first step you'll need to use MSIL Disassembler (also known as ildasm).
Just open a visual studio command prompt and run "ildasm". This will present you with the ildasm GUI. From the File menu, you should open the target assembly and choose "Dump" to create a text file with the assembly MSIL.
Alternatively, for a more direct approach, just run the command:
ildasm targetAssembly.dll /out=targetAssembly.il


Step 2: Change the IL accordingly
Now, it's where you really need to pay attention. This is the tricky part. Depending on what you're trying to achieve, you may have to change different stuff.
First, start by opening the newly generated text file on your favorite text editor.

The referenced assemblies should be among the first lines of the file. Each reference will look something like this:
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}

This of course is a reference to the main .NET DLL. Your references will have a different name (here it's "mscorlib"), a different public key token (herer it's "B7 7A 5C 56 19 34 E0 89"), and possibly a different version (here it's "4:0:0:0").
What you need to do next is look for the reference(s) you wish to change and update them accordingly. This can imply changing the name if you've changed the assembly name, changing the public key token if it has changed, or (most usually) the version if you want to bind against a different assembly version. Please note that the version in here is in the following format <major version>:<minor version>:<build number>:<revision>

Now, if you've changed either the assembly name or the assembly base namespace you'll have to do a few massive find and replaces. You'll have to change every type reference to the referenced assemblies you're targetting. Here's why: a simple call to Console.WriteLine, will show up in the MSIL as
IL_0011: call void [mscorlib]System.Console::WriteLine(string)

Look specifically at the "[mscorlib]System.Console" part. It's a reference to the type System.Console in the assembly "mscorlib". This means that type references to types in your target referenced assemblies will have a similar syntax, so if you've changed either the assembly name or the base namespace, you'll have to do a massive find and replace to align both dll's.

Notice that I said "base namespace", because if you do change several namespaces arbitrarly you may have multiplied the number of find and replaces to execute.


Step 3: Reassemble the assembly

Now that you've changed the MSIL to comply with the referenced assemblies, you'll have to reassemble your dll. For this you'll need to use MSIL Assembler (ilasm : the companion tool to the previously mentioned ildasm)

Once again open a visual studio command prompt and execute the command
ilasm /dll targetAssembly.il

This command will produce your brand new targetAssembly.dll


Step 4: Sign the assembly

For this last step we'll use the strong name utility (SN.exe). Also, you'll need an SNK file to sign the assembly. You're probably signing all your assemblies with the same SNK, so you shouldn't have a problem finding it. However if you need to create a new SNK, the command to execute is: "sn -k strongNameKey.snk"
I hope you haven't closed the VS command prompt, because you'll need it to execute the following command to sign your brand new assembly:
sn -Ra targetAssembly.dll strongNameKey.snk

Last but not least, to make sure you didn't forget to do anything, you should go fetch a nice cold beer. If you did forget anything, I'm sure it will come to you while you're drinking!

Sem comentários: