# Thursday, October 23, 2008

20081014 028 I’m addressing the v2 question first. It is hard to know what to do in v1 if you don’t know where you want to end up when you go to build and deploy v2 (and later versions) of your product.

First, I'm going back to the three different product categories that I outlined earlier. I'm going to collapse the two different types of applications into just one category. Today's post is about applications. The next one will be about components.

Applications

For v2, you will want one of two situations:

  • my v2 application completely replaces my v1 version entirely, or
  • my v2 application installs side-by-side with v1 and the two versions do not affect or interact with one another

In most cases, I suspect that folks want the former. As an example, almost no one expects or wants to continue to use Office 2003 after installing Office 2007 on their machine. Something like TurboTax is the example that breaks that rule.

If you opt to install your application side-by-side with the older version of your application, then you might need to consider the safety of side-by-side versions of the various libraries that you depend on. The specific scenario that I’m imagining is that v2 of your application might be perfectly side-by-side with the v1 version, but the newer version of the MathLib library that you depend on might not be properly designed for a side-by-side install. Let's assume that you purchased MathLib from MathCorp, and that the versioning/servicing scheme of MathLib is different (naturally) than what you employ for the libraries that you build for your own application.

There are (at least) two obvious gotchas that I can imagine:

  • MathLib doesn’t install side-by-side, but always overwrites older library versions with newer versions, and the MathLib developers made a breaking change this release (oops!), or
  • MathLib reads mathematic constants from a file at a particular location, and the format of the file changed, but the location did not (oops!), which will break the old version

And to the other option, if you opt to replace the earlier version of your application, you don't risk breaking the earlier version of your app (since it is now gone), but you still have the risk that you might affect another app on the machine that relies on MathLib in the same ways described above.

What I’ve just described is essentially the well-known “dll hell” problem. I’d hope that most developers are familiar with this problem and have enough information to avoid it. However, it is sometimes non-obvious that you are about to walk off that cliff. This post is really intended to get folks to think a little more about that cliff.

This whole scenario assumes that MathLib is installed in some global fashion, either to the GAC, or to a global directory, such as "C:\Program Files\MathCorp\MathLib". If MathLib is installed with the application, in the application directory, then this whole problem goes away. At that point, you really are back to the monolithic application that I discussed earlier where the entirety of the application is installed and serviced uniformly, in time and location.

To be clear, I'm not trying to push developers toward private installs. There are downsides to approach that too. The meta-message is that you need to understand the potential impact of deploying your v2, and align your v2 goals around avoiding those impacts.

What's with the picture in the post? I recently purchased a digital SLR and am getting into photography as a hobby of sorts. The pic is just to add something extra to the post. I like this particular picture since there is a lot to look at visually, and because the focus is pretty narrow and well off center. It is also fun to see a child’s toy as an artifact to study.

Thursday, October 23, 2008 6:40:49 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [9]  | 
# Friday, September 26, 2008

Lately, I’ve been thinking about the overall .NET deployment and servicing story. There are a number of aspects of deployment that one can consider. The particular issues that are salient for your product depend a lot on the kind of code you deploy onto end-user machines.

First, let’s look at the different kinds of products that developers might build, and need to deploy:

-          Monolithic applications

o   A single exe that is not dependent on any libraries, other than the .NET Framework itself, or

o   Solely dependent on libraries that are serviced/upgraded with the application

-          Aggregate applications

o   Dependent (at least partially) on libraries (or controls or components) that are installed/serviced/upgraded separately (in ‘time’ and/or ‘location’) from the application

o   These libraries are likely sourced from a 3rd party

-          Components, controls or class libraries

o   You build assemblies that other developers reference in their applications (both ‘Monolithic’ and ‘Aggregate’ applications as described above) or other libraries

While we’re thinking broadly, one is left wondering what the critical questions are to think about for each of the deployment cases above. There are a lot of questions that one can imagine looking into. They range from .NET Framework and application deployment, to servicing multiple versions of your applications out in the wild, to safely deploying a new version of your API (class libraries) onto end-user machines w/o any adverse impact.

The first one that I’d like to look at is: “Am I building my product correctly for the future?” This question, though, really boils down into two important questions:

-          What do I need to think about when shipping v1 of my product?

-          What do I need to do to safely, correctly and seamlessly ship v2 of my product?

Those questions seem very simple, and they are on the surface. The answers are not overly complicated, but do require some up-front thought to ensure that you end up where you want to.

We have had folks come to us after a successful v1, not quite sure what to do for v2. You may be wondering what the crux of this is … you just hit ‘go’ on csc/vbc another time and you’re done, right? Maybe. It all depends on how you want your code to behave when you ship your second version.

Note that I call out v2 specifically (and not v3 and v4) since v2 is the first version after your initial one, and will be the time when you need to address any issues that come up with shipping again. Once you’ve got a plan in place, you’ll be able to rinse and repeat for each subsequent version (provided that you have a good plan).

In my next posts, I’ll answer this question for each of the product types listed at the top of the post.

Friday, September 26, 2008 5:57:56 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, November 29, 2006

I took another look at the FileVersion sample. I wish the API was actually a little different. The API actually makes sense as a general use API, but it isn't as user-friendly as I would like. I wish that there were an instance method on the Assembly class called "GetFileVersion" or something like that it took nothing and returned a Version class.

Here is more of less what it would look like, except that the GetFileVersion wouldn't be static, it wouldn't take anything and would be on the assembly class.

If you look @ the FileVersion class, there is a lot of stuff on there, and it is a super wonky API anyway. I don't understand why it has a single static method that more or less acts that the instance constructor. Why not just have a constructor that takes a string or a FileInfo or even a FileStream. Bad API design. I prefer the Version class a lot more since it is super simple. I

using System;
using System.Reflection;
using System.Diagnostics;
namespace FileVersion
{
class Program
{
static void Main(string[] args)
{
Assembly asm;
Version ver;

asm = Assembly.Load("mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
ver = GetFileVersion(asm);
Console.WriteLine(ver.ToString());
}

static Version GetFileVersion(Assembly asm)
{
FileVersionInfo versionInfo;
Version ver;

versionInfo = FileVersionInfo.GetVersionInfo(asm.Location);
ver = new Version(versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart);

return ver;
}
}
}

If you look @ the FileVersion class, there is a lot of stuff on there, and it is a super wonky API anyway. I don't understand why it has a single static method that more or less acts like an instance constructor. Why not just have a constructor that takes a string or a FileInfo or even a FileStream. Bad API design. I prefer the above method (for the assembly case) that returns the Version class since it is super simple. I realize that the native file version is a string, so can contain more stuff, but the 4-part version number is really all I want.

Wednesday, November 29, 2006 3:39:29 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

The file version number is a native code concept – meaning not originating from the .NET Framework. This version number is a resource found within the resource section of the windows PE (portable executable) format of a managed or native code dll. This resource is named “FILEVERSION”. This version number is used for information purposes only, not for any runtime purposes such as binding. In addition, this version number does not have to conform to a particular format, but is only a string, although it does typically takes the form of a simple four-part number (i.e. 1.2.3.4).

Reading the File Version

The easiest way to view this number is to view the properties of a file in Windows Explorer. The version number listed is the file version number. The product version is also listed, although I don’t know how the two numbers differ exactly. Naturally, you can access the file version from code. The following code does just that, largely using the System.Diagnostics.FileVersionInfo class, which I’ve never used before. In fact, I had to ask someone else on the loader team for that information.

using System;

using System.Reflection;

using System.Diagnostics;

namespace FileVersion

{

    class Program

    {

        static void Main(string[] args)

        {

            Assembly asm = Assembly.Load("mscorlib, Version=2.0.0.0, Culture=neutral,  PublicKeyToken=b77a5c561934e089");

            System.Diagnostics.FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location);

            Console.WriteLine(fvi.FileVersion);

        }

    }

}

 

Setting the File Version

The CLR provides an assembly-level custom attribute to set this version number for an assembly from managed code. This attribute is called System.Reflection.AssemblyFileVersion. You can see how to set it below.

using System;

using System.Reflection;

 

[assembly:System.Reflection.AssemblyFileVersion("2.3.4.5")]

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Console.WriteLine("I just set the file version!");

        }

    }

}

 

You can actually set this attribute in Visual Studio 2005 via the properties menu. In that case, you cannot set it in code, as I’ve done above, since you’ll then have two instances of the attribute. You only need to set the attribute directly, as I’ve done  above, if you are using the compiler directly, from the commandline.

Wednesday, November 29, 2006 12:00:36 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, November 28, 2006

Versioning and version numbers are always a bit confusing. For the CLR and the .NET Framework, we’ve got lots of version numbers to think about. I’d like to debunk any confusion around them, explain what each version number means, how to view it and how to set it (if appropriate). Let’s take a look …

The version numbers that I’m going to discuss are:

·         Native file version

·         Managed assembly version

·         Metadata version

·         Metadata format version

·         .NET Framework versions

I’m going to discuss these version numbers (and anything else that comes up) across the next few posts.

Tuesday, November 28, 2006 11:53:35 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, October 26, 2006

The CLR loader offers a few APIs for loading assemblies, each of which have a slightly different model and behaviour for how they go about the task. I'd like to cover those and hopefully set folks on the best path for using loading assemblies.

The APIs that I have in mind are:

  • Assembly.Load(), and
  • Assembly.LoadWithPartialName()

First, you need to understand the concepts of fully-specified and partial assembly names.  A fully specified name contains the following parts: the simple name, version, culture, and public key or public key token. A partial name contains at least the simple name and optionally any of the other parts of the full-specified name. This concept is most relevant to the textual identity of an assembly. For example, here is the fully-specified assembly textual identity for v2.0 System.dll: "System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089". The textual identity is a specially formatted string that is used to describe some or all of the aspects of identifying an assembly.

Assembly.Load() takes a string as one of the overloads. The textual identity, as you see formatted above, is just the sort of string that Load() is expecting. Load() also takes other types, such as AssemblyName, but that's not important for the moment. The textual identity can be fully or partially specified, and which one it is has an important impact on the way that these loader APIs operate. The one you see above for System.dll is fully specified, since all the parts are there. Note that there is an additional part (or attribute) of the textual indentity that I've missed -- ProcessorArchitecture -- but that complicates the discussion too much for not much benefit, so I'm not going to cover it here.

The following bit of code shows the use of these APIs with full- and partially-specified names. The comments inline should pretty well explain what to expect

using System;
using System.Reflection;

namespace BindingByIdentity
{
class Program
{
static void Main(string[] args)
{
Assembly asm;

//LoadWithPartialName() looks in both the appbase and the GAC, looking for the best match,
//where "best match" isn't very clearly defined
//In addition, this method has been deprecated, which means "don't use this anymore, and it may be removed later"
try
{
Console.WriteLine("Calling LoadWithPartialName");
asm = Assembly.LoadWithPartialName("System");
Console.WriteLine("Loaded: {0}",asm.FullName);
}
catch(Exception e)
{
Console.WriteLine("LoadWithPartialName threw with the following exception:");
Console.WriteLine(e.Message);
}


//Load() with a partial name looks only in the appbase
//There is no gaurantee that you will get the assembly that you want
//In the case that you have private bin paths specified, there are no order gaurantees
//This particular use of the Assembly.Load() will always throw, since this assembly will not be found (because it is GAC'd)
try
{
Console.WriteLine("Calling Load");
asm = Assembly.Load("System, PublicKeyToken=b77a5c561934e089");
Console.WriteLine("Loaded: {0}", asm.FullName);
}
catch(Exception e)
{
Console.WriteLine("Load threw with the following exception:");
Console.WriteLine(e.Message);
}

//Load() with a fully-specified name looks in the appbase and the GAC and gaurantees that you
//will get the assembly that you asked for
try
{
Console.WriteLine("Calling Load");
asm = Assembly.Load("System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
Console.WriteLine("Loaded: {0}", asm.FullName);
}
catch (Exception e)
{
Console.WriteLine("Load threw with the following exception:");
Console.WriteLine(e.Message);
}


}
}
}

Output:

Calling LoadWithPartialName
Loaded: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Calling Load
Load threw with the following exception:
Could not load file or assembly 'System, PublicKeyToken=b77a5c561934e089' or one
 of its dependencies. The system cannot find the file specified.
Calling Load
Loaded: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

The moral of this story is that Assembly.LoadWithPartialName() is always bad and shouldn't be used since its behaviour isn't well-defined or predictable, and that you should always fully-specify the textual identity strings that you pass into Assembly.Load(). It may be the case that partially specified strings "work" since the assemblies you are attempted to load are always going to be in the app-base, but the fully-specified names do make your code and your intentions more clear, particularly for the next guy that has to look at the code you wrote. The funny thing is that sometimes "the next guy" is you, just two months later ;)

There is also the case that your code might not be strong-named. You should still specify everything but the publickey/token in that case.

Thursday, October 26, 2006 7:43:59 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, June 21, 2006

I've heard "convention over configuration" a number of times, largely in reference to Ruby on Rails. It's actually a really interested concept. It is simple in nature, and those kind of concepts are always the best. As an aside, I think I'm done with complicated concepts at this point ;) The question is where does the CLR loader lie w/rt CoC and is that the correct place to be.

I can tell you right away that the loader and binder are way, way over on the side of configuration. And the default behaviour -- the behaviour you get before you need to get into configuration -- is only useful for basic scenarios. Let me offer an example if this is seeming a little too vague. So, you've got assembly A (A, Version=2.0.0.0) and then you want to service it, giving us A' (A, Version=2.0.0.1). That's all good and seems to make sense. It makes sense to increment the (managed) version number to differentiate the two versions of the assembly. The only problem is that apps bound to A (A, Version=2.0.0.0) will still load 2.0.0.0 and not 2.0.0.1 (assuming A and A' were in the GAC).

Hmmm ... where does that leave us, since that's not quite the behaviour (read:convention) that we wanted. Well, we get to configure. Here are the choices available to us: re-compile all apps out there with A', configure (app.config) all apps to bind to A' or generate and install publisher policy to centrally configure all apps to bind to A'. And there is one more option still available that differs from the rest: don't change the (managed) version number in the first place to better conform to the loader convention. Ughh!

Hmmm again ... none of that sounds too good. So, what does Microsoft do, since you'd think that they are going with the better option or maybe have some secret hidden option for their use alone. Well, we (for the most part) go with not changing the (managed) version number when we service our binaries, thereby conforming to the convention. Pretty crappy, eh?

The good news is that we're moving much more toward convention-based approaches for v3. There will still be plenty of configurability via XML, but the vast majority of cases should just fall out of convention. Yeahh!!

This sort of thing really does make you need to stop and think. Are the default behaviours correct? Do they cover enough of the common scenarios? Do they address those common scenarios deeply enough? There's nothing worse than software that covers a lot of scenarios, but only 50% deeply enough in each. 100% enough in each is where I'm headed.

Wednesday, June 21, 2006 9:30:11 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 

I've been running an early adopter program for a couple months now for a bunch of changes that we're making to the CLR binder/loader in the CLR v3 timeframe. The changes are related to the binding model, versioning, servicing and an add-in model. Also, just to be clear, this isn't .NET Framework v3.0 (AKA WinFX). This is the one after that. Here is the blurb that I sent folks who I thought might be interested. Please @ mail me if you are interested in participating.

We have been busy designing some exciting new changes to the CLR loader/binder. We'd like to start collecting feedback from customers about our change early, in order to ensure that the final product is rock solid. The first phase of the program is to validate that our changes are useful and to understand the compatibility of these changes. In order to collect this information, we’d like you to run two tools on your system so that we can learn more about how you – actually the managed apps you use – use the loader and GAC. The one tool is a tool that collects very basic information about the GAC, while the second tool is actually an instrumented CLR which logs data about how the loader, binder and domains are used. The next phases involve running a prototype version of the CLR that includes the new changes, but we’ll get to that later, as it comes available.

There are also a couple legal documents to sign, specifically an MSFT NDA and a license. I'm happy to discuss those if you want to go to that stage.

Wednesday, June 21, 2006 1:38:12 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, November 21, 2005

We’re now at least half way into this set of posts on factoring features in Visual Studio 2005. The more I’ve thought about getting this set of posts fully written up, the more I’ve found these features interesting. My team produced them, so that helps, but I believe that a lot of developers will end up needing these features as a part of their versioning plan.

 

AKA InternalsVisibleTo

Friend assemblies are a nickname for the InternalsVisibleTo attribute in the System.Runtime.CompilerServices namespace. Not much of a stretch from the name, the use of the attribute allows you to specify which assemblies (your friends) should have access to your internal types and members. Just to remind you, “internal” != “private” – “internal” == “internal”.

 

As you might guess, this attribute must be set at the assembly-level. You might hope, however, that you could set this attribute at a more fine-grained level. I can explain that more later.

 

Syntax

The attribute is set in the following way:

 

[assembly:InternalsVisibleTo(“MyOtherAssembly, PublicKey=4asdsadasdsd”)

 

Couple things … The constructor of the attribute -- remember attributes are just types -- takes a textual assembly identity. Next, you must specify at least an assembly name and optionally a public key token. You can specify a culture as long as it is neutral. You cannot specify a version. Some of this may be a surprise to you. I’ll explain that below.

 

Design Decision Points

In retrospect, I’m not sure that making the public key token optional was the absolute best idea, but it was probably was necessary for a lot of interesting scenarios. First, friend assemblies is not a security feature, in the sense that visibility is not a security feature. Friend assemblies is just about changing the usual bounds of visibility to something that is more convenient for developers in a lot of cases.

 

That all being said, friend assemblies are really best kept to strong-named assemblies. Weak- or simple-named are very easily spoof-able (by definition). As a result, someone can simply use a particular name for their assembly and then get access to the internal fields in a simply-named assembly from which you’ve granted friendship. Even if you only ship your code internally within your business, this still isn’t a great plan. Think about it for a while and you’ll probably begin to better understand why.

 

It probably mostly goes without saying, but strong-named assemblies can only have strong-named friends. Strong-named assemblies can only depend on strong-named assemblies, so this point is moot anyway.

 

I do feel strongly that disallowing the version number was the right thing to do. The reason for that is that CLR binding doesn’t always produce the version of an assembly that you expect. There are a lot of mechanisms that can be at play, such as publisher policy, binding-redirects and other binding/versioning changes that we have planned for the CLR v3. As a result, you cannot assume that an assembly that depends on you is always going to be the same. Remember, with friends, it isn’t that you are assuming that a dependency of yours is going to be a particular version, but an assembly that is dependent on you. We would have potentially made a different decision if friends was the opposite way around, being a statement about your dependencies, but it isn’t.

 

The ability to specify culture provided that it is neutral is mostly a red-herring. There is no harm to specifying it, which is why we allowed it, but it is best to think in general that culture isn’t allowed. The main reason here is that you should not have code in culture-specific assemblies.

Monday, November 21, 2005 6:46:14 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, October 04, 2005

It’s high time for the next installment in this series. I was planning on getting to it sooner, but I’ve had a bunch of crazy things going on to do with getting the .NET Framework integrated into Windows Vista and planning for the next couple versions of the .NET Framework. None of that is calming down, but it’s time to get out another installment none the less.

 

I took a quick and somewhat vague look at type forwarders in my previous post (on purpose). Some of you were likely satisfied by that level of information and others are likely interested in more detail. As the title of this post suggests, this is the place to get more detail. To best do this, I’ve created a simple – and not particularly useful – application to demonstrate how forwarders really work. I’ll include this app in my next post.

 

Here is the entire program (it is only the Main method):

 

using System;

using StringUtilities;

using MathUtilities;

 

namespace TypeForwarderApp

{

    class Program

    {

        static void Main(string[] args)

        {

            Int32 int1, int2, intSum, stringCharacterCount;

            String initialString, reversedString;

            String appMessage;

 

            appMessage = "This app doesn't really do anything useful";

            Console.WriteLine(appMessage);

 

            int1 = 16;

            int2 = 14;

 

            initialString = "Happy 30th Birthday Microsoft!";

 

            intSum = MathUtil.Add(int1, int2);

 

            reversedString = StringReverse.GetReversedString(initialString);

            stringCharacterCount = StringCharacterCount.GetCount(initialString);

 

            Console.WriteLine();

            Console.WriteLine("Initial String : {0}", initialString);

            Console.WriteLine("Reversed String: {0}", reversedString);

            Console.WriteLine("String Length  : {0}", stringCharacterCount);

            Console.WriteLine();

            Console.WriteLine("Integers: {0}, {1}", int1, int2);

            Console.WriteLine("Sum     : {0}", intSum);

            Console.WriteLine();

            Console.WriteLine(StringReverse.GetReversedString(appMessage));

 

 

        }

    }

}

 

Like, I said, the program doesn’t do anything useful. I included the birthday message given that two Fridays ago was the annual Microsoft company meeting at SafeCo Field in Seattle. The theme was “Beyond 30” celebrating the first 30 years of Microsoft, although the content was much more about the future than the past (which I imagine most folks there appreciated). The 25th anniversary meeting was much more of a history lesson. I think that was the one that Sinbad was at ;)

 

I’m going to concentrate on one type, the StringCharacterCount type. At the moment, it is part of the StringUtilties assembly. If we look at the MSIL code within TypeForwarderApp.exe, we’ll see that clearly.

 

This is the MSIL code within the exe that calls the static method GetString on the StringCharacterCount type:

 

  IL_0030:  call       int32 [StringUtilities]StringUtilities.StringCharacterCount::GetCount(string)

 

As you can see, the fact that StringCharacterCount lives within the StringUtilities assembly is pretty apparent. Actually, you could go as far as saying it is hard-coded. Hence the need for forwarders …

 

Well, I’m a typical developer and I’ve decided in my second version of StringUtilites and MathUtilites – I happen to own them both – that StringCharacterCount is really a math utility and less of a string utility. As a result, I’m going to move this cool type to the MathUtilities assembly.

 

I just moved it to the MathUtilties assembly. You can see that it is indeed part of the MathUtilities assembly via the MetaInfo (CTRL-M) view in ILDasm.

 

TypeDef #2 (02000003)

-------------------------------------------------------

      TypDefName: StringUtilities.StringCharacterCount  (02000003)

      Flags     : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100001)

      Extends   : 01000001 [TypeRef] System.Object

      Method #1 (06000003)

      -------------------------------------------------------

            MethodName: GetCount (06000003)

 

Notice that the StringCharacterCount type is still part of the StringUtilities namespace and not MathUtilties. This is for two reasons: the namespace is part of the type name and type forwarders do not change the type name in any way. Put another way, if I moved StringCharacterCount from the one assembly to the other *and* changed its namespace, then I would have a breaking change that even type forwarders could not mitigate.

 

This all makes sense, but we have yet to see the interesting part. What does the metadata within the new version of StringUtilties look like? Let’s take a look at the manifest (another ILDasm view) within the StringUtilties assembly. There are actually two additional directives that we didn’t have before.

 

.assembly extern MathUtilities

{

  .ver 1:0:0:0

}

.class extern forwarder StringUtilities.StringCharacterCount

{

  .assembly extern MathUtilities

}

 

StringUtilties now has a dependency on MathUtilties and there is this “.class extern forwarder” line for “StringUtilities.StringCharacterCount”. Clearly, we’ve hit our jackpot. The dependency on MathUtilities is clearly required, given that StringUtilities intends to forward to it. The second directive, the class directive, is a way to signal to the runtime that the assembly knows about the class, but that it is located at another location.

 

If we look further yet, we’ll see that there is an entry in ExportedType table.

 

ExportedType #1 (27000001)

-------------------------------------------------------

      Token: 0x27000001

      Name: StringUtilities.StringCharacterCount

      Implementation token: 0x23000002

      TypeDef token: 0x00000000

      Flags     : [NotPublic] [AutoLayout] [Class] [AnsiClass] [Forwarder]  (00200000)

 

We’re now looking at metadata instead of the MSIL view (the directives) above. The ExportedType table is generally used to publish all the types in child netmodules of an assembly – build a multi-module assembly and you’ll notice this first-hand. In the case of multi-module assemblies, the implementation token would point to the netmodule, not to a separate assembly. In the case of type forwarders, the implementation token points (naturally) to a separate assembly. We can easily prove that too. Look at the AssemblyRef entry for the MathUtilities Assembly below.

 

AssemblyRef #2 (23000002)

-------------------------------------------------------

      Token: 0x23000002

      Public Key or Token:

      Name: MathUtilities

      Version: 1.0.0.0

      Major Version: 0x00000001

      Minor Version: 0x00000000

      Build Number: 0x00000000

      Revision Number: 0x00000000

      Locale: <null>

      HashValue Blob:

      Flags: [none] (00000000)

 

Notice that its token (0x23000002)  matches the implementation token of our forwarded type (0x23000002). There you go. That’s it! Now you know everything I’m going to tell you about type forwarders.

Tuesday, October 04, 2005 4:18:21 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [2]  | 
# Wednesday, September 14, 2005

Type forwarders are an interesting feature. If you’ve never needed them, you’ve probably never thought about or imagined them. They were designed to allow developers to move types from one assembly to another without breaking existing code that was dependent on types living within a particular assembly. There are a bunch of reasons why you might want to move types around; however existing type references will make that problematic.

 

You may be thinking “I move types between assemblies all the time and nothing breaks”. That may be true if you are building applications that happen to have dependent assemblies. At the point that you build frameworks that other developers build apps on *and* you distribute those libraries as pre-compiled binaries (not as source) *and* you promote apps built against earlier versions of your framework to newer versions (say with publisher policy or binding re-directs) without re-compiling apps, then you can easily run into this problem.

 

The .NET Framework hits this scenario dead-on. Everett apps, when run on Whidbey (i.e. Whidbey-only machines), for example, run against the Whidbey .NET Framework libraries. That work without issue, but those Everett apps still expect types to be in the same place as they where in Everett, which in the case of System.String is mscorlib.dll. If we moved System.String to system.dll, for example, we’d break 100% of Everett apps run on Whidbey. Why? The CLR loader would no longer be able to resolve the type reference – [mscorlib]System.String -- stored in the app’s metadata, but instead would throw a System.TypeLoadException exception. The app wouldn’t like that and neither would it’s users ;)

 

Before anyone gets the wrong idea, we didn’t move System.String or any other existing types in Whidbey. I’m merely using the .NET Framework as an example of how framework developers could similarly run into this problem, which would be the precursor for needing type forwarders.

 

Enter type forwarders. Type forwarders are a new MSIL directive that essentially say “type x used to be in this assembly, but it is now in this other one. Maybe you should go look over there”. Let me just show you.

 

  1. Create type t1 in assembly asm1
  2. Create an app that uses [asm1]t1 (that’s just short-hand for saying type t1 that lives in assembly asm1)
  3. Run app.
    1. Loader loads asm1
    2. Loader loads t1
    3. Everything works
  4. Close app and move back to VS 2005.
  5. Move type t1 from asm1 to asm2 (these will be two different projects)
  6. Create a forwarder in asm1 that points to [asm2]t1
  7. Recompile asm1 and asm2. Do not recompile the app.
  8. Run app
    1. Loader loads asm1
    2. Loader notices forwarder directive in asm1, pointing to asm2
    3. Loader loads asm2
    4. Loader loads t1 (this time for asm2)
    5. Everything works

 

You do need to realize though that forwarders are really just a temporary crutch for older apps. Notice that we didn’t re-compile the app above. That’s actually the point of the whole scenario. We’re assuming that we don’t have the option of re-compiling the app, because we don’t own it – in this scenario, we own the framework, not the app. When the owner of the app does get the chance, we get the following list of activities:

 

  1. Re-compile app against the latest version of asm2
  2. Run new version of app
    1. Loader loads asm2
    2. Loader loads t1
    3. Everything works

 

Notice that we no longer visit the forwarder in asm1. When the app owner re-compiled the app, the compiler found t1 in asm2. As a result, we no longer needed the mis-step in asm1. Like I said earlier, forwarders are merely a crutch for apps compiled against older versions of your framework. Once the apps are re-compiled against the new version of your framework, the forwarder is no longer needed (for that app). Unfortunately, you’ll need to keep that forwarder in place for some time, as there are likely a whole host of other apps and add-ins to those apps still reliant on the forwarder to work properly.

 

My next post will deal with the details of forwarders and I’ll post some source that uses the feature.

Wednesday, September 14, 2005 8:30:12 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, September 13, 2005

My last “Wonders of Whidbey” series seemed to be quite well received, at least in terms of aggregator traffic, so I thought that I’d do another one. This time, I’d like to talk about the new factoring features in Whidbey. This topic has been bouncing around my head now for quite some time, so it was easy to choose. I also have the next WoW series picked out, but you’ll have to wait a little longer for that one.

 

I’d like to give you some insight into these features first. I’m a little vague on the birth (early 2003?) of these two features, since I was not on the CLR team yet, but working in another part of Microsoft at that time. I joined the team in November 2003. In early 2003, a subset of the CLR team (and a bunch of other folks from across the company) was working on some major improvements to managed code versioning. They had a particular take on the problematic parts of the current versioning system and had come up with quite an interesting solution. As part of that, the team was going to do some serious factoring (moving types between assemblies) of the .NET Framework, but that approach would have the negative effect of breaking pretty much 100% of existing applications since type references included the assembly identity. As a result, the team developed some cool factoring features in the CLR to mitigate those problems.

 

We’re no longer going down the same path with versioning as we thought we were back in 2003, but we’ve kept the factoring features in the product. We do have some other uses for these features, but they are not quite as far reaching as the earlier versioning plan. I think that Microsoft platform teams are realizing, more and more, that many of the engineering problems that they face are not unique to Microsoft, but are also faced by other software companies. That realization is actually the basis of the Visual Studio Team System products in a lot of ways. That’s a long way of saying that we’ve kept these factoring features in the product, believing that they will be useful to enough managed code developers out there. That being said, I don’t expect a lot of folks out there to use these features. In fact, I’m hoping that adoption of these features is left to niche scenarios, as I can imagine some folks painting themselves into some bad corners if they do not use them judicially.

 

The features in question are colloquially called “Friend Assemblies” and “Type Forwarders”. They have different names within the product, but I recommend using these names since they are accurate and easier for folks to grasp than InternalsVisibleTo and TypeForwardedTo. Learn more about them in the following posts in this series.

Tuesday, September 13, 2005 6:17:13 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, May 24, 2005

I need to post on the assembly version number for Whidbey again, for two reasons: (A) I never posted what it actually was, and (B) I posted some inaccurate data that I'd like to correct.

(A) is easy. The assembly version number for Beta 2 and RTM is: 2.0.0.0. This may seem to make sense for a version number, however for Everett it was 1.0.5000.0 -- not sure why that was chosen. 

(B) is slightly more complicated. I said in an earlier post that apps built on top of Whidbey Beta 1 (or earlier) would need to be recompiled. That is not true. They will work fine on Whidbey RTM without re-compilation. What will not work is Whidbey RTM apps running on the Whidbey beta 1 runtime -- which is presumably not a scenario for anyone.

(B) is true due to framework unification, which only applies to .NET Framework assemblies (i.e. System.Web.dll). The assembly binder basically does a quick test to determine if the version of a framework assembly is higher or lower than the one that ships with a given runtime -- meaning the runtime currently being run in the process. If it is higher, the load is rejected; if it is lower or equal, the referenced version number is ignored and the version that shipped with that runtime is loaded instead. We go through this algorithm to prevent Everett System.Web.dll running on Whidbey with Whidbey System.dll. Those configurations are not supported, so we prevent them from occuring.

Tuesday, May 24, 2005 12:05:18 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |