Browsing Posts published in January, 2008

As promised, Microsoft has made the source for the .NET Framework available for debugging purposes. You’ll need to be running Visual Studio 2008 and install this QFE (aka patch). Full instructions for how to enable .NET Framework source debugging can be found on Shawn Burke’s blog here. You can also read Scott Guthrie’s announcement here.

As luck would have it, I couldn’t get this working initially. There was no “Load Symbols” in the right-click menu of the call stack. (“Load Symbols” should appear right above “Symbol Load Information…”)

image

Double-clicking in my call stack on any frames in the .NET Framework:

image

resulted in:

image 

After some quick investigation and reading through Shawn’s troubleshooting section at the bottom of his post, I realized that the _NT_SYMBOL_PATH environment variable was overriding the settings in Visual Studio. (I had set up _NT_SYMBOL_PATH to the Microsoft Symbol Server for WinDbg.) The problem is that the symbols provided by the Microsoft Symbol Server have their source information stripped out.

To solve this problem, you have two options.

  1. Delete the environment variable and just set the symbol paths in Visual Studio and WinDbg independently as noted in Shawn’s blog post above.
  2. Add the Reference Source Symbol Server to _NT_SYMBOL_PATH. (This has the advantage that the setting is shared by all debugging tools, including Visual Studio and WinDbg.)

Regardless of which option you choose, first close all instances of Visual Studio 2008 and delete all the files in your local symbol cache. Visual Studio has no way of knowing which version of the symbols you have. So if you already have System.Web.pdb downloaded from the Microsoft Symbol Server – the PDB file without source information – you won’t be able to debug into System.Web.

To add/modify the environment variable:

  1. Right-click Computer… Properties…
  2. On Vista, click Advanced System Settings…
  3. Click on the Advanced tab, then Environment Variables…
  4. Click New… under System Environment Variables (or Edit… if _NT_SYMBOL_PATH is already defined).
    • Variable name: _NT_SYMBOL_PATH
    • Variable Value: SRV*c:\dev\symbols*http://referencesource.microsoft.com/symbols;SRV*c:\dev\symbols*http://msdl.microsoft.com/download/symbols
  5. Click OK three times.

Maybe I’m just dense, but it took me awhile to figure out the syntax for _NT_SYMBOL_PATH. Note that you have to separate symbol servers via a semi-colon. Specifying SRV*LocalCache*Server1*Server2 fails miserably. Symbols don’t download and no errors are shown. The four-part syntax is valid, but meant for caching symbols on your network for all developers to share. Local cache is on each developer’s box, Server1 is a fileshare on your network with read-write access for all developers, and Server2 is the actual public symbol server. If you specify a public symbol server as Server1 in the four-part format, symbol loading just fails silently. Use the semi-colon separated syntax noted above to specify multiple public symbol servers and everything works as expected.

The local symbol cache can be the same for all public symbol servers. It can be anywhere that you have read/write access, such as c:\symbols, c:\users\james\symbols, or – my preferred location – c:\dev\symbols.

You should be sure that the Reference Source Symbol Server is before the Microsoft Symbol Server. The order of symbol servers is the order of search. If your debugger doesn’t find the correct PDB file in the local symbol cache, it will check the first symbol server. If the first symbol server doesn’t have the appropriate PDB file, it will proceed to the second. So if you have the Microsoft Symbol Server first, you’ll be downloading the PDB files without source information.

Which brings me to my last point. Right now, source has been released for:

  • .NET Base Class Libraries (including System, System.CodeDom, System.Collections, System.ComponentModel, System.Diagnostics, System.Drawing, System.Globalization, System.IO, System.Net, System.Reflection, System.Runtime, System.Security, System.Text, System.Threading, etc.)
  • ASP.NET (System.Web, System.Web.Extensions)
  • Windows Forms (System.Windows.Forms)
  • Windows Presentation Foundation (System.Windows)
  • ADO.NET and XML (System.Data and System.Xml)

LINQ, WCF, Workflow, and others will be following in the coming months, according to Scott Guthrie. So if I debug WCF today, I’ll download symbols from the Microsoft Symbol Server without source information since the Reference Source Symbol Server won’t have the PDB files. When the PDB files are released, I won’t be able to debug the source until I delete the old PDB files without source information and force a re-download. You can do this by deleting the appropriate folder in your local symbol cache – in this case, the c:\dev\symbols\System.ServiceModel.pdb folder – or you can just delete the entire contents of your local symbol cache and re-download everything. If you’re not able to view source on something that you know is available, the easiest solution is to just clear out your local cache and let Visual Studio download the symbols again from the correct location. Downloading symbols over a broadband connection doesn’t take that long and is a lot faster than trying to troubleshoot which PDB files are causing you problems.

Happy Debugging!!!

A great new feature of Visual Studio 2008 is multi-targeting, which allows VS 2008 to compile for .NET 2.0, 3.0, or 3.5 simply by changing a project property.

ApplicationDesignerWindowPaneControl

You might be thinking, now I don’t have to keep Visual Studio 2005 and 2008 installed. I can just use Visual Studio 2008 for all my projects! Well, yes, but with one big proviso. Code targeting .NET 2.0 and written in VS2008 may only compile in VS2008! The reality is that multi-targeting changes Intellisense, the project templates, and the assemblies that you’re offered, but your code is still compiled using the C# 3.0 or VB9 compilers regardless of which .NET Framework version you target. Compiling a project targeting .NET 2.0 using VS2008 results in this output:

—— Build started: Project: Multitargetting, Configuration: Debug Any CPU ——
C:\Windows\Microsoft.NET\Framework\v3.5\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt /warn:4 /define:DEBUG;TRACE /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\Multitargetting.exe /target:exe Program.cs Properties\AssemblyInfo.cs

Compile complete — 0 errors, 0 warnings
Multitargetting -> c:\dev\Examples\Multitargetting\Multitargetting\bin\Debug\Multitargetting.exe
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

Note the path to csc.exe – C:\Windows\Microsoft.NET\Framework\v3.5. We’re using the C# 3.0 compiler that ships with .NET 3.5. (Don’t get me started about the renaming of WinFX to .NET 3.0 again. My friends at Microsoft already know how I feel about that one.) Generally this doesn’t matter because .NET 3.0 and .NET 3.5 are additive libraries to .NET 2.0. .NET 2.0, 3.0, and 3.5 all run on the CLR that shipped with .NET 2.0. (I originally read about this in Dustin Campbell’s C#2.5 post.)

So how do we get all the crazy goodness of lambda expressions, anonymous types, LINQ, extension methods, and more? In .NET 3.0, WCF, WPF, WF, and CardSpace were all just additive libraries on top of .NET 2.0. In .NET 3.5, the new features can be divided into two categories – additive libraries and compiler enhancements.

Feature Library Compiler
implicitly-typed locals (var)   X
lambda expressions   X
automatic properties   X
anonymous types   X
object and collection initializers   X
LINQ X X
Expression Trees X X
Extension Methods X* X

* See below

Any feature not requiring library support is just syntactic sugar provided by our compilers. If you look under the covers using Lutz Roeder’s Reflector, you’ll see that the compilers are generating the same old CLR 2.0-compatible MSIL as they always were.

Implicitly-Typed Locals

Let’s take a look at implicitly-typed locals using the “var” keyword:

var foo = "Hello, World!";
 
Reflector says! (Harkening back to Family Feud and Richard Dawson.)
 
string foo = "Hello, World!";
 
The compiler was able to infer the type of “foo” based on its usage. No magic MSIL instruction to infer type. It’s all in the compilers.

Lambdas

Let’s take a look at our new swanky lambda expressions:
 
Action<string> display = msg => Console.WriteLine(msg);
 
Reflector says!
 
Action<string> display = delegate(string msg) { Console.WriteLine(msg); }
 
Once again, it’s just compiler magic turning the terse lambda expression (that funky “params => body” syntax) into the anonymous delegate syntax that we’ve known and loved since .NET 2.0. (To be honest, that’s not quite what Reflector says. The C# compiler actually caches the anonymous delegate in a static field for performance reasons, but the code above is close enough for purposes of this discussion.)

Automatic Properties

What about automatic properties?

public string Hello { get; set; }

Reflector says!

[CompilerGenerated]
private string <Hello>k__BackingField;
[CompilerGenerated]
public string get_Hello()
{
    return this.<Hello>k__BackingField;
}
[CompilerGenerated]
public void set_Hello(string value)
{
    this.<Hello>k__BackingField = value;
}
Once again, more compiler magic.

Anonymous Types

var position = new { Lat=42, Long=42 };

Reflector says!

[CompilerGenerated, DebuggerDisplay(@"\{ Lat = {Lat}, Long = {Long} }", Type="<Anonymous Type>")]
internal sealed class <>f__AnonymousType0<<Lat>j__TPar, <Long>j__TPar> {
  // Compiler generated goo omitted for clarity
}

Compiler magic.

Object/Collection Initializers

Object and collection initializers allow us to create and initialize objects/collections in a single line. They only require that the object have a parameterless constructor.

Program program = new Program { Hello="World" };
 
Reflector says!
Program <>g__initLocal0 = new Program();
<>g__initLocal0.Hello = "World";
Program program = <>g__initLocal0;
 
The C# 3.0 compiler is changing our object initializer into a plain old object instantiation via the parameterless constructor followed by a bunch of property sets. Nothing else to see here. Move it along…

LINQ, Expression Trees, and Extension Methods

Each of these features requires library support from System.Core, a new assembly that ships with .NET 3.5. Without System.Core installed in the GAC, your code isn’t going to run. So you can’t use these features without .NET 3.5 installed on the client.

The one exception is extension methods. If you try to compile an extension method while targeting .NET 2.0, you will receive the following compile error:

Cannot define a new extension method because the compiler required type ‘System.Runtime.CompilerServices.ExtensionAttribute’ cannot be found. Are you missing a reference to System.Core.dll?

As noted by Jared Parsons here, you can simply define the attribute yourself:

namespace System.Runtime.CompilerServices {
    [AttributeUsage(AttributeTargets.Method)]
    public class ExtensionAttribute : Attribute {
    }
}

Extension methods now compile while targeting .NET 2.0.

N.B. If you target .NET 3.5 as well as .NET 2.0 with this code, you’ll end up with a duplicate definition warning under .NET 3.5 as noted by Dustin Campbell here. If you’re really going to use this trick, you should probably wrap the class in some conditional directives that omit the definition when compiled under .NET 3.5.

Multi-targeting Proviso

What about that proviso I mentioned at the beginning of this post? If everyone on your project is using VS 2008 and targeting .NET 2.0, then all is good. You can use all the new syntactic sugary goodness that the C# 3.0 compiler (and VB9 compiler – if you swing that way) provides for you. If some team members are still using VS 2005, you need to be careful to not introduce C# 3.0 language constructs as VS2008 will not provide feedback that you are using them even if you target .NET 2.0. The new constructs are sufficiently different enough that it’s unlikely that you’ll mistakenly use a lambda expression or automatic property, but it’s something to keep in mind. Besides your CruiseControl.NET server is using a NAnt script that compiles with the C# 2.0 compiler to keep everyone honest, right? If not, this is yet another reason you should set up a CI server for your project today…