# 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 did an interview with my buddy John Bristowe a while back. You can hear me largely talking non-sensically about the CLR and at Developer Night in Canada. If you read Joel Pobar's blog too, then you hear me taking that guy down. Sorry Joel.

I really like the program that John's got going there. I couldn't believe how fancy the intro sounded. I'm now trying to get other Canadians at Microsoft to do interviews with John. Let's see where that leads. I hope that we'll have something in the later summer, as John made it sound like he'd be taking the summer off.

Wednesday, June 21, 2006 10:51:08 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [5]  | 

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]  | 
# Tuesday, June 20, 2006

Well, I managed to get the blog back up -- now that my readership has hit zero. I haven't posted in 6 months and its been down for 3. That's a pretty impressive record. Hopefully most other parts of my life are in better shape.

The short version of why it was down was that my ISP moved to partial trust for security reasons and dasBlog requires full trust. They gave me lots of warning and I didn't do anything (read:my fault). I've since moved to thinkjot, which is a derivative version of dasBlog that fixes exactly those partial trust problems. Thanks go to the folks at thinkJot.

In the past six months, I've also moved to a new (to me) house and had a second child. It also turns out that my job is probably changing a fair bit (but in a good way and still on the CLR team). Excuses, excuses ...

Updated: This post in a .Net forum is awesome. If you call having a second child with lots of laziness and procrastination added in "abducted by aliens", then "yes".

Tuesday, June 20, 2006 9:02:13 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, December 11, 2005

I've been working on the CLR team now for slightly over 2 years (FYI: been at MSFT for 5.5). I've learned a great deal in that time about managed code (and native code too), new programming concepts, managing large teams (there are about 150 us now) and some other odds and sundries. I've also had access to early hardware from our friends at AMD and Intel. None of that surprises me now, nor would it have suprised me back in late 2003 (when I started). One concept that I have picked up that I didn't expect was around correctness. Maybe I just missed class the day they taught "correctness", but it was new to me, at least to the degree it is taken seriously on this team.

I took a look @ Wikipedia to see what it thought about correctness. The definition there flirts with what I think of correctness, but it isn't quite it. The closest bit on Wikipedia is this bit:

"In theoretical computer science, correctness of an algorithm is asserted when it is said that the algorithm is correct with respect to a specification. Functional correctness refers to the input-output behaviour of the algorithm (i.e., for each input it produces the correct output)."

If I can construe those two sentences into something slightly more precise, w/rt what I'm thinking ... An algorithm can be considered correct if it produces a result that is predictable (including predictably random) and satisfies the requirements of all of its users (including potential users) under all required scenarios (including potential scenarios).

Another way of thinking of this is not going half-way with an implementation for some method. You need to solve the entire problem from all angles. You need to consider the turkish i and the multiple sort orders of the thai character set (sorry:pdf link). You also have to consider proper locking semantics, avoiding race conditions and dead locks when called/exercised from multiple threads. One can all consider "not guaranteed to be thread safe" as another form of correctness, since the contract is specified up front. One could argue that stance though. You also have to ensure that you clearly specify the expected behaviour of operations, particularly when casting between data types. You also have to determine what should be done when you run out of stack, memory or other resources. What happens when the machine loses power in the middle of an operation. Are you in a consistent state when the machine comes back to life?

So, back to the CLR team ... I find that the concept of correctness comes up a lot in our design sessions and general conversations. The world, in a very real way, is building massive amounts of code across an infinite number of possibilies and scenarios on top of the runtime. It is impossible for each (or any) of us on the CLR team to understand all of those scenarios, let alone take them all into consideration at each step during our development. Instead, we try to learn about as many weird and wacky ones as possible to help us, but correctness is really the thing that guides us each day. It may well turn out that our platform is not feature-rich enough in some niche areas. That's fine, and will always be the case to some degree. Developers still need to write code ;) What isn't acceptable is that our platform operates incorrectly (lack of correctness) in some corner case (or worse yet, general case) scenario. Provided that we think hard and build correctness into every layer of our platform and line of code, we can be certain that the runtime will perform properly and correctly in those infinite number of possibilities and scenarios.

That's one of the reasons why I love working on this team ... not as much because I want to ensure that the runtime is correct in all cases, but because I find it incredibly interesting learning what the correct behaviour is in all those cases. Sometimes it is obvious, but other times it requires a lot of thought to understand why something is correct if done that way. And even more interesting yet is when you're the guy (or gal) who needs to determine what the correct behaviour is, and defend it to your peers. That sort of analysis and hard thinking definitely requires those extra brain cells and cycles that are not neccessarily used every day. And then you sleep really well the next night ;

Sunday, December 11, 2005 12:03:46 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, December 06, 2005

I posted almost a month ago about all the searches that I've been noticing on my blog about gacutil.exe. I believe I've determined the question that at least some of the folks are wanting to ask. Folks don't know where to get it.

gacutil.exe and other tools such as ildasm.exe are part of the .NET Framework SDK. The .Net Framework admin tool, which used to be part of the redist, is now part of the SDK.

There may be some confusion around gacutil in particular. It has never shipped with the redist, but apparently a servicing package of the redist in the Everett (v1.1) timeframe used it as part of its servicing logic (oops) and didn't properly clean up after itself (double oops). As a result, there may be developers out there that have gotten used to gacutil being in the framework directory. If that's the case, we're sorry for giving the wrong impression. gacutil is intended to be a developer-only tool, used as part of development.

Instead of using gacutil at deployment time (as our not-so-smart servicing package did), you should use MSI or code against the fusion APIs yourself (with the former being the preferred method). For more info, you might take a look through Junfeng's blog.

Happy GACing.

Tuesday, December 06, 2005 6:43:16 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [1]  | 
# Monday, December 05, 2005

I'm still on this anonymous delegates kick. Maybe I'm on the wrong team and should consider a move to the C# team??

I've seen references to the feature of anonymous delegates as both "anonymous delegates" and "anonymous methods". From a philosophical standpoint, both seem like bad names. It had been bothering me for a while, so I decided to ask my fellow CLR buddy, Joe, the question. Joe is a good person to go to since he's a lot smarter than me. Joe handles concurrency and threading for our team. Joe was doing such a good job at concurrency that they decided that he could handle another job ;)

Anyway, Joe agreed that both names are misleading. Here's what we came up with for what the features would be if you interpreted the names literally:

- anonymous delegates: the ability to define a delegate (the actual declaration) in a more dynamic manner, in which you do not need to statically define the signature/contract of the delegate up-front. Joel and I did a quick co-routines implementation on top of the framework last summer. My first thought was to use delegates. This failed pretty quickly given that you need to delegate signature to be something better than "public delegate Object[] CoRoutineDelegate(Object[])".

- anonymous methods: the ability to define and generate a method in a more dynamic manner, in which you do not need to statically define the signature/contact of the method up-front or care which class it sites on. All you really want here is some sort of handle/reference to that method that you can pass around and call through as needed.

The second definition here feels a lot more like what I've been calling anonymous delegates. In fact, that's more or less how this feature that we've been discussing actually works. I guess "anonymous methods" is really a better term.

Just for kicks, I decided to make a category on anonymous methods so that folks can link back to all the posts, since there are quite a few now. I'm hoping that this post was the last one on the subject, unless I get some interesting feedback from the C# team. With that, it's now time to get back to my factoring features in Whidbey series, which is in dire need of some attention.

Monday, December 05, 2005 7:07:09 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, December 03, 2005

I recently posted twice (one and two) on anonymous delegates. I guess they really caught my attention for some reason, even though I've known about them for a long time. The remaining question to close out this debate is whether anonymous delegates are the .Net Framework answer to closures. Another related topic is continuations. I'm certain that these are not that.

First, what is a closure?

Here is how wikipedia defines closures:  "Unlike garden-variety functions which retain no memory of what happened in previous calls, closures are capable of storing information across function calls."

Here is another explanation from guile, which is an open source scheme interpreter that I've never heard of before: "The concept of closure is the idea that a lambda expression "captures" the variable bindings that are in lexical scope at the point where the lambda expression occurs. The procedure created by the lambda expression can refer to and mutate the captured bindings, and the values of those bindings persist between procedure calls. "

My definition is (as of 5 mins ago): a closure is the outcome of capturing both the in-scope variables used by a block of code and the block of code itself to the end of preserving their relationship beyond the normal lifetime of those in-scope local variables. As a result, the local variables can be shared across all calls to the block of code (i.e. method). 

So, do anonymous delegates in C# v2.0 conform to the definition of lexical closures? That seems to be a matter of great debate. It is amazing when you start to look at a topic and then you realize that there has been a whole crowd to the party before you. Anonymous delegates in C# appear to be just such a topic. Brad appears to have been interested in the topic about a year ago. Brad takes the stance (at least at that time) that anonymous delegates are not closures. abhinaba also takes the same stance. Dan Muller takes the opposite opinion, believing that C# has implemented closures.

I've come to the conclusion that anonymous delegates are closures. If you look at my somewhat-in-depth explanation of anonymous delegates in my last post, you'll see that the way they work seems to conform to the three definitions above. In summary, the C# compiler generates a class on your behalf which contains both an instance method (which is your anonymous delegate) and fields that map directly to the in-scope variables that you access in the containing method. All access to these variables are wired up to these generated fields on the class. That generated class is in itself the closure. Being heap-allocated, it naturally can have a different lifetime than what the containing method has on the stack, given that stack-frames have a tendency to go way.

I have a mail out to the C# team asking them their opinion on this interesting subject. I'm curious to see how they'll respond.

Saturday, December 03, 2005 5:40:06 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [1]  | 

Here is a better example of using an anonymous delegate that proves, in my mind, that they are lexical closures. I sure hope my definition of closures is correct ;)

Here's the code. Please do guess what it does or better yet, compile and run it. The important aspect is to guess which values of "s" do or do not match.

using System;

using System.Threading;





namespace AnonDelegateTest2

{

class Program

{

delegate void FunkyDelegate();



static void Main(string[] args)

{

FunkyDelegate fd = GetDelegate();



for (Int32 i = 0; i < 10; i++)

{

fd();

}

}



static FunkyDelegate GetDelegate()

{

String s = DateTime.Now.Ticks.ToString();



FunkyDelegate fd = delegate()

{

Console.WriteLine("Entering fd");

Console.WriteLine(s);

s = DateTime.Now.Ticks.ToString();

Thread.Sleep(100);

Console.WriteLine(s);

Console.WriteLine("Leaving fd");

Console.WriteLine();

};



return fd;

}

}



}
Saturday, December 03, 2005 2:06:27 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  |