Released: Crockford Base32 Encoder

Now, doesn’t that just sound sexy? No, not really. I hear you.

Alas, I went and built it anyway.

crockfordbase32.codeplex.com

nuget.org/List/Packages/crockford-base32

Crockford Base32 lets you encode a number into an alphanumeric string, and back again.

Where it shines is in the character set it uses.

It’s resilient to humans:

  • No crazy characters or keyboard gymnastics
  • Totally case insensitive
  • 0, O and o all decode to the same thing
  • 1, I, i, L and l all decode to the same thing
  • Doesn’t use U, so a number like 519,571 encodes to FVCK instead
  • Optional check digit on the end

It’s great for URLs:

  • No funky characters that require special encoding
  • No plus, slash or equals symbols like base 64

It handles really big numbers. (Well, my implementation is limited to 18,446,744,073,709,551,615 but you could extend the algorithm even further just by changing the data type from ulong to something even bigger.)

Number Encoded Encoded with optional check digit
1 1 11
194 62 629
456,789 1CKE 1CKEM
398,373 C515 C515Z
3,838,385,658,376,483 3D2ZQ6TVC93 3D2ZQ6TVC935
18,446,744,073,709,551,615 FZZZZZZZZZZZZ FZZZZZZZZZZZZB

 

Don’t have too much fun now.

Making Internet Explorer Better for Developers

Here’s what my Start Bar looks like:

Screenshot of my start bar: Outlook, IE9, IE6, Firefox, Chrome, Safari, Opera, Visual Studio, Photoshop, Excel, MetroTwit, Communciator, Skype, Live Messenger

Specifically, you’ll notice that I have all the major browsers there as first class citizens. When I want to launch a browser, I just randomly move my mouse towards the bottom left of my screen and click which ever browser I hit first. As I write this post, I’m using four of them to browse the web. LastPass deals with my passwords and I don’t use bookmarks anyway.

I do this to maintain my knowledge of all the browsers, to help me be a better web developer.

For day-to-day browsing, this works fine. For development though, I find myself actively switching to Firefox just so I can use Firebug. On paper, Internet Explorer’s developer tools do everything Firebug does. In practice, I’ve just never liked them as much. Even with the current progress on consistent rendering, we need good development tools in all the browsers.

Over the last few days, I’ve been exclusively using the Internet Explorer 9 developer tools. I’ve noted each feature that I think needs work. Now, I want to add your feedback so that we can build a cohesive set of requests for the IE team. None of these suggestions will make it into the IE9 release, but now is a good time to get our feedback in before they commence planning for IE10.

General Browser

  • JS error dialog is annoying as hell – would be good to say “stop showing error dialogs” after the first one, and route the rest of the text to the console
  • Would be nice to be able to view JSON responses in the browser instead of getting a download prompt, just like XML

Console tab

HTML tab

  • Minor, but the HTML should really get renamed to DOM. It feels a bit silly telling people to go to the HTML tab when they want to see how an SVG is structured
  • Selecting an element in the DOM should outline it on the page
  • Can’t delete an element
  • Adding CSS properties in dev tools (should be able to do it from trace styles, not via attributes)
  • "Edit" button in DOM always seems to edit the wrong node
  • Typing style=" to add an attribute results in style="" which works against the habit
  • Rename "Trace Styles" to "Applied Styles" and make it the default
  • Rename "Style" to “Trace Styles” and make it second
  • Layout pane is too chunky – always have to make the dev tools bigger just to see it
  • With an active node in the DOM, need a way to get into the attribute editing via the keyboard only F2

Script tab

  • Whenever I hit “Start Debugging”, the dev tools undock and docking gets disabled. Seems unrelated and certainly annoying.

CSS tab

  • "This stylesheet cannot be viewed because its source is in a different domain than the page."

General dev tools

  • Why is there a "Clear Browser Cache" button in the HTML + CSS tabs? It’s already in the menu.
  • Why is Disable > Script not available? (It’s there, but greyed out.)
  • Consolas over Courier please.

What are you suggestions? Comment on this post.

I’m on the Microsoft campus in Seattle this week so I’ll be championing as many of these issues with the IE team as I can.

Announcing: one awesome Windows Phone 7 app + the Open Conference Protocol

This weekend, off the back of Web Directions South, the awesome conference team also organised the Amped hack day. Sponsored by Adobe, Microsoft, PayPal, the Powerhouse Museum and Yahoo, there were a number of coding challenges throughout the day.

Aaron Powell (@slace), Brendan Forster (@shiftkey) and I attended in the capacity of mentors for the Windows Phone 7 track. In the end though, the ratio of mentors to participants and one awesome idea threw us down the path of competing instead.

Here’s what we built:

I was talking at a mile a minute because we only had 3 minutes to pitch in the final. (You might notice me glancing at the stopwatch in my left hand.)

Lucky we decided to compete, because in the end we won the whole day! That means we’re off to Web Directions East in Tokyo next month.

We’ll progressively release resources once we polish it up a bit more, take the mirrors down and let the smoke clear out. In the mean time, start with http://openconferenceprotocol.org.

Yet Another Debugging Tale – Visual Studio Disappearing

Call me a nerd (that’s obvious!), but I find a good debugging tale like something of a geek murder thriller. Every issue has its own little debugging quirks. This blog post, and some of my previous ones, are posted to be both entertaining as well as educational. I don’t want to bore you to death with cdb or WinDbg documentation, but you might find some of the approaches useful in the future.

The Issue

This morning ScottGu announced NuPack, a package management solution for .NET.

Eager to try it out, I opened an existing solution, expanded a web application project, right clicked on the References node and chose Add Package Reference.

The dialog popped up for a second or so and then my entire VS shell just disappeared without a trace. No error. No crash dialogs. Nothing.

This happened reliably every time.

Note: This issue is now fixed in the latest source.

My Debugging Steps

I opened a fresh instance of VS, attached WinDbg, opened the solution in question, and expanded the project nodes.

Before opening the context menu, I set a pretty wide exception breakpoint:

 0:051> !soe -derived -create System.Exception 1 Breakpoint set 

Then resumed execution:

 0:051> g 

Next, I right clicked on the References node and clicked Add Packaged Reference.

Bam! Exception:

(1ee4.1144): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=2b542f64 edi=2b5fb018
eip=03458ddf esp=00457b30 ebp=00457b38 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
03458ddf 8b721c          mov     esi,dword ptr [edx+1Ch] ds:002b:0000001c=????????

Where is it?

0:000> !clrstack
OS Thread Id: 0x1144 (0)
Child SP IP       Call Site
00457b30 03458ddf NuPack.Dialog.Providers.OnlinePackagesProvider.IsInstalled(System.String)*** ERROR: Module load completed but symbols could not be loaded for NuPack.Dialog.dll

00457b40 03458dae NuPack.Dialog.Providers.OnlinePackagesItem.get_IsInstalled()
00458094 5ad921db [DebuggerU2MCatchHandlerFrame: 00458094] 
00458060 5ad921db [CustomGCFrame: 00458060] 
00458034 5ad921db [GCFrame: 00458034] 
00458018 5ad921db [GCFrame: 00458018] 
0045823c 5ad921db [HelperMethodFrame_PROTECTOBJ: 0045823c] System.RuntimeMethodHandle._InvokeMethodFast(System.IRuntimeMethodInfo, System.Object, System.Object[], System.SignatureStruct ByRef, System.Reflection.MethodAttributes, System.RuntimeType)
004582b8 55b1d689 System.RuntimeMethodHandle.InvokeMethodFast(System.IRuntimeMethodInfo, System.Object, System.Object[], System.Signature, System.Reflection.MethodAttributes, System.RuntimeType)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\4ff1f12a08d455f195ba996fe77497c6\mscorlib.ni.dll

0045830c 55b1d3d0 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo, Boolean)
00458348 55b1bfed System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
0045836c 55af63f8 System.Reflection.RuntimePropertyInfo.GetValue(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
00458390 55af63ac System.Reflection.RuntimePropertyInfo.GetValue(System.Object, System.Object[])
0045839c 52076f58 MS.Internal.Data.PropertyPathWorker.GetValue(System.Object, Int32)

[...]

At this stage there’s no managed exception yet, so I stepped out a few times until I got one:

0:000> gu
(1ee4.1144): CLR exception - code e0434352 (first chance)
'System.Exception hit'
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00457e40 ebx=00000005 ecx=00000005 edx=00000000 esi=00457eec edi=0027fbe0
eip=75d9b727 esp=00457e40 ebp=00457e90 iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
KERNELBASE!RaiseException+0x58:
75d9b727 c9              leave
0:000> !pe
Exception object: 2b613d1c
Exception type:   System.Reflection.TargetInvocationException
Message:          Exception has been thrown by the target of an invocation.
InnerException:   System.NullReferenceException, Use !PrintException 2b6125a0 to see more.
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131604
0:000> !pe 2b6125a0
Exception object: 2b6125a0
Exception type:   System.NullReferenceException
Message:          Object reference not set to an instance of an object.
InnerException:   <none>
StackTrace (generated):
    SP       IP       Function
    00457B20 03458DDF NuPack_Dialog_1b750000!NuPack.Dialog.Providers.OnlinePackagesProvider.IsInstalled(System.String)+0xf
    00457B30 03458DAE NuPack_Dialog_1b750000!NuPack.Dialog.Providers.OnlinePackagesItem.get_IsInstalled()+0x1e

StackTraceString: <none>
HResult: 80004003

A quick trip to Reflector shows this to be the culprit (NuPack source was only published after this debugging exercise):

public bool IsInstalled(string id)
{
    return (this.ProjectManager.LocalRepository.FindPackage(id) != null);
}

Either get_ProjectManager or get_LocalRepository is returning null.

I let the app crash, then setup the scenario again.

This time I caught a tighter exception:

0:061> !soe -create2 System.NullReferenceException
Breakpoint set
0:061> g

As expected, we got a hit in the same place:

0:000> !clrstack
OS Thread Id: 0x1634 (0)
Child SP IP       Call Site
00367ab0 027bd85f NuPack.Dialog.Providers.OnlinePackagesProvider.IsInstalled(System.String)*** ERROR: Module load completed but symbols could not be loaded for NuPack.Dialog.dll

00367ac0 027bd82e NuPack.Dialog.Providers.OnlinePackagesItem.get_IsInstalled()
00368014 5ad921db [DebuggerU2MCatchHandlerFrame: 00368014] 
00367fe0 5ad921db [CustomGCFrame: 00367fe0] 
00367fb4 5ad921db [GCFrame: 00367fb4] 
00367f98 5ad921db [GCFrame: 00367f98] 
003681bc 5ad921db [HelperMethodFrame_PROTECTOBJ: 003681bc] System.RuntimeMethodHandle._InvokeMethodFast(System.IRuntimeMethodInfo, System.Object, System.Object[], System.SignatureStruct ByRef, System.Reflection.MethodAttributes, System.RuntimeType)
00368238 55b1d689 System.RuntimeMethodHandle.InvokeMethodFast(System.IRuntimeMethodInfo, System.Object, System.Object[], System.Signature, System.Reflection.MethodAttributes, System.RuntimeType)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\4ff1f12a08d455f195ba996fe77497c6\mscorlib.ni.dll

0036828c 55b1d3d0 System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo, Boolean)
003682c8 55b1bfed System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
003682ec 55af63f8 System.Reflection.RuntimePropertyInfo.GetValue(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
00368310 55af63ac System.Reflection.RuntimePropertyInfo.GetValue(System.Object, System.Object[])
0036831c 52416f58 MS.Internal.Data.PropertyPathWorker.GetValue(System.Object, Int32)

[...]

I grabbed the native disassembly:

0:000> !u 027bd85f
Normal JIT generated code
NuPack.Dialog.Providers.OnlinePackagesProvider.IsInstalled(System.String)
Begin 027bd850, size 51
027bd850 55              push    ebp
027bd851 8bec            mov     ebp,esp
027bd853 57              push    edi
027bd854 56              push    esi
027bd855 8bfa            mov     edi,edx
027bd857 ff1570007e02    call    dword ptr ds:[27E0070h] (NuPack.Dialog.Providers.OnlinePackagesProvider.get_ProjectManager(), mdToken: 06000048)
027bd85d 8bd0            mov     edx,eax
>>> 027bd85f 8b721c          mov     esi,dword ptr [edx+1Ch]
027bd862 33c9            xor     ecx,ecx
027bd864 33d2            xor     edx,edx
027bd866 e8057b2e53      call    mscorlib_ni+0x245370 (55aa5370) (System.Version.op_Equality(System.Version, System.Version), mdToken: 06001487)
027bd86b 85c0            test    eax,eax
027bd86d 750e            jne     027bd87d
027bd86f 6a00            push    0
027bd871 8bd7            mov     edx,edi
027bd873 8bce            mov     ecx,esi
027bd875 ff153818f901    call    dword ptr ds:[1F91838h]
027bd87b eb18            jmp     027bd895
027bd87d 8bd7            mov     edx,edi
027bd87f 8bce            mov     ecx,esi
027bd881 ff15a82a7f02    call    dword ptr ds:[27F2AA8h] (NuPack.PackageRepositoryExtensions.FindPackagesById(NuPack.IPackageRepository, System.String), mdToken: 06000285)
027bd887 6a00            push    0
027bd889 6a00            push    0
027bd88b 8bc8            mov     ecx,eax
027bd88d 33d2            xor     edx,edx
027bd88f ff15142b7f02    call    dword ptr ds:[27F2B14h] (NuPack.PackageExtensions.FindByVersion(System.Linq.IQueryable`1<NuPack.IPackage>, System.Version, System.Version, System.Version), mdToken: 060001c5)
027bd895 85c0            test    eax,eax
027bd897 0f95c0          setne   al
027bd89a 0fb6c0          movzx   eax,al
027bd89d 5e              pop     esi
027bd89e 5f              pop     edi
027bd89f 5d              pop     ebp
027bd8a0 c3              ret

From this we can note that the crash is after the call to get_ProjectManager but before a call to System.Version.op_Equality.

Checking the eax register shows that the call to get_ProjectManager returned null:

0:000> r
eax=00000000 ebx=00465938 ecx=0046c824 edx=00000004 esi=00000043 edi=00004000
eip=5ad91984 esp=00465868 ebp=00465868 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
clr!StressLog::StressLogOn+0xa:
5ad91984 854508          test    dword ptr [ebp+8],eax ss:002b:00465870=00004000

How could this occur?

protected ProjectManager ProjectManager
{
    get
    {
        return this.PackageManager.GetProjectManager((Project) this.Project);
    }
}

public ProjectManager GetProjectManager(Project project)
{
    ProjectManager manager;
    this.EnsureProjectManagers();
    this._projectManagers.TryGetValue(project, out manager);
    return manager;
}

From Reflector, we can see that get_ProjectManager calls into GetProjectManager.

This latter methods ‘ensures’ that a dictionary is initialized, then tries to return a value from it.

If the EnsureProjectManagers() method logic is wrong at all, and TryGetValue returns null, the application crashes. If it is always expected that this method will return a value, TryGetValue(Project, out ProjectManager) should be replaced with a standard dictionary lookup. If it’s valid for this method to return null, then NuPack.Dialog.Providers.OnlinePackagesProvider.get_ProjectManager or NuPack.Dialog.Providers.OnlinePackagesProvider.IsInstalled(string) need to be updated to handle null values.

Let’s work out why it returned null. First up, lets find all PackageManager instances on the heap so we can interrogate them:

0:000> !dumpheap -type NuPack.ProjectManager
Address       MT     Size
2bceed0c 21187e4c       52     
2bcf7450 21189858       60     
total 0 objects
Statistics:
      MT    Count    TotalSize Class Name
21187e4c        1           52 System.Collections.Generic.Dictionary`2[[EnvDTE.Project, NuPack.VisualStudio],[NuPack.ProjectManager, NuPack.Core]]
21189858        1           60 System.Collections.Generic.Dictionary`2+Entry[[EnvDTE.Project, NuPack.VisualStudio],[NuPack.ProjectManager, NuPack.Core]][]
Total 2 objects

Out of these two, the first one is the interesting one:

0:000> !do 2bceed0c
Name:        System.Collections.Generic.Dictionary`2[[EnvDTE.Project, NuPack.VisualStudio],[NuPack.ProjectManager, NuPack.Core]]
MethodTable: 21187e4c
EEClass:     558b99ac
Size:        52(0x34) bytes
File:        C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
55b82938  4000bd3        4       System.Int32[]  0 instance 2bcf7438 buckets
56320924  4000bd4        8 ...non, mscorlib]][]  0 instance 2bcf7450 entries
55b82978  4000bd5       20         System.Int32  1 instance        3 count
55b82978  4000bd6       24         System.Int32  1 instance        3 version
55b82978  4000bd7       28         System.Int32  1 instance       -1 freeList
55b82978  4000bd8       2c         System.Int32  1 instance        0 freeCount
55b82fd8  4000bd9        c ...Canon, mscorlib]]  0 instance 2bceed70 comparer
55b7568c  4000bda       10 ...Canon, mscorlib]]  0 instance 00000000 keys
55b7b730  4000bdb       14 ...Canon, mscorlib]]  0 instance 00000000 values
55b7f5e8  4000bdc       18        System.Object  0 instance 00000000 _syncRoot
55b7b794  4000bdd       1c ...SerializationInfo  0 instance 00000000 m_siInfo

The count is correct. (My solution has three projects.)

This indicates that the dictionary context is correct, but that we’re looking for the wrong key.

This is where the lookup key comes from:

protected ProjectManager ProjectManager
{
    get
    {
        return this.PackageManager.GetProjectManager((Project) this.Project);
    }
}

protected Project Project
{
    get
    {
        return Utilities.GetActiveProject(this.DTE);
    }
}

public static Project GetActiveProject(_DTE dte)
{
    Project project = null;
    if (dte != null)
    {
        object activeSolutionProjects = dte.ActiveSolutionProjects;
        if (((activeSolutionProjects != null) && (activeSolutionProjects is Array)) && (((Array) activeSolutionProjects).Length > 0))
        {
            object obj3 = ((Array) activeSolutionProjects).GetValue(0);
            if ((obj3 != null) && (obj3 is Project))
            {
                project = (Project) obj3;
            }
        }
    }
    return project;
}

In the last method, there are a whole host of scenarios that would cause it to return null. This will then cascade through GetProjectManager(Project) and get_ProjectManager before resulting in the NullReference exception we saw in IsInstalled(string). Either GetActiveProject(_DTE) should be updated to fail fast when the active project cannot be determined, or IsInstalled(string) needs to be updated to handle this scenario.

Which scenario caused GetActiveProject(_DTE) to return null for me?

Interrogating the _DTE instance was beyond me because it’s a COM object. By this point I had a pretty good idea though so trial and error got me the rest of the way.

My Diagnosis

The traditional Solution Explorer tool window needs to be opened at least once between opening a solution and trying to add a package reference. If you’re exclusively using the Solution Navigator tool window from the power tools, your VS will disappear as soon as you try to add the package reference.

My Repro Steps

  1. Open Visual Studio
  2. Close the Solution Explorer tool window
  3. Open the Solution Navigator tool window (the one from the power tools)
  4. Open your .sln
  5. In the Solution Navigator, expand a project
  6. Right click the ‘References’ node
  7. Click ‘Add Package Reference’
  8. Boom!

I hope you found this story interesting for the mystery factor, as well as showing a few debugging techniques that I use. 🙂

Talk Resources – Internet Explorer 9 for Developers

At REMIX10, TechEd AU 2010 and TechEd NZ 2010 I’ve been showing some of what’s new in Internet Explorer 9 for developers.

Here are the slides and code: http://db.tt/JvEUu3o

The recording from TechEd New Zealand (the third and best version!) is available here: http://www.msteched.com/2010/NewZealand/WEB304

IE9NZ

The recording from TechEd Australia (version 2 of the talk) is available here: http://www.msteched.com/2010/Australia/WEB204

IE9AU

And finally, here’s a recording from REMIX10 Australia (version 1 of the talk): http://www.microsoft.com/australia/remix/videos/default.aspx

IERemix

If you’ve attended any of these talks, thank you for your feedback! The session evals at conferences are like crack for speakers. We read every single one, and then we read them again.

— Tats

Scoop! C#’s new #until directive

(Disclaimer: This post is about a C# language feature I’d like to see, not one that actually exists. Once the feature gets added, the title will be accurate and I’ll be able the world’s most pro-active blogger. :))

Update 1: Added another approach

I’m trying to evolve a framework here at my current client. There are some 30+ solutions and an unknown (to me) number of developers dependent upon this framework. As such, I can’t go and make breaking changes without everybody’s CI build dieing and me getting escorted from the building.

Even when I do have access to all the code in one solution, I prefer a three pass approach of:

  1. implementing new functionality and bridging old functionality but marking it obsolete
  2. cleaning up all the build warnings triggered by the [Obsolete] attributes
  3. going back and deleting the obsolete code now that nothing depends on it anymore

Starting with that approach, I have some code like this:

[Obsolete("Use SomeOtherProperty instead.")]
public SomeType SomeProperty { get; set; }

public SomeOtherType SomeOtherProperty { get; set; }

In theory, each team will then drive down their build warnings over time.

In practice, I’ve never seen people do this very excitedly and I have no hope of driving all these down myself.

What I want to do is offer a fixed grace period something like this:

[Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
public SomeType SomeProperty { get; set; }

public SomeOtherType SomeOtherProperty { get; set; }

This gives each team a known grace period to update their usages, and then forces them after that. (It’s basically two release cycles.)

The problem is that I want to have these members die automatically once this date arrives. (I may or may not be here, etc.)

Approach #1

I came up with this:

#until 2010-08-01
    [Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
    public SomeType SomeProperty { get; set; }
#enduntil

public SomeOtherType SomeOtherProperty { get; set; }

Basically, that code block is only compiled up until 1st August. As soon as that date ticks around, the member is magically nuked from the build and the lazy downstream users start getting compile errors. The simple syntax of this also makes it easy for me to run some PowerShell + regular expressions over the framework codebase on a regular basis and remove the actual source code.

Unfortunately, C# doesn’t include the #until directive yet and I doubt Anders is going to give me a custom compiler build any time soon. 🙂

Approach #2

My next idea was to create a numeric version of the date and then using a basic conditional compilation directive:

#if DATESERIAL < 20100801
    [Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
    public SomeType SomeProperty { get; set; }
#endif

public SomeOtherType SomeOtherProperty { get; set; }

I’d then include something in the build script that adds the current date as a symbol (eg. csc.exe /define:DATESERIAL=20100801).

Unfortunately, symbols are just symbols (duh) and thus don’t have values. Also, the pre-processor ‘expressions’ used in #if only support basic boolean expressions.

Approach #3

My next idea was to make the dates less granular and define a series of symbols for the last 3 months or so. For example, a build run today 17th June 2010 would be executed like so – with a symbol for April, May and June:

csc.exe /define:OBSOLETE_201004;OBSOLETE_201005;OBSOLETE_201006

The code could then look like this:

#if OBSOLETE_201005
    [Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
    public SomeType SomeProperty { get; set; }
#endif

public SomeOtherType SomeOtherProperty { get; set; }

As soon as September ticks around, the OBSOLETE_201005 symbol will fall off the list and voila, the member dies.

This approach is basically flagging the date that we marked something obsolete (in this example, May 2010) and then allowing the build process to determine which ones are in and which ones are one.

I don’t like the approach for a few reasons:

  • it means that the directive isn’t as clear (it’s the date we indicated the change, not the date it’s going to take effect)
  • the message in the attribute can potentially become wrong (say we decided to include four months’ worth of obsolete changes instead of three, all the messages will now be out by one month)
  • all of the members are now forced on to the same attrition cycle – I can’t spread the ‘easier’ ones on to one cycle and the ‘harder’ ones on to a longer cycle

Approach #4

Let’s go back and evolve the syntax from approach #1:

//#until 2010-08-01
    [Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
    public SomeType SomeProperty { get; set; }
//#enduntil

public SomeOtherType SomeOtherProperty { get; set; }

All I’ve done is add the comment indicator to the start of each of the directives so that they still look like directives but the compiler doesn’t try and process them.

I was already planning to have a PowerShell script that I could use to find the stale code after it had passed its used by date. Keeping the syntax simple makes it easy to find the blocks via regular expressions, so this would be quite easy to do.

I could run this same script at the start of the build:

  1. Create a workspace for the build
  2. Run the PS script across it to remove any expired code
  3. Run the compiler

The problem with this approach is that it clobbers your code. This works fine on a build server where you’re creating a new workspace for every build. It doesn’t work so well in your local environment, and that’s just yucky. This doesn’t affect the downstream consumers (they only get binaries) however it kind of sucks for the framework team.

Approach #5

(This is inspired from Simon’s response.)

Bringing this all back into C#, we can move the onus on to the framework team. First up, lets add a custom attribute to the member:

[ValidUntil(2010, 08, 01)]
[Obsolete("Use SomeOtherProperty instead. This member will be removed on 1st Aug 2010.")]
public SomeType SomeProperty { get; set; }

public SomeOtherType SomeOtherProperty { get; set; }

Now, the framework build could include a unit test that uses reflection to find all the instances of this attribute and evaluate the dates. If the date is in the past, the unit test fails and the framework build fails. The framework team would then identify the build break and delete the now expired code.

Approach #6

Feel free to suggest. 🙂

Talk Resources – Riding the Geolocation Wave

At both the REMIX10 conference in Melbourne, Australia and more recently TechEd New Zealand I presented on geolocation for developers.

This was the abstract:

It’s pretty obvious by now that geolocation is a heavy player in the next wave of applications and APIs. Now is the time to learn how to take advantage of this information and add context to your own applications. In this session we’ll look at geolocation at every layer of the stack – from open protocols to operating system APIs, from the browser to Windows Phone 7. Building a compelling geo-enabled experience takes more than simple coordinates. In this session Tatham will introduce the basics of determining a user’s location and then delve into some of the opportunities and restrictions that are specific to mobile devices and their interfaces.

The talk was filmed at TechEd New Zealand, and is available for download here: http://www.msteched.com/2010/NewZealand/WEB205

(Note: this version has a Windows Phone 7 demo in it too.)

GeoNZScreenshot

The first version of the talk was also filmed at REMIX10, and is available for download here: http://www.microsoft.com/australia/remix/videos/default.aspx

GeolocationScreenshot

Here are some links to the code and resources (but you really want to watch the talk first):

(Post last updated 7th Sep 2010 with new links and videos)

Web Forms Model-View-Presenter on Hanselminutes

Over the last few months Damian Edwards and myself have been spending quite a bit of time building out a Model-View-Presenter framework for ASP.NET Web Forms.

Until now we’ve been pretty quiet about it all on our blogs because we were busy polishing off v1 and trying to get all the documentation in order. Nevertheless, the word has definitely started to spread as Scott Hanselman interviewed me about the library on this week’s Hanselminutes episode.

Listen to the podcast

Learn more about the library

Custom Code Analysis Rules in VS2010 (and how to make them run in FxCop and VS2008 too)

Back in 2002 Microsoft released FxCop, a static code analysis tool. At the time it was shipped as a separate product and received a bit of buzz. It used .NET reflection and a series of pre-defined rules to detect and report coding issues that wouldn’t normally be picked up by the compiler. Since this initial release, FxCop has undergone an amazing amount of work and become more mainstream with its integration into Visual Studio under the title of ‘Code Analysis’.

Recently I’ve been developing some custom extensions to FxCop – my own code analysis rules. While extremely powerful, this isn’t yet a fully documented or supported scenario. Until it is, this post shows you how to do it all.

Why should we care?

Lately I’ve been working on the ASP.NET Web Forms Model-View-Presenter framework. It’s not quite ready for launch yet, which is why I haven’t been blogging about it, but it is already in use by a number of high traffic websites. As more and more people have started to adopt the project in its relative infancy, documentation hasn’t been up to standard. To try and keep everybody in line I contemplated writing up some ‘best practices’ documentation but then figured that this probably wouldn’t get as much attention as it should and had a high chance of rapidly becoming stale.

Code analysis rules were the perfect solution. They would allow me to define a series of best practices for use of the library in a way that could be applied across multiple projects by the developers themselves. Code analysis rules are also great because they produce a simple task list of things to fix – something that appeals to developers and managers alike.

Over the course of developing these rules I’ve increasingly come to realise that custom rules are something that should be considered in any major project – even if it’s not a framework that will be redistributed. All projects (should) have some level of consistency in their architecture. The details of this are often enforced through good practice and code reviews, but from time to time things slip through. In the same way that we write unit tests to validate our work, I think we should be writing code analysis rules. Think of them like an “architectural validity test” or something.

The Basics

A quick note about versioning: First we’ll create some rules in VS2010, to be executed in VS2010. Later in the post we’ll look at how to compile these same rules in a way that makes them compatible with FxCop 1.36 (and thus VS2008). If you’re only targeting VS2008 then all the same concepts will apply but you’ll be able to skip a few steps.

  1. Start with a new class library project. Make sure you choose to target “.NET Framework 4”, even if the rest of your solution is targeting an earlier framework. Because we’re going to be loading these rules inside VS2010, and it uses .NET 4.0, we need to use it too.

    New Class Library using .NET Framework 4 

  2. Add references to FxCopSdk.dll, Microsoft.Cci.dll and Microsoft.VisualStudio.CodeAnalysis.dll. You’ll usually find these in C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop, or an equivalent location. Don’t worry about copying these libraries to a better location or anything – we’ll look at a smarter way of referencing them shortly. (If you’re doing this in VS2008, you’ll need to download and install FxCop 1.36 first and then find these references in that folder. Also, you’ll only need the first two.)
  3. Add a new XML file to your project called Rules.xml. This will be a manifest file that describes each of our individual rules. To get us started, paste in the following content:

    <?xml version="1.0" encoding="utf-8" ?>
    <Rules FriendlyName="My Custom Rules">
      <Rule TypeName="AllTypeNamesShouldEndInFoo" Category="CustomRules.Naming" CheckId="CR1000">
        <Name>All type names should end in 'Foo'</Name>
        <Description>I like all of my types to end in 'Foo' so that I know they're a type.</Description>
        <Url>http://foobar.com</Url>
        <Resolution>The name of type {0} does not end with the suffix 'Foo'. Add the suffix to the type name.</Resolution>
        <MessageLevel Certainty="95">Warning</MessageLevel>
        <FixCategories>Breaking</FixCategories>
        <Email />
        <Owner />
      </Rule>
    </Rules>
    

    This XML file is pretty self-explanatory, but there are a few things I should point out:

    The type name needs to match the name of the class that we define the actual rule in, so make it appropriate (don’t use special characters, use Pascal casing, etc).

    The check id must be unique within the namespace of your rules, but really should be unique across the board. Microsoft uses the letters “CA” followed by a four digit number, and we use a similar scheme for Web Forms MVP.

    The resolution message is stored in the XML here, and not in your own code, but you want it to be as specific as possible so that the developer on the receiving end of it knows exactly what they need to do. Use it like a formatting string – you’ll soon see that it works really nicely.

  4. Go to the properties for the XML file and change the Build Action to EmbeddedResource so that it gets compiled into our DLL.

    Build Action: Embedded Resource

  5. Create a class called BaseRule and paste in the following code:

    using Microsoft.FxCop.Sdk;
    
    public abstract class BaseRule : BaseIntrospectionRule
    {
        protected BaseRule(string name)
            : base(
    
                // The name of the rule (must match exactly to an entry
                // in the manifest XML)
                name,
    
                // The name of the manifest XML file, qualified with the
                // namespace and missing the extension
                typeof(BaseRule).Assembly.GetName().Name + ".Rules",
    
                // The assembly to find the manifest XML in
                typeof(BaseRule).Assembly)
        {
        }
    }
    

    There are three pieces of information we’re passing into the base constructor here. The first is the type name of the rule which the framework will use to find the corresponding entry in the manifest, the second is the namespace qualified resource name of the manifest file itself and the last is the assembly that the manifest is stored in. I like to create this base class because the last two arguments will be the same for all of your rules and it gets ugly repeating them at the top of each rule.

  6. Create a class called AllTypeNamesShouldEndInFoo and paste in the following stub code:

    using Microsoft.FxCop.Sdk;
    using Microsoft.VisualStudio.CodeAnalysis.Extensibility;
    
    public class AllTypeNamesShouldEndInFoo : BaseRule
    {
        public AllTypeNamesShouldEndInFoo()
            : base("AllTypeNamesShouldEndInFoo")
        {
        }
    }
    

That’s all of the boilerplate code in place. Before we start writing the actual rule, let’s take a brief detour to the world of introspection.

Um … ‘introspection’?

The first version of FxCop used basic .NET reflection to weave its magic. This approach is relatively simple, familiar to most developers and was a quick-to-market solution for them. As FxCop grew, this approach couldn’t scale though. Reflection has two main problems: First and foremost, it only lets you inspect the signatures of types and members – there’s no way to look inside a method and see what other methods it’s calling or to identify bad control flows. Reflection also inherits a major restriction from the underlying framework – once loaded into an app domain, and assembly can’t be unloaded. This restriction wreaks havoc in scenarios where developers want to be able to rapidly rerun the tests; having to restart FxCop every time isn’t the most glamorous of development experiences.

At this point we could fall back to inspecting the original source code, but that comes with a whole bunch of parsing nightmares and ultimately ties us back to a particular language. CIL is where we want to be.

Later versions of FxCop started using an introspection engine. This provided a fundamentally different experience, light-years ahead of what reflection could provide. The introspection engine performs all of its own CIL parsing which means that it can be pointed at any .NET assembly without having to load that assembly into the runtime. Code can be inspected without ever having the chance of being executed. The same assembly can be reloaded as many times as we want. Better yet, we can explore from the assembly level right down to individual opcodes and control structures through a unified API.

Jason Kresowaty has published a nice write up of the introspection engine. Even cooler yet, he has released a tool called Introspector which allows us to visualise the object graph that the introspection engine gives us. I highly recommend that you download it before you get into any serious rules development.

Introspector

Back to our rule…

Now that we know some of the basics of introspection, we’re ready to start coding our own rule. As a reminder, this is what we have so far:

using Microsoft.FxCop.Sdk;
using Microsoft.VisualStudio.CodeAnalysis.Extensibility;

public class AllTypeNamesShouldEndInFoo : BaseRule
{
    public AllTypeNamesShouldEndInFoo()
        : base("AllTypeNamesShouldEndInFoo")
    {
    }
}

The FxCop runtime manages the process of ‘walking’ the assembly for us. It will visit every node that it needs to, but no more, and it’ll do it across multiple threads. All we need to do is tell the runtime which nodes we’re interested in. To do this, we override one of the many Check methods.

As much as possible, use the most specific override that you can as this will give FxCop a better idea of what you’re actually looking at and thus provide better feedback to the end user. For example, if you want to look at method names don’t override Check(TypeNode) and enumerate the methods yourself because any violations you raise will be raised against the overall type. Instead, override Check(Member member).

In our scenario, because we want to check type names, we’ll override Check(TypeNode type).

The actual code for this rule is quite simple:

public override ProblemCollection Check(TypeNode type)
{
    if (!type.Name.Name.EndsWith("Foo", StringComparison.Ordinal))
    {
        var resolution = GetResolution(type.Name.Name);
        var problem = new Problem(resolution, type)
                          {
                              Certainty = 100,
                              FixCategory = FixCategories.Breaking,
                              MessageLevel = MessageLevel.Warning
                          };
        Problems.Add(problem);
    }

    return Problems;
}

All we’re doing here is checking the name of the type, and then adding a problem to a collection on the base type. The GetResolution method acts like string.Format and takes an array of parameters then formats them into the resolution text we defined in the XML file.

The second argument that we pass to the Problem constructor is the introspection node that the problem relates to. In this case it’s just the type itself, but if we were doing our own enumeration then we would pass the most specific node possible here so that FxCop could return the most accurate source reference possible to the end user.

Let’s start ‘er up.

At the time of writing, the latest standalone version of FxCop is 1.36 which still targets .NET 2.0 – 3.5. Because we’ve written our rule in .NET 4.0, our only option is to test it within Visual Studio. Luckily, that’s not as hard as it sounds. (If you’re writing your rules in VS2008, jump over this section.)

  1. Create another class library in your solution called TestLibrary. We won’t put any real code in here – we’re just going to use it as the library to execute our rules against.
  2. Add a new Code Analysis Rule Set file to the project:

    New Code Analysis Rule Set

  3. When the file opens in the designer you’ll see a list of all the built-in rules. Because custom rules aren’t really supported yet, there’s no nice way of adding our own rules into this list.

    Default Rules

  4. In Solution Explorer, right click on the .ruleset file, choose Open With and select XML Editor from the options. This will show you the raw contents of the file, which is currently pretty boring. To point Visual Studio in the direction of your custom rules, you then add a series of hint paths.

    This is what my rule set XML looks like:

    <?xml version="1.0" encoding="utf-8"?>
    <RuleSet Name="New Rule Set" Description="" ToolsVersion="10.0">
      <RuleHintPaths>
        <Path>C:\Temp\CARules\BlogDemo\BlogDemo.CodeAnalysisRules\bin\Debug</Path>
      </RuleHintPaths>
    </RuleSet>
    

    Hint paths can be absolute, or relative to the location of the rule set file. They should point at the exact folder that your compiled rules sit in. Because Visual Studio fails silently if it can’t load a rule, I prefer to start with an absolute folder path first, then change it to a relative path once everything is working.

  5. Make sure you have compiled your rules project, then go back to Solution Explorer, right click on the .ruleset file, choose Open With and select Code Analysis Rule Set Editor.

    (If you have file locking issues, close Visual Studio, delete all of your bin folders, reopen the solution, build the rules project, then attempt to open the Code Analysis Rule Set Editor again.)

Now, you should see your custom rule loaded into the list:

6CustomRules

Running the rule is now easy. Open the project properties for your test library project, go to the Code Analysis tab, enable Code Analysis and select our new rule set:

7EnableCodeAnalysis

Now when we build the project, the output from our new rule will appear in the Errors List just like any of the default rules:

8ErrorList

A Bit of Clean-up

Back when we first created the project file for our rules we referenced a couple of DLLs from a system location. This isn’t very maintainable, particularly in a team environment, so let’s clean that up quickly.

  1. Right click on the rules project and select “Unload Project”
  2. Right click on the rules project again and select “Edit .csproj” – this will show you the raw XML definition for the project
  3. Find these three references:

    <Reference Include="FxCopSdk">
      <HintPath>..\..\..\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\FxCopSdk.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.Cci">
      <HintPath>..\..\..\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Microsoft.Cci.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.VisualStudio.CodeAnalysis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\Microsoft.VisualStudio.CodeAnalysis.dll</HintPath>
      <Private>True</Private>
    </Reference>
    

    And replace them with this:

    <Reference Include="FxCopSdk">
      <HintPath>$(CodeAnalysisPath)\FxCopSdk.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.Cci">
      <HintPath>$(CodeAnalysisPath)\Microsoft.Cci.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Microsoft.VisualStudio.CodeAnalysis">
      <HintPath>$(CodeAnalysisPath)\Microsoft.VisualStudio.CodeAnalysis.dll</HintPath>
      <Private>False</Private>
    </Reference>
    

    The build system populates the $(CodeAnalysisPath) variable for us automatically. This way, our references will be valid on every developer’s machine.

  4. Save and close the file, then right click the project and select “Reload Project”

Do the shuffle. The two-step, multi-framework shuffle…

For Web Forms MVP we want to support users on both VS2008 and VS2010. The work we’ve done so far in this post is all exclusively targeted towards VS2010 and not compatible with VS2008 or FxCop 1.36.

To make the compiled rules compatible with both IDEs we’ll need to compile two different versions of it. The VS2008 version will use .NET 3.5 and only two references while the VS2010 version will use .NET 4 and a third reference, Microsoft.VisualStudio.CodeAnalysis.

  1. Right click on the rules project and select “Unload Project”
  2. Right click on the rules project again and select “Edit .csproj” – this will show you the raw XML definition for the project
  3. Find both your Debug and Release property groups and add a DEV10 constant to each:

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
      ...
      <DefineConstants>TRACE;DEBUG;CODE_ANALYSIS;DEV10</DefineConstants>
      ...
    </PropertyGroup>
    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
      ...
      <DefineConstants>TRACE;DEV10</DefineConstants>
      ...
    </PropertyGroup>
    
  4. Find the Microsoft.VisualStudio.CodeAnalysis reference and make it conditional based on the framework version being compiled against:

    <Reference Include="Microsoft.VisualStudio.CodeAnalysis" Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
      <HintPath>$(CodeAnalysisPath)\Microsoft.VisualStudio.CodeAnalysis.dll</HintPath>
      <Private>False</Private>
    </Reference>
    
  5. Save and close the file, then right click the project and select “Reload Project”
  6. Go to AllTypeNamesShouldEndInFoo.cs and wrap the using statement for Microsoft.VisualStudio.CodeAnalysis.Extensibility in an #if construct like so:

    using System;
    using Microsoft.FxCop.Sdk;
    #if DEV10
        using Microsoft.VisualStudio.CodeAnalysis.Extensibility;
    #endif
    
  7. Make sure that your project still compiles with VS2010

At this point our project is still only building for VS2010 but it now contains all of the hook points we need to perform a second build for VS2008. The reference to Microsoft.VisualStudio.CodeAnalysis.dll will only be included if we’re building against .NET 4 and the using statements will only be compiled if the DEV10 compilation constant is present.

Normally, we would build the project using a simple call to MSBuild (which is exactly what VS2010 does under the covers):

MSBuild "BlogDemo.CodeAnalysisRules.csproj" /p:Configuration=Release /maxcpucount

To compile the FxCop 1.36 version, we just pass some extra arguments:

MSBuild  BlogDemo.CodeAnalysisRules.csproj " /p:Configuration=Release /maxcpucount /p:CodeAnalysisPath="..\Dependencies\FxCop136\" /p:DefineConstants="" /p:TargetFrameworkVersion="v3.5"

The CodeAnalysisPath parameter is normally supplied by MSBuild, but we are now overriding it with the location of the FxCop 1.36 SDK. We’re also overriding TargetFrameworkVersion.

Of course, there are nicer ways to script the build process using technologies like PowerShell. The ZIP file below contains a nice little Build-CARules.ps1 which you can use as a template.

The Resources

Download all of the sample code from this blog post and a PowerShell build script here:

Download CodeAnalysisRulesBlogDemo.zip