Scott Hanselman

SmallestDotNet: On the Size of the .NET Framework

August 24, '08 Comments [34] Posted in ASP.NET | Learning .NET | Musings | Windows Client | WPF
Sponsored By

Badge that says There's been some confusion about the size of the .NET Framework. The .NET Framework is not really a 200+ meg download. 

Which installer do I use?

Here's the whole thing in a nutshell for Developers, ISVs, and Administrators.

  • Offline Installer - One single file that can be run offline and can install the .NET Framework any system it's run on. It's complete, all platforms, installable offline.
  • Online Installer - A 2.7 meg setup program that will detect what just the files you need, then go download between 10 and 60 megs.
    • NOTE: If you're IT and inside an office, you'll want to decide if you want everyone in the office downloading .NET separately, or if you just want download it once, and have them to run it off a network share. Check out the Deployment Guide for Administrators for ways to push it out via AD or SMS.

So how big is it the .NET Framework download, really?

It depends on what you've already got installed. Here's some examples of my results using an XP SP2 machine. 

Version of
Framework installed
Download size
to get to 3.5SP1
Time to Download
(512 kbps)
None ~56 MB 15 min
2.0 ~50 MB 15 min
2.0SP1 ~33 MB 9 min
3.0SP1 ~10 MB 3 min

From a download perspective, those numbers aren't too bad. It's not nearly as bad as its been made out to be. I'm not saying this as a Microsoft apologist or paid Microsoft shill, I'm showing the numbers that I have seen in my testing. This is my opinion based on my testing.

However, as a free-micro-ISV myself, and the distributor of a .NET Client Application, namely BabySmash!, I'd like the download size to me as SMALL as possible. I've personally received the same emails many of you have - "I'd love to download your software but I don't want to download a 200 meg .NET Framework." I'd like to help change that perception to get more people to run BabySmash! and the best way to change negative perceptions is to improve reality. ;)

Online/Download Experience

The best way to get a user with reasonable Internet connectivity up on the 3.5 SP1 .NET Framework is with the 2.7 Meg "bootstrapper."  This will detect what they need and only download what they need. The worst-case scenario for a x86 machine is around 60 megs, as seen in the table above.

What's the "Client Profile?"

The Client Profile is an even smaller install option for .NET 3.5 SP1 on XP. It's small 277k bootstrapper. When it's run on a Windows XP SP2 machines with no .NET Framework installed, it will download a 28 meg payload and give you a client-specific subset of .NET 3.5.  If the Client Profile bootstrapper is run on a machine with any version of .NET on it, it'll act the same as the 3.5 SP1 web installer and detect what it needs to download, then go get it.  There's more details in the Client Profile Deployment Guide.

Ultimately, this will be the best and fastest possible way to get the .NET Framework, as it's smart on each platform. I'm going to move BabySmash to the Client Profile to get new XP customers up on .NET more than twice as fast with less than half the total download size.

The Client Profile Offline Installer (Preview) that Chris noticed was big was such because it is another example of an offline installer. It is the Client Profile Setup + the Full Installer mentioned before. Again, this particular download is totally for offline only scenarios. Unless you need a completely offline scenario, neither you nor your users need download it. The Client Profile Bootstrapper and its associated customization tools will be released soon.

Offline Experience

Now, from an offline perspective, if you're on a CD/DVD if you want to make sure that any machine you come upon will be able to get up to .NET Framework 3.5, you'll want the super offline installer. However, you can and should modify that offline installer to make it as smaller as you need. If you only support x86, then only ship those bits. Aaron Stebner points out that you can :

Extract the contents of the full install package and selectively remove payload that is not needed or supported by the scenarios that the setup that requires it supports.  I describe this option in a bit more detail in this blog post.  At a high level, this option involves identifying supported OS's and processor architectures and then selectively removing prerequisite payload from the .NET Framework 3.5 install location for platforms that the setup carrying the .NET Framework 3.5 does not plan to support.

He's basically saying for administrators and IT folks should customize the .NET Framework installer to yank stuff you don't need. This kind of customization and more is going to be easier later in September when the .NET Client Profile Configurator is released, so watch for that.

Why is that one installer so big?

That giant .NET download is for one thing - It's meant for developers or administrators who might want to redistribute a a setup that contains not just the whole of the .NET Framework, but for all possible platforms.  It has installers for x86, x64 and ia64. As mentioned above, you can customize it and make it smaller, shipping just what you need, based on your product's needs.

I've brought the total size up to bosses who care, as have many other smarter people and it's and making the installers even smaller, via separation, compression and other ideas are being actively looked at.

Windows Update

Over the next several months, machines with .NET 2.0 and up will start updating to the latest .NET 3.5SP1 using Windows Update. For ISVS (like me) this'll start to level versioning out so I'll know more about what's on the average user's machine. For example: If a machine already has .NET 3.5 on it, BabySmash is a small 1 meg download, which makes BabySmash, and me, look good.

Introducing SmallestDotNet.Com

imageLast night I made this website, SmallestDotNet.com to help out. It'll sniff your browser's UserAgent and tell you want version of .NET you have, how big the download would be to get you to .NET 3.5 and what .NET redistributable is best for you in order to minimize your download. As some newer online deployment options are released I'll update the site's sniffing to make sure that sizes and choices are is optimal.

SmallestDotNet.com also has some Javascript that you can add to your product's sites as an include, that can help let users know how big the download for .NET will be ahead of time, an lead users to the right downloads, like this:

<script src="http://www.smallestdotnet.com/smallestdotnet/javascript.ashx" type="text/javascript"></script>

It's not perfect, as it has only the UserAgent to base it's guess on. However, I've found it useful on BabySmash! and I hope you find it useful too.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

ASP.NET Futures - Generating Dynamic Images with HttpHandlers gets Easier

August 21, '08 Comments [24] Posted in ASP.NET | ASP.NET Dynamic Data | ASP.NET MVC
Sponsored By

There's a treasure trove of interesting stuff over at http://www.codeplex.com/aspnet. It's a bunch of potential future stuff that the ASP.NET team is working on. That's where you can get builds of ASP.NET MVC, Dynamic Data Futures, and new AJAX stuff.

Two days ago a new CodePlex release snuck up on the site. It's a potential new feature, so it could go nowhere, or we could make T-shirts and sing its praises. Could go either way. No promises.

Justin Beck, an ASP.NET intern, prototyped and design it, then Marcin Dobosz, an ASP.NET Developer tidied it up nicely. Right now it's called ASP.NET Generated Image and you can get a release today.

Why should you care?

I've done a lot of HttpHandlers that generate images. It's usually pretty tedious. When I was working banking, I wrote an example HttpHandler that would take two Check Images (back and front) and composite them into a single image on the server side, then serving up the composite. Usually you're messing around in with MemoryStreams and Images, and then you serialize the result out to the Response.OutputStream, making sure the MIME Types are set appropriately. If you're really clever, you'll remember to do some client-side and appropriate caching, but I rarely see that in the wild.

So what have Justin and Marcin done here?

First, they've created a control with a little design mode "chrome" that makes getting started easier. The control is actually an extension of <asp:image/> that creates an HttpHandler and wires up the the src= attribute to the handler. If that were all, it'd be cute. However, second, and most importantly, they've created a base class that can do caching, transformations and parameter passing for you. It's really nicely factored, IMHO.

Here's a simple example. Put one of these GeneratedImage Controls on the page, and click on it...

Screenshot of the Context Menu of the GeneratedImage Control 

Next, click "Create Image Handler." You'll get this in the markup, and a new file in the Project:

public class ImageHandler1 : ImageHandler {

public ImageHandler1() {
// Set caching settings and add image transformations here
// EnableServerCache = true;
}

public override ImageInfo GenerateImage(NameValueCollection parameters) {
// Add image generation logic here and return an instance of ImageInfo
throw new NotImplementedException();
}
}

All I need to do now is override GenerateImage. Any parameters to the control will be in the NameValueCollection. I just need to return a new ImageInfo, and the constructor for ImageInfo can take either an Image, a byte[] or an HttpStatusCode. Clever.

Here, I'll add some text:

<cc1:GeneratedImage ID="GeneratedImage1"
runat="server" ImageHandlerUrl="~/TextImageHandler.ashx" >
<Parameters>
<cc1:ImageParameter Name="Hello" Value="text in an image" />
</Parameters>
</cc1:GeneratedImage>

As I said, that parameter is passed in, and it come from <% %> code or DataBinding or whatever. It doesn't care. Now, I'll draw on an image:

public class TextImageHandler : ImageHandler {

public TextImageHandler() {
this.ContentType = System.Drawing.Imaging.ImageFormat.Png;
}

public override ImageInfo GenerateImage(NameValueCollection parameters) {
// Add image generation logic here and return an instance of ImageInfo
Bitmap bit = new Bitmap(300, 60);
Graphics gra = Graphics.FromImage(bit);
gra.Clear(Color.AliceBlue);
gra.DrawString(parameters["Hello"], new Font(FontFamily.GenericSansSerif, 16), Brushes.Black, 0, 0);
return new ImageInfo(bit);
}
}

Which results in...

Picture of a dynamic generated image with text inside it 

Slick. What else can I do easily? Let's right-click on the ImageHandler base class and see what it looks like:

namespace Microsoft.Web
{
public abstract class ImageHandler : IHttpHandler
{
protected ImageHandler();

public TimeSpan ClientCacheExpiration { get; set; }
public ImageFormat ContentType { get; set; }
public bool EnableClientCache { get; set; }
public bool EnableServerCache { get; set; }
protected List<ImageTransform> ImageTransforms { get; }
public virtual bool IsReusable { get; }

public abstract ImageInfo GenerateImage(NameValueCollection parameters);
public void ProcessRequest(HttpContext context);
}
}

ImageTransforms? Hm...you can also setup a collection of ImageTransform objects into a little mini-image processing pipeline to do whatever you like.

Here we add a copyright watermark dynamically. The Transform is added in the constructor in this example.

public class TestCustomImages : ImageHandler {

public TestCustomImages()
{
this.ImageTransforms.Add(new CustomImageTransforms.ImageCopyrightTransform { Text = "Copyright Me! 2008" });
this.ContentType = System.Drawing.Imaging.ImageFormat.Png;
}

public override ImageInfo GenerateImage(NameValueCollection parameters) {
Bitmap pic = new Bitmap(200, 50);
Graphics gra = Graphics.FromImage(pic);
gra.Clear(Color.SkyBlue);
return new ImageInfo(pic);
}
}

The source for the CustomTransform, and for all the samples and the preview assembly that makes it all work is up on CodePlex now. If you've got ideas, find bugs, or think it's cool, leave a comment here and I'll make sure they get them. You can also leave bugs in the Issue Tracker on CodePlex.

Here's what the roadmap document says they've got planned...

  • Add Item Templates for an Image Handler
  • Providing parameter type inference for the handler instead of a Name Value Collection.
  • Giving you control of transforms, caching inside of your Generate Image Method.

Does it work with ASP.NET MVC?

Gee, I dunno. Let's see. Remember that if you're using ASP.NET MVC with the WebFormsViewEngine, as long as there's no Postback, some Server Controls can still work. This just might, as it renders on the first load.

I added the ASHX handler file to my MVC project, referenced the DLL, made sure to register the tag prefix and it mostly worked. The design service isn't working, saying something about "could not be set on property ImageHandlerUrl," but I'm encourage I got this far. Let's encourage them to keep MVC in mind!

image

Cool. Have at it at CodePlex. No warranty express or implied.

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

New Outlook VSTO AddIn: How to disable Reply To All and Forward in Outlook 2007

August 21, '08 Comments [6] Posted in Source Code | Tools
Sponsored By

2010 UPDATE: Gavin has released an updated version of his No Reply To All Add-In on the Microsoft Research Site. Go get it free!

Last October I posted a Macro-quasi-hack to Disable Reply To All and Forward in Outlook within your own company network. The technique uses a macro to flip a metadata bit in a message.

Of course the only REAL way truly to disable Reply to All and Forward is to use IRM (Intellectual Rights Management) in Outlook 2003/7. However, this technique was useful to a lot of people as it is super simple and can stop those "knee-jerk" Reply to Alls.

Anyway, after this post Gavin Smyth of Microsoft Research emailed me and said:

"However, it's still such a useful idea that I finally got round to writing a C# addin to do it (vaaaassst overkill, I know, but it was easy) - toggle buttons (one for reply, one for forward) on the ribbon that set the two flags appropriately."

Cool. He's written his first Visual Studio Tools for Office (VSTO) AddIn, and it's a good tutorial on how to write one!

image The general idea os:

  • Start with the VS Outlook Add-In project wizard
  • Add the ribbon group & buttons
  • Create click event handlers for both, replicating what was in your my blog posting

Poof. Package and Deploy. It's really obscenely easy. Actually, way easier than the macro way I did it.

Now my Messages have these nice shiny new icons:

NoReplyButtons 

The source is trivial:

using System;
using Microsoft.Office.Tools.Ribbon;
using Outlook = Microsoft.Office.Interop.Outlook;
using Office = Microsoft.Office.Core;

namespace NoReplyAllAddin
{
public partial class Ribbon : OfficeRibbon
{
public Ribbon() { InitializeComponent(); }

private bool SetActionFromButton( object sender, object context, string action )
{
bool oldValue = false;
Outlook.Inspector inspector = context as Outlook.Inspector;
if( inspector != null )
{
Outlook.MailItem msg = inspector.CurrentItem as Outlook.MailItem;
if( msg != null )
{
oldValue = msg.Actions[ action ].Enabled;
RibbonToggleButton btn = (RibbonToggleButton)sender;
msg.Actions[ action ].Enabled = !btn.Checked;
}
}
return oldValue;
}

private void OnClickNoReplyAll( object sender, RibbonControlEventArgs e )
{
SetActionFromButton( sender, e.Control.Context, "Reply to All" );
}

private void OnClickNoForward( object sender, RibbonControlEventArgs e )
{
SetActionFromButton( sender, e.Control.Context, "Forward" );
}
}
}

You can download the setup and/or the source for Gavin's "No Reply for Outlook 2007"  over at his Software Utilities site. Thanks to Gavin!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

The Weekly Source Code 32- Atom, AtomPub and BlogSvc, an AtomPub Server in WCF

August 21, '08 Comments [7] Posted in ASP.NET | Source Code | Web Services | Windows Client | WPF | XML
Sponsored By

In my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-second in a infinite number of posts of "The Weekly Source Code."

UPDATE: The BlogSvc.NET code has been refactored considerably, so consider checking out the Recent CheckIns to see what's changed..

BlogSvcBlogSvc.NET - The AtomPub Server for WCF and .NET

Much respect to people who not only release Open Source Software, but also take the time to get a nice, clean logo for their project as BlogSvc.NET did. It's written by Jarrett Vance and the code is up on CodePlex with Documentation and details on the main BlogSvc.NET site.

It also appears that the site at http://www.blogsvc.net is also the Demo Site for BlogSvc itself! Actually, if you download the source, you're downloading the complete implementation of the BlogSvc.net website. Booyachaka. Much respect.

Since the project uses Atom and AtomPub he can work against it using Windows Live Writer. You can learn all about how WLW likes Atom by reading the series of posts the most excellent Joe Cheng of the Live Writer team did on how this is all implemented.

Made it this far and wondering what the heck it all means? Let us turn to Wikipedia who gets it mostly right this time.

The name Atom applies to a pair of related standards. The Atom Syndication Format is an XML language used for web feeds, while the Atom Publishing Protocol (short AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

One way to look at it is that RSS is as a syndication format that looked like XML, but didn't really respect XML at its heart. Atom does. For publishing to blogs or content sites, the Blogger/MetaWeblog APIs are based on XML/RPC (a protocol that you either love or it makes you ill. Or both) while AtomPub is based on the RESTmodel and Atom and went through a more Standardsy adoption process. Ultimately it's a safe bet to use Atom and/or AtomPub. Everyone's pretty much on board. Microsoft got on board earlier this year. My hero Pablo Castro, the Principal Architect of ADO Data Services is supporting AtomPub in his project.

That said, BlogSvc is:

"...an open source implementation of the Atom Publishing Protocol. It is built on top of a provider model. There are providers for the file system and databases. The service is compatible with Live Writer."

Enough chatter, let's see some code. It's 0.3, but it's marching right along. When Jarrett starts using .NET 3.5 SP1, he'll likely get to do some "coding via subtraction" and swap out a lot of the boring Atom Object Model code with the new System.ServiceModel.Syndication and use the ServiceDocument. Jarreyy might be able to remove most of his "BlogService.Core" project. The new 3.5 SP1 stuff includes Atom and AtomPub object models as well as an Rss20 object model. It'll be interesting to see if those new object models can stand up against Argotic.

He's got a pretty comprehensive IAtomPub namespace with the UriTemplates that correspond to the "tag" URI Scheme from RFC 4151 that he's complying with.

[ServiceContract]
public interface IAtomPub
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}")]
Stream CreateResource(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}/*")]
Stream Annotate(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}/*")]
Stream RetrieveResource(string workspaceName, string collectionName);

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "{workspaceName}/{collectionName}/*", Method = "PUT")]
Stream UpdateResource(string workspaceName, string collectionName, Stream stream);

[OperationContract]
[WebInvoke(UriTemplate = "{workspaceName}/{collectionName}/*", Method = "DELETE")]
void DeleteResource(string workspaceName, string collectionName);

[OperationContract]
[WebInvoke(UriTemplate = "{workspaceName}/{collectionName}/*", Method = "HEAD")]
void HeadResource(string workspaceName, string collectionName);

[OperationContract]
[WebGet(UriTemplate = "service")]
AppService RetrieveService();

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "service", Method = "PUT")]
AppService UpdateService(AppService serviceDoc);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}/category?scheme={scheme}")]
AppCategories RetrieveCategories(string workspaceName, string collectionName, string scheme);

[OperationContract]
[WebGet(UriTemplate = "{workspaceName}/{collectionName}")]
AtomFeed RetrieveFeed(string workspaceName, string collectionName);
}

Now, let's look at CreateMedia which would be called as a part of CreateResource for any type of data that's not an Atom content type. CreateMedia is the kind of thing you'd want to do if you were attaching a picture or a video to your blog post.

public AtomEntry CreateMedia(string workspace, string collection, Stream stream)
{
try
{
if (!Authenticate(true)) return null;
string slug = WebOperationContext.Current.IncomingRequest.Headers["Slug"];
string contentType = WebOperationContext.Current.IncomingRequest.ContentType;
string etag;
AtomEntry entry = Atom.Provider.GetService().GetCollection(workspace, collection).CreateMedia(stream, slug, contentType, out etag);
WebOperationContext.Current.OutgoingResponse.ContentType = Atom.ContentTypeEntry;
WebOperationContext.Current.OutgoingResponse.Headers["Content-Location"] = entry.Location.AbsoluteUri;
WebOperationContext.Current.OutgoingResponse.ETag = etag;
WebOperationContext.Current.OutgoingResponse.SetStatusAsCreated(entry.Location);
return entry;
}
catch (ResourceNotFoundException rnfe)
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound(rnfe.Message);
}
catch (InvalidContentTypeException icte)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.NotAcceptable;
WebOperationContext.Current.OutgoingResponse.StatusDescription = icte.Message;
}
catch (Exception ex)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
WebOperationContext.Current.OutgoingResponse.StatusDescription = ex.Message;
}
return null;
}

Personally I'd have done a little refactoring in and pulled out WebOperationContext.Current.OutgoingResponse into a variable, but you get the idea. I think there's some more opportunity for some larger, more profound refactorings as this method looks a lot like most of the others. CRUD (Create, Read, Update, Delete) code does tend to repeat.

I think Jarrett will be able to move all the try/catch and the caching work for eTags and what not into some helpers. The project site says the next step is to add some authentication and have that factored out into a module, and that's confirmed by his copious TODO comments.

I really enjoy reading code where someone, like Jarrett, has taken the time to actually put in TODO: comments. It's easier to get into the programmers head if you know where they are going, and TODOs also tell you where they've been. I can tell when I'm reading this that the project owner knows what's good and what's not, and from that, gauge the trajectory of the project.

image

Great stuff and I hope that we see more work like this in the .NET Open Source space around Atom and AtomPub. Why should you care? Perhaps you work for a company that has a content management system, or a sales catalog system where sales people want to edit project web pages. Why not put something like BlogSvc in front of it and let your sales folk use Windows Live Writer or some other AtomPub compliant editor? The possibilities are really endless.

WEIRD SIDE NOTE: I had a weird COM Interop Error when opening his "Web.csproj" - I'd get "System.Runtime.InteropServices.COMException." I opened the csproj in notepad and noticed two things. First, the IISUrl was set to his computer name, http://rocket, so I changed that to http://localhost. The other was that it said UseIIS="True" so I suspect that was a lame error message trying to tell me that I didn't have the IIS 6.0 compatibility extensions on my Vista system. I set it to UseIIS="False" and the project loaded fine.
CONFIRMED: I ran into this bug on Connect and I shall now yell about it internally.

It'll be interesting to see how BlogSvc and ADO Data Services in 3.5 SP1 intersect. BTW: You can learn more about ADO Data Services by watching the How Do I videos on MSDN. (Yes, the video player sucks, but you can download the WMVs.)

Technorati Tags: ,,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast 126 - Chat with John Resig, Creator of jQuery

August 15, '08 Comments [9] Posted in ASP.NET | Javascript | Podcast
Sponsored By

image My one-hundred-and-twenty-sixth podcast is up. I got to talk to John Resig the creator of jQuery. We talk about about how he developed jQuery, how it performs, and where he thinks it's headed.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Telerik's new stuff is pretty sweet, check out the ONLINE DEMO of their new ASP.NET AJAX suite. RadGrid handles sorting, filtering, and paging of hundreds of thousands of records in milliseconds, and the RadEditor loads up to 4 times faster and the navigation controls now support binding to web services on the client.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.