Flickr Schedulr on BBC's Webscape#

On a related note to releasing the much improved v2.0 of Flickr Schedulr, I just noticed that the previous release got mentioned in the BBC's Webscape reviews.

How awesome is that?!

Flickr Schedulr BBC Webscape Review

It shows a quick tour of what the application does and how it works. And note that the part where they set up the Windows scheduled task is no longer necessary in v2.0 since that's now baked in :-)

Sunday, September 05, 2010 9:51:40 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Just released: Flickr Schedulr v2.0!#

Hot off the press and might I add, finally! I just got around to packaging up and publishing the completely rewritten v2.0 of Flickr Schedulr.

Flickr Schedulr

Flickr Schedulr is a Windows desktop application that automatically uploads pictures to Flickr based on a schedule (e.g. to post a new picture every day at a certain time). It allows you to create a queue of pictures to be uploaded, along with their titles, descriptions, tags, and the photoset into which they should end up. This effectively takes the hassle of uploading pictures at regular intervals away, and allows you to go out and have fun shooting pictures (or drinking beer) while your photoblog is maintained for you.

Flickr Schedulr

What's new in this version?

  • The application was completely rewritten in Windows Presentation Foundation (WPF), which makes for a much nicer user experience.
  • You can now create a Windows scheduled task directly from the application.
  • You can now assign a content type and license to pictures.
  • You can now configure more than one Flickr account and maintain upload queues for each account separately from within the same application.
  • You can now add pictures to the queue from the command line using the "/add" and "/addbatch" switches.

As always, you can find all information, screenshots and downloads on the dedicated site at http://schedulr.codeplex.com

Happy Flickring!

Sunday, September 05, 2010 8:50:55 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Just Released: Mayando v1.2!#

Ok maybe it's not just released but still I'm happy to finally put the word out that you can now install Mayando v1.2 directly from the Microsoft Web Application Gallery!

Install Mayando using the Microsoft Web Platform Installer

Mayando is a full-featured photo blogging application that you can use to showcase your photos online.

Mayando-Logo-Medium

What's new in this release:

  • Dates on photos are now hyperlinks to other photos taken or published on the same date.
  • The Flickr photo provider no longer synchronizes machine tags (because they are not intended to be displayed).
  • You can now configure the photo provider to synchronize automatically at regular intervals (e.g. every 60 minutes).
  • You can also use a command-line client application (or if you're a developer, a client API) to remotely trigger a photo provider synchronization through the use of a new Service API.
  • You can now filter the event log by severity.
  • Mayando is now compatible with ASP.NET "medium trust" hosting providers.
  • You can now disable distributed transactions (in the AppSettings.config file) if your hosting provider does not allow them. Note that this can cause data loss and/or corruption so only change this if you accept the risks associated with disabling transactions.

If you want to see it running: check out the Mayando Demo Site or of course my own photo blog.

Wednesday, August 25, 2010 7:51:46 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

What Has Jelle Been Up To (a.k.a. The Last Post)#

Since it’s been almost two years since my last blog post, I figured I owed the remaining 3 subscribers of my blog a short update on what I have been up to...

#1 – Maya

There have indeed been a couple of interesting things going on, and first and foremost, that would include the birth of our amazingly beautiful and unbelievably cute daughter Maya in June last year :-)

Prinses  Koekjestijd Verjaardagskroon

She just turned one year old this week, so time flies indeed! If you would be interested in seeing some more pictures, then I’m sure you can figure out where her own website would be located if you studied the incredibly complicated naming pattern I used to locate my own website :-)

#2 - Mayando

Because Project #1 called for a way to keep the family up to date and to regularly show off exactly how cute Maya is, and (almost equally importantly) because I was looking for an excuse to learn ASP.NET MVC, I decided to write a photo blogging web application. “What, another photo gallery site”, you might ask? Eh, yeah, exactly. But in my defense: I looked hard at the existing ones and couldn’t find one that covered my requirements so this is one I built to fit my needs :-)

Nonetheless, I didn’t just want to build a one-off “baby web site for Maya”, but really a generic application that I could also use later on use as the engine for my own photo blog – and that you, dear reader, might also want to use if you want to publish a collection of photos in a nice and user-friendly way. (For example, I have an architect friend who is interested in using it as a portfolio site for the houses he designed.)

And so, Mayando was born: a full-featured photo blogging application that you can use to showcase your photos online.

Mayando-Logo-Medium

Now I did not want to reinvent solutions to the problem of globally storing and serving images on the web, so I figured that I should only build a rich front-end on top of existing photo storage services such as Flickr. So I built a provider model where the URL’s of the photos and their details (and comments) just get “sucked in” from a photo sharing site and you can work with them from your own website. So basically, the photos get pulled in from a service such as Flickr and then displayed through Mayando, using a lot of navigation possibilities (by creating static pages and dynamic galleries, by browsing through photos, comments, tags, dates, ...).

It also allows visitors to post new comments and obviously I needed to handle comment spam so again I implemented a provider model for anti-spam services (such as Mollom).

And finally, the whole thing had to be easily customizable so I made sure to allow different themes for the photo blog’s look and feel, with customization options ranging from simple (e.g. simply changing the CSS stylesheet) to advanced (completely changing the entire site layout and/or individual pages). Thankfully, by now I know that the ASP.NET MVC framework is so flexible it easily let me do all this with surprisingly little effort. Anyway, I won’t go into the many details – if you’re interested in how it works: it’s open source so feel free to look at the Mayando source code and let me know if you want to contribute!

If you just want to see it running: check out the Mayando Demo Site or of course my own photo blog :-)

#3 – Flickr Schedulr v1.4 & v2.0

Because Project #1 and Project #2 meant I would be using Flickr more, I figured it was also time to give my Flickr Schedulr application an update to incorporate feedback from a number of users.

In case you’re wondering what it is: Flickr Schedulr is a Windows desktop application that automatically uploads pictures to Flickr based on a schedule (e.g. to post a new picture every day at a certain time). It allows you to create a queue of pictures to be uploaded, along with their titles, descriptions, tags, and the photosets and groups into which they should end up.

Schedulr-Logo-Small

I published v1.4 last January; new features include the possibility to upload multiple pictures at a time in batch, better handling of multiple selected files and overall UI improvements.

I’ve also been working really hard on v2.0 which is a complete rewrite of the application in Windows Presentation Foundation (WPF), and as such looks and feels so much nicer in many ways than the previous version. It will also have a few new features of course. I’m still polishing some things but you can expect to see a release in the next few weeks.

Since I’ve been very happy with CodePlex for my other projects, I decided to move the source code and work items there and lo and behold: here is the new Flickr Schedulr homepage on CodePlex!

#4 – Proxy Monitor

Last October, the trend continued: another release around a year after the last one. This time, I got the help from David Huntley, who was kind enough to finally get something off my list I’ve been planning to do for a while now: properly setting the proxy via the Win32 API’s instead of just writing to the registry. This more robust way of setting the proxy came for free with the new feature he implemented, which is support for multiple connections. This allows you to specify proxy servers for other connections than the default LAN (such as dial-up or VPN connections).

To make it easier to work together, I decided to move this project to CodePlex as well. So for all information, downloads, forums, etc. go to the new Proxy Monitor homepage at CodePlex!

#5 – The NOT Part

So after a small list of things that I have been up to the last two years or so, it’s quite clear what I have not been up to: blogging. And that’s probably going to stay that way. I either have too little to say (which is increasingly the case), or too much (which would take too much time to write down). So honestly, I expect this would be the last entry on my blog for quite a while – if not eternity.

In case it becomes the latter: thanks for having followed my random thoughts for the last 7 years, and if you want to keep up with what I’m doing on the technical side of life, follow my projects on CodePlex:

If you want to keep up with the non-technical side, check out my photography site - <plug>based on Mayando of course, and updated with the help of Flickr Schedulr</plug> :-)

Blog | General | Photography | Programming | .NET | ASP.NET | WPF | Websites | Windows
Thursday, June 10, 2010 1:03:08 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Just Released: ProxyMonitor 1.2!#

Interestingly, I seem to have a near-yearly rhythm of releasing new versions of Proxy Monitor. This minor release adds the "skipAutoDetect" attribute to support proxies that are only set manually, and I also (think I) fixed the bug where Interet Explorer would override the proxy settings again (at random) to their previous values.

Proxy Monitor is a small application that monitors the network and auto-detects the internet proxy server to use. It can be started as a regular application, which will make it run as an icon in the system notification area. When started, it will auto-detect the proxy server to use. It will also automatically re-detect the proxy server when the computer’s network address has changed. The application can also be run from the command-line with the /detect flag to auto-detect the proxy and exit immediately (e.g. when the computer starts up).

You can download the tool below. Don't hesitate to contact me for suggestions or bug reports!

The ProxyMonitor 1.2 executable (103 KB)

The ProxyMonitor 1.2 source code (104 KB)

You can also view the Readme file online.

Monday, September 08, 2008 4:10:06 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Updated Again: Setting up Source Server for TFS Builds (v1.2)#

I've just published another update to my guide on Setting up Source Server for TFS Builds, since the Debugging Tools for Windows now has built-in support for Team Foundation Server. So that means: no more third party downloads and an even simpler installation procedure! I've also updated the document to reflect all applicable versions of the tools you'll be using: Visual Studio 2005 and 2008, as well as Team Foundation Server 2005 and 2008.

Because I have been troubleshooting Source Server issues a few times as well, I also added a chapter on how to find out what's going on inside the Source Server indexing and what might be going wrong. Note that there is some Perl going on in there, so avert your eyes if you can't handle the look and feel of it (I know I can't) :-)

For the full setup instructions, please refer to the original post on Setting up Source Server for TFS Builds.

Oh, and finally, my homie Pieter also posted a more detailed guide on how to set Source Server support up inside the Team Build script - and what's more important, what the invaluable benefit is of having a central reusable build script. Great job Pieter!

Tuesday, July 08, 2008 8:57:07 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Extracting OLE embedded images from emails in Outlook#

While it seemed a simple requirement, saving all attachments from emails in Outlook to disk proved to be challenging - to say the least. Using the Outlook Object Model, it's quite easy to enumerate all emails in a folder, look at their attachments and call the SaveAsFile method on them. However, for OLE-type attachments (typically images), this throws a COMException saying: "Outlook cannot do this action on this type of attachment". While looking for alternatives or workarounds, I found nothing but confirmation that this is indeed not an easy task - even from Dmitry Streblechenko, Outlook MVP and creator of the excellent and very affordable Outlook Redemption library: "If you mean embedded graphics objects in the RTF messages, there is not much you can do [...] You can look at the storage itself to figure that out, but I've never tried that".

Ultimately, after lots of trial and error, I did manage to find a fairly easy way to save these OLE embedded images by (mis)using the clipboard. Basically, I retrieve the attachment’s IStorage OLE interface (available through Redemption) and call OleLoad on it to have OLE load the contents and retrieve an IDataObject. The magic trick is to place that IDataObject on the clipboard and retrieve the actual image from the clipboard (so that the clipboard itself handles the nasty OLE details).

Great success! At least for a moment. That already worked for Device Independent Bitmaps, but Outlook also uses Enhanced Metafiles (wmf) and apparently there is a problem with the .NET Framework when it comes to handling Enhanced Metafiles from the clipboard. So I needed some additional COM interop to handle these Enhanced Metafiles as well, which made the code slightly more difficult to read but fortunately still effective. The trick here is to make sure you have a valid handle to pass to OpenClipboard. Because I didn't have access to a form or other type of existing control, I just created a dummy button and used its handle.

Finally, be aware that to access OLE functionality, you need a Single Threaded Apartment (STA) model. Of course I was in an MTA context, so from there I launched a new thread which I put to STA - after that, everything was golden.

Below is the full code using Redemption Data Objects (RDO), hopefully this will save other people a few hours in trying to achieve the same thing...

public static class Program
{
  public static void Main()
  {
    // Calling code should always ensure to be in STA.
    Thread staThread = new Thread(new ThreadStart(SaveOutlookAttachments));
    staThread.SetApartmentState(ApartmentState.STA);
    staThread.Start();
  }

  private static void SaveOutlookAttachments()
  {
    RDOSession session = new RDOSessionClass();
    RDOFolder inbox = session.GetDefaultFolder(rdoDefaultFolders.olFolderInbox);
    string attachmentRootPath = AppDomain.CurrentDomain.BaseDirectory;
    foreach (RDOMail mail in inbox.Items)
    {
      foreach (RDOAttachment attachment in mail.Attachments)
      {
        if (attachment.Type == rdoAttachmentType.olOLE)
        {
          // We don't have a filename for this type of attachment, create a unique one.
          string filename = Guid.NewGuid().ToString() + ".png";
          string attachmentPath = Path.Combine(attachmentRootPath, filename);
          // We assume here that only images will be stored as OLE attachments.
          // We save them as PNG to keep the file size small.
          SaveOleImageAttachment(attachment, attachmentPath, ImageFormat.Png);
        }
        else
        {
          string attachmentPath = Path.Combine(attachmentRootPath, attachment.FileName);
          attachment.SaveAsFile(attachmentPath);
        }
      }
    }
  }

  private static void SaveOleImageAttachment(RDOAttachment attachment, string filePath, ImageFormat format)
  {
    // Use the OLE storage interface to load the OLE document into a DataObject.
    IStorage oleStorage = (IStorage)attachment.OleStorage;
    object oleDataObject;
    OleLoad(oleStorage, ref IDataObjectGuid, null, out oleDataObject);

    // Copy the OLE DataObject to the clipboard so it can handle the internals.
    Clipboard.SetDataObject(oleDataObject, false);

    // Try to retrieve an image back from the clipboard.
    if (Clipboard.ContainsData(DataFormats.EnhancedMetafile))
    {
      // Enhanced Metafiles cannot be handled natively from .NET.
      // Use the Clipboard directly to retrieve the data.

      // We need a valid handle, otherwise this won't work.
      Button dummy = new Button();
      if (OpenClipboard(dummy.Handle))
      {
        try
        {
          if (IsClipboardFormatAvailable(CF_ENHMETAFILE))
          {
            IntPtr metafileData = GetClipboardData(CF_ENHMETAFILE);
            if (metafileData != IntPtr.Zero)
            {
              using (Metafile metafile = new Metafile(metafileData, true))
              {
                metafile.Save(filePath, format);
              }
            }
          }
        }
        finally
        {
          EmptyClipboard();
          CloseClipboard();
        }
      }
    }
    else if (Clipboard.ContainsImage())
    {
      using (Image image = Clipboard.GetImage())
      {
        if (image != null)
        {
          image.Save(filePath, format);
        }
      }
    }
  }

  [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  private static extern bool OpenClipboard(IntPtr hWndNewOwner);
  [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  private static extern bool CloseClipboard();
  [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  private static extern IntPtr GetClipboardData(uint format);
  [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  private static extern bool IsClipboardFormatAvailable(uint format);
  [DllImport("user32.dll")]
  private static extern bool EmptyClipboard();
  [DllImport("ole32.dll")]
  private static extern int OleLoad(IStorage pStg, [In] ref Guid riid, IOleClientSite pClientSite, [MarshalAs(UnmanagedType.IUnknown)] out object ppvObj);

  private static Guid IDataObjectGuid = new Guid("0000010E00000000C000000000000046");
  private const uint CF_ENHMETAFILE = 14;
}
Blog | General | Programming | .NET | Samples
Monday, June 02, 2008 12:18:29 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Just Released: Mollom for .NET v1.0!#

My friend Dries Buytaert - known all around the world for creating Drupal (the wildly popular open source content management system) and Axl (the incredibly cute kid he co-created with my even better friend Karlijn) - asked me a few months ago if I had any trouble with spam on my blog... It turned out he was building Mollom, a solution for fighting spam and automating content monitoring, and was looking for beta testers. I immediately jumped aboard and implemented a .NET client API for his service and integrated it into dasBlog, the blog engine I'm using.

Now that Mollom and its API and developer documentation have finally been released (in public beta), I've packaged my client library as well and published it on CodePlex: see the Mollom for .NET homepage.

Mollom's purpose is to dramatically reduce the effort of keeping your websites clean and the quality of their user-generated content high. Currently, Mollom is a spam-killing, one-two punch combination of a state-of-the-art spam filter and CAPTCHA server.

I have to say it's working really well for me, I don't get any spam at all anymore through my blog, and the XML-RPC API that Mollom provides is easy and straight-forward to use. And, of course, if you develop on .NET then it's even easier to talk to Mollom using my client API. As a very basic sample, this should give you an idea of how easy it is to have Mollom classify a piece of content:

MollomClient client = new MollomClient(privateKey, publicKey);
ContentCheck result = client.CheckContent(postTitle, postBody, authorName, authorMail, authorUrl, authorIPAddress);
if (result.Classification == ContentClassification.Spam)
{     // Handle spam here...
}

All information, downloads and documentation is available on the Mollom for .NET homepage on CodePlex, so rush out and let me know what you think!

Sunday, May 18, 2008 5:22:48 PM (Romance Standard Time, UTC+01:00) #    Comments [6]  | 

 

Updated: Setting up Source Server for TFS Builds#

Just a quick note to let you know that I've updated my guide on Setting up Source Server for TFS Builds, since I just found out that there is an issue with Build Definitions that contain spaces. The fix is fairly easy though:

  • In TFIndex.cmd (on the build server), remove the quotes around the %1 argument for SYMBOLS:
@call "%~dp0SSIndex.cmd" -SYSTEM=TF -SYMBOLS="%1" %*
@call "%~dp0SSIndex.cmd" -SYSTEM=TF -SYMBOLS=%1 %*
  • In the Team Build Script (in Source Control), add XML-escaped quotes around the $(BinariesRoot) argument:
<Exec Command="&quot;C:\Program Files\Debugging Tools for Windows\sdk\srcsrv\TFIndex.cmd&quot; $(BinariesRoot)"
      WorkingDirectory="$(SolutionRoot)" />
<Exec Command="&quot;C:\Program Files\Debugging Tools for Windows\sdk\srcsrv\TFIndex.cmd&quot; &quot;$(BinariesRoot)&quot;"
      WorkingDirectory="$(SolutionRoot)" />

For the full setup instructions, please refer to the original post on Setting up Source Server for TFS Builds.

Wednesday, April 30, 2008 11:20:47 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

My "Deep Dive Into The Guidance Automation Toolkit" presentation now online!#

Tom's team has been kind enough to put my session of last year's TechDays (then still known as the Developer & IT Pro Days) online on MSDN Chopsticks. You can find my "Deep Dive Into The Guidance Automation Toolkit" presentation at http://www.microsoft.com/belux/msdn/nl/chopsticks/default.aspx?id=10. Everything I said back then is still relevant today, so if you missed it last year you can now catch up for free :-)

And in the light of Software Factory technologies, it also makes a nice preparation for my talk on Domain-Specific Development with Visual Studio DSL Tools next week. My session is scheduled on Thursday March 13 at 10:45. I'm really looking forward to it, and I hope to see you there!

Monday, March 03, 2008 10:39:16 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

DSL Tools session at TechDays in Belgium#

The annual Belgian tech-fest for Microsoft developers, architects and IT pro's is coming to Ghent again soon, and I'm once again proud to host a session in the Developers track for TechDays!

Domain Specific Development with Visual Studio Domain Specific Language (DSL) Tools

As one of the pillars of the Software Factories initiative, Domain Specific Languages (DSLs) provide a way to describe your business domain in a language closer to the actual problem than using traditional programming code.

The Visual Studio Domain Specific Language Tools allow developers to create their own graphical designers and code generation tools – much like the ones you can find in Visual Studio today, such as the Class Designer.

In this session, you will learn how to develop your own DSLs inside Visual Studio and see an example of a real-world DSL that simplifies your life as a developer: the Configuration Section Designer.

TechDays 2008

Heroes are Assembled { in Software Factories } :-)

Hope to see you there!

Blog | General | Programming | .NET | DSLs
Monday, February 18, 2008 8:56:39 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

 

Just Released: BuildCop v1.0!#

It is with great pleasure that I'm finally ready to release another open source tool on CodePlex: BuildCop.

BuildCop is a tool that analyzes MSBuild project files (interactively or during e.g. a daily build) according to a customizable set of rules and generates reports - e.g. is strong naming enabled, are certain project properties set correctly, is XML documentation being generated, are assembly references correct, are naming conventions respected, ...

This has grown out of a quick-and-dirty tool to check various build settings in a large customer project (to make sure that the developers were sticking to the guidelines), and has evolved into quite a clean, flexible and customizable tool that you can now start using as well.

All information, downloads and documentation is available on the BuildCop homepage on CodePlex, so rush out and let me know what you think!

Tuesday, February 05, 2008 10:37:20 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Just Released: Configuration Section Designer#

I just released my first domain specific language to the public! The Configuration Section Designer is a Visual Studio add-in that allows you to graphically design .NET Configuration Sections and automatically generates all the required code and a schema definition (XSD) for them.

The Configuration Section Designer In Action

For all information, downloads, source code, work item tracking and discussions visit http://www.codeplex.com/csd.

The Configuration Section Designer is built on Visual Studio 2008 with the very excellent DSL Tools. Instead of publishing it here on the blog as I normally do with my pet projects, I've decided to host it as open source software on CodePlex so that hopefully other people will find it interesting enough to contribute and make it even more powerful. This also gives me the chance to test-drive CodePlex in a real project, and so far it's been working great so I'll probably be moving more projects to it.

Anyway, if you have Configuration Sections that you're currently writing by hand, I encourage you to try this and let me know how it works for you. I'm pretty excited about it, I've only published it yesterday evening and it's already got over 40 downloads and a good feature suggestion of someone that's been porting his hand-written code to the designer! Good times!

Blog | Programming | .NET | DSLs | CSD
Sunday, December 30, 2007 5:21:30 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Just Released: E = m c² v2.1!#

It's taken way too long to remind myself to release a new version of E = m c² - so finally, here goes: v2.1 is out the door! It even comes with source code again! What a perfect Christmas present for 3.4 gazillion E = m c² users*! Actually, there are quite a lot of extra features so if I would've released a bit more sensibly over the last months then it probably would've been v2.4 or so already. Anyway...

E = m c² is a free utility that can check various sources for messages, then filter and redistribute them.

A typical example is checking your mailbox and showing you a popup window if there are new messages, but it goes far beyond that since it's fully extensible using plugins. For example, you can check RSS files for new blog posts, monitor when a server comes online or goes offline, play a soundfile when new messages arrive, send a summary email, run a program, write to an RSS file, ...

Basically, you can make E = m c² do all you can think of! And it can do your laundry too (Pro version only**)!

What's new in this version?

  • I'm also releasing the source code from now on (built on .NET 2.0 with Visual Studio 2005).
  • Added support for trigger plugins to provide a list of messages of their own when they trigger. This allows non-pull scenarios to be supported as well (i.e. not only receiving messages by "pulling" Source plugins, but "pushing" to deliver messages from within a trigger immediately).
  • Added support for "hosted" plugins, which don't actively participate in the message cycle but are just running in the context of E = m c². This means they don't handle messages but they can still have their own settings and commands. This can be convenient to host small pieces of functionality with settings that would otherwise require a standalone application.
  • Duplicated the "Change Background" as a separate "hosted" plugin so you can also use it as is, outside of the message cycle.
  • The "Change Background" plugins now support other background styles than just "stretch". The new StretchedWithAspectRatio option retains the aspect ratio of the original image while stretching it to fit the screen.
  • Added "Yahoo! Mail" plugin that retrieves messages from Yahoo! Mail (but you need to make sure that your account is still using the "classic" user interface for this to work).
  • Added "Network Changed" trigger, which triggers when the network changes (e.g. when you plug in or out of a network).
  • Added "Debug" publisher, which writes messages to the Windows debug stream.
  • The "Viewer" can now have message styles to color rows depending on certain message properties, e.g. the source of a message or its subject.
  • The "Viewer" can now be configured to have an initial window state, e.g. to make it start as maximized.
  • The template processor can now also use reflected properties, so not just the built-in tokens as before - e.g. $(Sender) - but also tokens that are taken from the value of a property on the (optionally subclassed) message at runtime. This provides better templating support for messages that are subclassed from the base EmcMessage class.
  • The "RSS Source" plugin now attempts to remember the read items between sessions of the application.
  • The "RSS Publisher" plugin now also writes the message recipients to the RSS feed so that a client also has access to the original message recipients.

You can find all information, screenshots, downloads, and even tutorials on writing plugins at the dedicated website: http://jelle.druyts.net/emc/.

Now I can continue working on the next version, which should turn out to be quite interesting. I already have a number of features lined up and semi-implemented that will take it to a next level. So I think that'll make it v3.0 and non-arguably the most interesting download of 2008 galaxywide***.

I'm even thinking of changing the name to something more meaningful and search-engine-friendly. I don't want to keep steeling Google Love™ from my good friend Albert, you know... So any suggestions would be welcome, and if yours makes it then you'll get a Pro version for free**!

* Usage information obtained by bribing a guy with a moustache who works at a local internet provider and promised me the number of downloads was legit.
** Ha ha! There isn't really a Pro version!
*** I might be a bit biased.

Blog | General | Programming | .NET | Emc | Download
Friday, December 28, 2007 2:15:25 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Setting up Source Server for TFS Builds#

[Update 30/04/2008] Fixed issue for Build Definition names that contain spaces.

[Update 08/07/2008] Updated for new version of Debugging Tools for Windows; added Troubleshooting chapter.

[Update 05/08/2008] Updated "srcsrv.ini" file contents for new version of Source Server script (does not include cmd.exe anymore).

[Update 20/03/2009] Added troubleshooting part for "Illegal characters in path" error.

[Update 30/03/2009] Updated "Illegal characters in path" patch to also include the srcsrv.dll file (in addition to symsrv.dll).

I've read about symbol servers and source servers before (in John Robbins' excellent Debugging Microsoft .NET 2.0 Applications book, for example, and also due to the recent news that Visual Studio 2008 will support source-code debugging of the .NET Framework Libraries), but never really got around to trying them out since they seemed pretty complex to set up. However, I've recently set up a source server on a Team Foundation build server (twice!), and my eyes have now gone wide open: anybody who references assemblies built by those TFS build servers can now seamlessly debug them.

When an assembly is "source server-enabled", the pdb file will contain the full path and the exact version of the file in source control that was used to build that assembly. If the debugger then enters a method, Visual Studio automatically downloads that correct file, places it in a local cache, and opens it for debugging. This is super sweet!

Now the hard work is already done and available in the Debugging Tools for Windows, but there are some extra steps to take if you want to integrate this in a real Team Build (i.e. on the TFS build server instead of on a local machine) and it also lacks some additional information to get everything working. So I put together a full document that covers all aspects of setting up a source server, modifying the Team Build script, configuring the project files, and finally configuring the development machines.

The full document (PDF) can be downloaded here, and you can read the one-page summary that briefly covers the required steps below. I cannot recommend setting up a source server enough, it's free and takes less than an hour if you simply follow the procedure, and the advantages are - hopefully - obvious.

Setting up Source Server for TFS Builds.pdf (0,98 MB)

Note that this procedure works for both TFS 2005 as TFS 2008, and both VS 2005 as VS 2008.

Installing Source Server on the Build Server

  • Install the Debugging Tools for Windows (at least the "Source Server" component)
  • Install Perl (e.g. ActivePerl)
  • In the tfsindex.cmd file in the Debugging Tools for Windows directory, change the last line to
@call "%~dp0SSIndex.cmd" -SYSTEM=TFS -SYMBOLS=%1 %*
  • In the srcsrv.ini file in that directory, set MYSERVER to the exact URL of your Team Foundation Server (Application Tier)
  • Add the full path to TF.exe (usually "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE" for VS 2005 or "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE" for VS 2008) to the system's Path environment variable
  • Restart the "Team Build Service" (for TFS 2005) or "Visual Studio Team Foundation Build" (for TFS 2008) Windows service
Enabling Source Server in the Team Build Script
  • Add a new target to TFSBuild.proj to be run on "AfterCompile" that calls tfsindex.cmd:
<Target Name="RunSourceServerIndexing">
<Exec Command="&quot;C:\Program Files\Debugging Tools for Windows (x86)\srcsrv\tfsindex.cmd&quot; &quot;$(BinariesRoot)&quot;"
        WorkingDirectory="$(SolutionRoot)" /> </Target> <Target Name="AfterCompile"
        DependsOnTargets="RunSourceServerIndexing" />
Configuring the Project Files
  • Set the Debug Info in Project Properties / Build / Advanced to "full" (Debug configuration) or "pdb-only" (Release configuration)
Configuring the Development Machines
  • In Visual Studio / Tools / Options / Debugging / General: check the "Enable source server support" checkbox
  • Create a new text file named srcsrv.ini in the Visual Studio directory (usually "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE" for VS 2005 or "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE" for VS 2008) that contains the exact following text:
[trusted commands] 
TF.exe view
Sunday, December 09, 2007 7:16:12 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Flickr Schedulr v1.2#

I've just pushed the button to release Flickr Schedulr v1.2.

Flickr Schedulr is a Windows desktop application that automatically uploads pictures to Flickr based on a schedule (e.g. to post a new picture every day at a certain time). It allows you to create a queue of pictures to be uploaded, along with their titles, descriptions, tags, and the photoset into which they should end up. This effectively takes the hassle of uploading pictures at regular intervals away, and allows you to go out and have fun shooting pictures (or drinking beer) while your photoblog is maintained for you.

What's new in this version?

  • You can now choose Groups and multiple Photosets to associate your pictures with.
  • I'm also releasing the source code from now on (built on .NET 2.0 with Visual Studio 2008).
  • I've set up a dedicated Schedulr Group on Flickr, a community site where you can ask questions, give comments, complain about bugs (what, bugs?!) and post pictures - uploaded with Schedulr of course!

As always, you can find all information, screenshots and downloads on the dedicated site at http://jelle.druyts.net/schedulr.

Happy Flickring!

Tuesday, December 04, 2007 8:43:07 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Flickr Schedulr v1.1#

I just released an update of Flickr Schedulr, making it v1.1.

Flickr Schedulr is a Windows desktop application that automatically uploads pictures to Flickr based on a schedule (e.g. to post a new picture every day at a certain time). It allows you to create a queue of pictures to be uploaded, along with their titles, descriptions, tags, and the photoset into which they should end up. This effectively takes the hassle of uploading pictures at regular intervals away, and allows you to go out and have fun shooting pictures (or drinking beer) while your photoblog is maintained for you.

What's new in this version?

  • You can now move pictures directly to the top or bottom of the upload queue.
  • You can now shuffle the upload queue to make a randomized list of pictures to upload.
  • You can now upload all selected pictures in the upload queue with the "Upload Now" button.
  • You can now get inline previews of the pictures inside the Queued Pictures list.
  • You can now import and export the current configuration (containing queued and uploaded pictures).
  • You can now see how many items are in the queue and which one you are editing.
  • You can now easily navigate up and down in the queue from anywhere with the ALT+UP and ALT+DOWN keys.

As always, you can find all information, screenshots and downloads on the dedicated site at http://jelle.druyts.net/schedulr.

Happy Flickring!

Monday, November 12, 2007 11:23:04 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Popfly Explorer: Nifty & Smooth!#

Well finally: my good buddy Steven has at last done something useful since arriving at Microsoft Corp. a little over a year ago :-) Although Popfly itself has been getting a lot of good vibe and is certainly a cool playground for mash-ups and showcase for Silverlight, the recently announced Popfly Explorer is something that "clicks" with me a lot more. In other words: something I'll actually use :-)

Basically, Popfly Explorer is a tool window in Visual Studio (all editions, even Express!) that allows you to connect to a Microsoft server to upload your projects, create web sites and even host them directly on their servers, and build a community of friends where you can share code and comment on each other's stuff. So for me, the fun part is where you can directly upload your current solution to Popfly, switch to another machine, download it again and continue working. It's sort of like a Shelveset in Team Foundation Server, but then without the Team Foundation Server. And for free.

When he first described this to me, I was like "hey, cool, it's like CodePlex LightTM(R)(C) or something". And then Steven was all "yeah dude that's right". And I was like "yo wicked bro". And he was all like "yeah, it's so totally sweet". And then I tried it, and it actually worked, and everything went smooth, and I was all "w000000t"!

Anyway. Soma announced it a few days ago on TechEd EMEA, Steven posted a quick overview on Popfly Explorer (by the way, check out the cool picture of Steven as a kid on the logon screen!) and Dan Fernandez followed up with a bigger post describing how to build and publish web sites with Popfly Explorer. I encourage you to check it out, yo! Download Popfly Explorer Beta and get to the party!

Blog | Programming | .NET | VS.NET
Saturday, November 10, 2007 8:07:56 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Announcing: Flickr Schedulr#

One week ago it was only an idea, last weekend and the past evenings were development and beta testing, and now I'm releasing version 1.0 of Flickr Schedulr!

Flickr Schedulr is a Windows desktop application that automatically uploads pictures to Flickr based on a schedule (e.g. to post a new picture every day at a certain time). It allows you to create a queue of pictures to be uploaded, along with their titles, descriptions, tags, and the photoset into which they should end up.

This effectively takes the hassle of uploading pictures at regular intervals away, and allows you to go out and have fun shooting pictures (or drinking beer) while your photoblog is maintained for you.

I'm using this myself (of course) to post my pictures to Flickr every other day at seven in the evening.

You can find all information, screenshots and downloads on the dedicated site at http://jelle.druyts.net/schedulr. If you use it, I'd love to know about it! Please give me some feedback and feature requests :-)

Happy Flickring!

Friday, October 19, 2007 5:54:03 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Proxy Monitor 1.1#

Due to numerous feature requests (hey Brian, hi Sven), I've added two features to Proxy Monitor: the possibility to set the proxy bypass list, and the option to disable notifications (balloon tips) when the proxy changed. So this is basically a small upgrade to version 1.1.

Proxy Monitor is a small application that monitors the network and auto-detects the internet proxy server to use. It can be started as a regular application, which will make it run as an icon in the system notification area. When started, it will auto-detect the proxy server to use. It will also automatically re-detect the proxy server when the computer’s network address has changed. The application can also be run from the command-line with the /detect flag to auto-detect the proxy and exit immediately (e.g. when the computer starts up).

You can download the tool below. Don't hesitate to contact me for suggestions or bug reports!

The ProxyMonitor 1.1.0 executable (101 KB)

You can also view the Readme file online.

Saturday, September 15, 2007 2:36:14 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Introducing SmartPass: free password manager for Smartphones#

SmartPass is a free, small and easy-to-use password manager for Windows Mobile 5.0 Smartphones.

SmartPass uses the highly secure Rijndael encryption algorithm to store all your passwords on the device in a single file, which is then protected by one master password. Now you can always keep your passwords safely with you, wherever you go.

To install SmartPass, simply copy the CAB file to your Smartphone and open it through File Explorer. It will then be installed and a shortcut will automatically be added to the Start Menu. Note that the .NET Compact Framework 2.0 needs to be installed on the device for SmartPass to run.

Download the latest version here:

The SmartPass CAB file (111 KB)

Here are some screenshots to give you a taste of what it looks like:

   

   

Thursday, June 28, 2007 6:53:18 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Just released: E = m c² version 2.0!#

Background

I started rewriting my pet project from scratch in .NET 2.0 last summer, and I'm proud to announce version 2.0 of E = m c² (or should I say E = m c2.0) is now finally all polished and ready! I'm actually pretty happy with it (and using it all day every day), so to make it stand out a bit more, I've created a separate site for it at http://jelle.druyts.net/emc/. That's where you can find all the information and download this free tool.

By the way, E = m c² (apart from a silly little formula relating an object's mass to its energy content) stands for Extensible Message Checker 2.0, because it's a tool that can check for messages (in the broadest sense) and, well, it's extensible (fully driven by plugins that you can easily create yourself).

Introduction

E = m c² is a utility that can check various sources for messages, then filter and redistribute them.

A typical example is checking your mailbox and showing you a popup window if there are new messages, but it goes far beyond that since it's fully extensible using plugins. For example, you can check RSS files for new blog posts, monitor when a server comes online or goes offline, play a soundfile when new messages arrive, send a summary email, run a program, write to an RSS file, ...

Basically, you can make E = m c² do all you can think of! And it can do your laundry too (Pro version only)!

Features

Runtime

  • Fully extensible using plugins.
  • Settings are saved to an xml file so you can easily edit and copy it.
  • Certain settings can be encrypted if needed by plugins (e.g. passwords).
  • Runs in the background, it's basically just an icon in the system notification area.
  • Easy to install, set up and remove (if you could live without it).

Plugins

  • Check POP3 and IMAP email.
  • Check Outlook Web Access (OWA) and Outlook Mobile Access (OMA) email.
  • Check RSS 2.0 feeds.
  • Monitor the file system, i.e. get notified when directories or files change.
  • Monitor server status (by ping or by checking a url).
  • Filter messages by content.
  • Write new messages to a text file or an RSS 2.0 feed.
  • Show a desktop alert, a message box or a rich message viewer when messages arrive.
  • Run a program, change the desktop background, play a sound or send an email when messages arrive.
  • ...And you can easily write your own!

Alright, enough already, where is it?

You can find all information, screenshots, downloads, and even tutorials on writing plugins at the dedicated website: http://jelle.druyts.net/emc/. Did I mention it's free?

Blog | General | Programming | .NET | Emc | Download
Sunday, April 15, 2007 4:21:17 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

 

MVP & MSFT!#

Woohoo, I've just been awarded as a Most Valuable Professional (MVP) by Microsoft!

Dear Jelle Druyts,

Congratulations! We are pleased to present you with the 2007 Microsoft® MVP Award!

The Microsoft MVP Award is our way of saying thank you and to honor and support the significant contributions you make to communities worldwide. As a recipient of Microsoft’s Most Valuable Professional award, you join an elite group of technical community leaders from around the world who foster the free and objective exchange of knowledge by actively sharing your real world expertise with users and Microsoft. Microsoft salutes all MVPs for promoting the spirit of community and enhancing people’s lives and the industry’s success everyday. To learn more about the MVP Program, visit: www.microsoft.com/mvp.

Your extraordinary efforts in Visual Developer - Visual C# technical communities during the past year are greatly appreciated.

On behalf of everyone at Microsoft, thank you for your ongoing contributions to Visual Developer - Visual C# technical communities.

Sincerely,

Candice Pedersen, MVP Program Manager

On a related note (Tom already mentioned it briefly), I was also "awarded" with a full time employee contract by Microsoft :-) So this effectively means I've changed jobs to the Microsoft Services division at Microsoft BeLux (no, not Microsoft Corp like the other MVP's of this country seem to do all the time ;-) ) as of this week. I've had a great time at the Compuware Professional Services division the last years, and I really want to wish the team all the best! Now, I'm taking up the challenge of representing Microsoft for our customers as a development consultant, and making sure their development efforts are running as smoothly as possible!

Wednesday, April 04, 2007 11:49:49 AM (Romance Standard Time, UTC+01:00) #    Comments [13]  | 

 

Interview on Guidance Automation Toolkit#

Right after my session at the Developer & IT Pro days last week, I was interviewed by the Wygwam team - which was pretty cool :-) It also serves as a pretty good 5-minute introduction to Guidance Automation Toolkit, so check it out!


Video: DevITProDays - Jelle

Wednesday, April 04, 2007 11:45:36 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

 

Developer & IT Pro Days wrap-up#

I had a great time at the Belgian Developer & IT Pro Days in Ghent last week, many thanks to the organizing team (great job Tom, David, Arlindo, Ritchie, Wim and everybody else)!

If you came to my sessions last year, you might remember that I ended a little early (25 minutes!) on my Framework Design talk and a little late (10 minutes!) on the Application Development talk with Steven Wilssens, so I was pretty happy that I finished right on the minute this year :-)

You can download the slides here: A Deep Dive Into The Guidance Automation Toolkit (3 MB). And don't forget about the Guidance Automation Series that goes into much more detail.

Now for a quick shout out to my fellow community members: congratulations Jan on the birth of your first child Fran! From the pictures, it looks like she's already well into the Microsoft spirit :-) And congratulations Bart on joining Microsoft Corp on the WPF team! I'm sure you'll fit right in with the rest of the brainiacs on campus! Say hi to Steven from me :-)

Wednesday, April 04, 2007 11:06:24 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Speaking at Microsoft Developer & IT Pro Days 2007 in Belgium!#

A lot of people have already noticed that I'm on the speaker agenda for the Microsoft Developer & IT Pro Days 2007 in Belgium, but here's the official word: I'm proud to be your host for a session in the MSDN track!

A Deep Dive Into The Guidance Automation Toolkit (Or: Sit Back And Make It Do Your Work)

28 March 2007 - 10:45 - 12:00

The Guidance Automation Toolkit provides a means for solution architects and developers to deploy consumable guidance to other developers. If this seems a little vague and “out there”, then this session is for you: you will see what the Guidance Automation Toolkit really is, how it works, what it can and cannot do, how it fits into Microsoft’s overall Software Factories vision and generally how you can use it to make your life easier.

If you've ever published a 40-page document full of guidelines, do's and don'ts on how to structure solutions and projects in your company and what the namespaces and class names should be, if the developers need to be able to start new projects quickly, if you have a need for code generation, if you’re thinking of building your own Software Factory, or all of the above – this session will help you solve some real-world problems today.

Satisfy your Technical Curiosity!

See you there? I hope so!

Tuesday, February 06, 2007 10:37:18 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Proxy Monitor 1.0#

Getting tired of switching your Internet Explorer proxy settings every time you plug your laptop in your home network after a day's work? Or when connecting to the corporate VPN that uses yet another proxy server? Then I've got the tool for you...

Proxy Monitor is a small application that monitors the network and auto-detects the internet proxy server to use. It can be started as a regular application, which will make it run as an icon in the system notification area. When started, it will auto-detect the proxy server to use. It will also automatically re-detect the proxy server when the computer’s network address has changed. The application can also be run from the command-line with the /detect flag to auto-detect the proxy and exit immediately (e.g. when the computer starts up).

It's taken me a while to release this tool, but the good news is that I've been using it every day for over two months so I guess that counts as an extensive beta period :-)

You can download the tool below. Don't hesitate to contact me for suggestions or bug reports!

The ProxyMonitor 1.0.60905 executable (101 KB)

You can also view the Readme file online.

Tuesday, October 17, 2006 9:00:38 PM (Romance Standard Time, UTC+01:00) #    Comments [9]  | 

 

GAS07: Renaming the solution, signing projects and showing documentation#

[This is episode 7 of the Guidance Automation Series]

It's been a while since the last episode, but I've finally found the time to finish the (long awaited) grand finale of the Guidance Automation Series.

Last time, we looked at class generation using the powerful T4 text templating engine. Armed with all the knowledge up to here, it's already perfectly possible to build an entire guidance package that allows the developer to create Enterprise Library Application Blocks with a minimal amount of effort. As I said in the introduction to the Guidance Automation Series, I'm not going to build that guidance package here. I'll just show you some more tricks you can teach your guidance package to perform... This time, we'll be adding a strong name to our assemblies by signing it with a key and see how we can rename the solution's file name.

Renaming The Solution

We'll start off with the relatively easy task of renaming the solution. Although the developer has already specified a solution name, in a lot of organizations it's required that the solution also adheres to a certain naming convention. And possibly, this depends on some fields that the user can define in the wizard belonging to the Guidance Package, such as a namespace. The only problem is, our Guidance Package doesn't kick in until after the developer has specified a name for the solution file - and now it's obviously too late to rename it. With a little trick, however, it's perfectly possible to change the solution name, e.g. to ApplicationBlockNamespace.ApplicationBlockName.sln in our Guidance Package for Enterprise Library application blocks.

To do this, we will need a custom action class that performs this magic. We can create a custom action by deriving from the Microsoft.Practices.RecipeFramework.Action class. An action can take inputs and create outputs by defining properties that are decorated with the Input or Output attributes:

public class RenameSolutionAction : Action
{
    /// <summary>
    /// The new name of the solution.
    /// </summary>
    private string m_NewSolutionName;

    /// <summary>
    /// Gets or sets the new name of the solution.
    /// </summary>
    [Input(Required = true)]
    public string NewSolutionName
    {
        get
        {
            return m_NewSolutionName;
        }
        set
        {
            m_NewSolutionName = value;
        }
    }

    /// <summary>
    /// Executes the action.
    /// </summary>
    public override void Execute()
    {
    }

    /// <summary>
    /// Performs an undo of the action.
    /// </summary>
    public override void Undo()
    {
    }
}

At this point, our custom action doesn't really do anything but you can see that it overrides the Execute and Undo methods that are called when the action is run and when it needs to be undone.

The actual work of renaming the solution can now be performed in the Execute method by using the EnvDTE objects of the Visual Studio SDK. In our case, we'll perform the rename by simply doing a "Save As" of the solution using the new name, and then deleting the original solution file and its associated suo file:

public override void Execute()
{
    DTE vs = this.GetService<DTE>(true);

    string newFilename = this.NewSolutionName;
    string originalSolutionPath = (string)vs.Solution.Properties.Item("Path").Value;
    string originalSolutionDir = Path.GetDirectoryName(originalSolutionPath);

    // Check if it is an absolute or relative path.
    if (!Path.IsPathRooted(this.NewSolutionName))
    {
        // Relative path from the current solution root.
        newFilename = Path.Combine(originalSolutionDir, this.NewSolutionName);
    }

    // Make sure the destination directory exists.
    Directory.CreateDirectory(Path.GetDirectoryName(newFilename));

    // Save the current solution as the new file name.
    vs.Solution.SaveAs(newFilename);

    // Delete the old solution.
    File.Delete(originalSolutionPath);
    string suoFile = Path.GetFileNameWithoutExtension(originalSolutionPath) + ".suo";
    string suoFilePath = Path.Combine(originalSolutionDir, suoFile);
    File.Delete(suoFilePath);
}

Guidance Automation Tip #3: The vs.Solution.FileName or vs.Solution.FullName properties do not return a value while the solution is being created. You can use "string solutionPath = (string)vs.Solution.Properties.Item("Path").Value;" to get the full path of the solution.

Now that our action is created, we can register it in the Guidance Package's main XML configuration file in the <Actions> section:

<Action Name="RenameSolution" Type="JelleDruyts.EnterpriseLibraryGuidance.Actions.RenameSolutionAction, JelleDruyts.EnterpriseLibraryGuidance">
  <Input Name="NewSolutionName" RecipeArgument="FinalSolutionName" />
</Action>

The action takes an input argument named NewSolutionName (as defined in the RenameSolutionAction class), which is taken from a Recipe Argument named FinalSolutionName. This argument is again a "derived" argument, which is composed of the application block namespace and name through an evaluator:

<Argument Name="FinalSolutionName">
  <ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).sln">
    <MonitorArgument Name="ApplicationBlockNamespace" />
    <MonitorArgument Name="ApplicationBlockName" />
  </ValueProvider>
</Argument>

Signing The Projects

As a last step, we want to enable the developer to sign the generated projects with a strong-name key. This means we will need to generate a key-file dynamically (by using the sn.exe tool that ships with the .NET SDK), and that we need to configure the generated projects to use the key to sign the assemblies.

In our specific case, signing the Enterprise Library Application Block assemblies has an extra risk: this will only work if Enterprise Library itself is signed because a signed assembly cannot reference an unsigned assembly. Because Enterprise Library is not signed by default, we will need to make this step optional so the developer can choose whether or not to sign the projects. This means we will need an extra argument (that defaults to false) and a field in the wizard:

<Argument Name="EnableStrongNaming" Required="true" Type="System.Boolean">
  <ValueProvider Type="Evaluator" Expression="false" />
</Argument>
...
<Field ValueName="EnableStrongNaming" Label="Enable strong-naming">
  <Tooltip>Only enable strong naming if you have a build of Enterprise Library that is itself strong-named.</Tooltip>
</Field>

Now the trick to signing the projects when they are generated is actually quite simple: there are two settings in the C# project file that are required: SignAssembly must be set to true, and AssemblyOriginatorKeyFile must be set to the path to the key file. We can do this by using recipe arguments in the .csproj template files as such:

<SignAssembly>$EnableStrongNaming$</SignAssembly>
<AssemblyOriginatorKeyFile>$AssemblyOriginatorKeyFile$</AssemblyOriginatorKeyFile>

The developer has already specified the EnableStrongNaming argument through a field in the wizard. Now the AssemblyOriginatorKeyFile needs to point to a valid path to a key file in case strong naming is enabled, or it should be an empty string if strong naming is not enabled. This can be performed by using a custom value provider that checks the EnableStrongNaming argument and determines which string to return: either a string containing the path to the key-file, or an empty string. I won't go into the details here, since Value Providers were already covered in a previous article in this series, but I just want to stress one important point when developing them:

Guidance Automation Tip #4: When writing a Value Provider, returning null means "don't use this value".

This means that if you would return null in the Value Provider that provides a value for the AssemblyOriginatorKeyFile argument and the developer chose not to strong-name the assemblies, the $AssemblyOriginatorKeyFile$ would not get replaced at all in any template! If you want the argument to be replaced with an empty string, be sure to return an empty string from the Value Provider as well:

if (!enabled)
{
    newValue = string.Empty; // Not null!
}
else
{
    newValue = string.Format(@"..\{0}", keyFileName);
}

Now that we have a Value Provider for the path to the key-file that also works when the developer chose not to use strong-naming, we can register the following recipe arguments to define the KeyFileName (again based on the Application Block namespace and name) and the AssemblyOriginatorKeyFile arguments:

<Argument Name="KeyFileName">
  <ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName).snk">
    <MonitorArgument Name="ApplicationBlockNamespace" />
    <MonitorArgument Name="ApplicationBlockName" />
  </ValueProvider>
</Argument>
<Argument Name="AssemblyOriginatorKeyFile">
  <ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.KeyFilePathValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 KeyFileNameArgument="KeyFileName" EnableStrongNamingArgument="EnableStrongNaming">
    <MonitorArgument Name="KeyFileName" />
    <MonitorArgument Name="EnableStrongNaming" />
  </ValueProvider>
</Argument>

The only remaining work is to actually generate the key file itself and add it to the Visual Studio solution. This can be implemented as a custom action again, in which we will perform the following work:

public override void Execute()
{
    if (this.EnableStrongNaming)
    {
        EnvDTE.DTE vs = this.GetService<EnvDTE.DTE>(true);
        string solutionPath = (string)vs.Solution.Properties.Item("Path").Value;
        string solutionDir = Path.GetDirectoryName(solutionPath);
        string keyFilePath = Path.Combine(solutionDir, this.KeyFileName);

        // Find the .NET SDK directory to execute the sn.exe command.
        string visualStudioDir = Path.GetDirectoryName(vs.Application.FileName);
        string sdkDir = Path.Combine(visualStudioDir, @"..\..\SDK\v2.0\Bin");
        string snPath = Path.Combine(sdkDir, "sn.exe");
        if (!File.Exists(snPath))
        {
            throw new InvalidOperationException("Could not find the Visual Studio SDK directory to execute the sn.exe command.");
        }

        // Make sure the directory exists.
        Directory.CreateDirectory(Path.GetDirectoryName(keyFilePath));

        // Launch the process and wait until it's done (with a 10 second timeout).
        ProcessStartInfo startInfo = new ProcessStartInfo(snPath, string.Format("-k \"{0}\"", keyFilePath));
        startInfo.CreateNoWindow = true;
        startInfo.UseShellExecute = false;
        Process snProcess = Process.Start(startInfo);
        snProcess.WaitForExit(10000);

        // Add the key file to the Solution Items.
        DteHelper.SelectSolution(vs);
        vs.ItemOperations.AddExistingItem(keyFilePath);

        // The AddExistingItem operation also shows the item in a new window, close that.
        vs.ActiveWindow.Close(EnvDTE.vsSaveChanges.vsSaveChangesNo);
    }
}

This custom action uses an input parameter named KeyFileName which is the recipe argument we defined before. We have also passed in the EnableStrongNaming argument to know if we actually need to generate the key file. We then find the path to the .NET SDK by first looking at the application directory of Visual Studio (vs.Application.FileName) and then using a relative path to the SDK. We check for the presence of the sn.exe application, and start a process (without a window to avoid the DOS box from popping up) to generate the key file. When it's done, we add the key file to the solution. Note the special way of adding a file to the "Solution Items" special folder: we're first selecting the solution in Solution Explorer (through the DteHelper class), and then calling the AddExistingItem method with the path to the item. This seems to be the easiest and most robust way of doing this, since the "Solution Items" folder is handled quite differently and it wouldn't be enough to just create a folder with that name. In the end, we also close the active window, because by default the added item is shown in a new window - which doesn't make a lot of sense for a binary key file.

Now that we have all this infrastructure in place, running the Guidance Package with strong naming enabled results in the following solution, properly renamed, containing the strong name key-file and with both projects enabled for signing:

Showing Documentation

One of the nice added features in the latest version of the Guidance Automation Toolkit is the Guidance Navigator, which provides an overview of the currently loaded Guidance Packages and the recipes they provide. It can also show an HTML page containing more information and links to online resources, to provide continuous guidance to developers using the package.

Showing an HTML page in this Guidance Navigator window is as simple as adding the following XML section to the top of the Guidance Package's main XML file, as the first node inside the main <GuidancePackage> tag:

<GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core" ...>
  <Overview Url="Templates\Doc\Overview.html" />

The Url points to the HTML page to be shown, of course, and I've chosen to store this in the Templates\Doc directory. When the solution is created or opened, the Guidance Navigator window will show the page as such:

Where Are We

At this point, we've added some custom actions to rename the solution and we've also given the developer the option to sign the assemblies with a strong name.

I think I've covered quite a lot of scenarios already to end the Guidance Automation Series right here. Any additional tips and tricks I discover will of course be posted here, so keep an eye on my blog if you're interested in the Guidance Automation Toolkit!

Download the source code for the current state of the Guidance Package.

Monday, September 18, 2006 7:00:28 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

 

Slides for my Visug session on Guidance Automation Toolkit#

Thanks to everyone that showed up yesterday at the Visug session, where I tried to show you the power of the Guidance Automation Toolkit. It was great to see such an interested crowd, and to get some excellent questions. The meat of the presentation was in the demos, but if you want to take a look at the slides, here you go:

Guidance Automation Toolkit.ppt (1,25 MB)

Thursday, August 10, 2006 8:47:36 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Visug Session: Guidance Automation Toolkit#

If you've been looking at my Guidance Automation Series posts lately but didn't really "feel" it yet, I have good news for you: on Wednesday August 9th I'll be giving a session at the Belgium Visual Studio User Group (Visug) on the Guidance Automation Toolkit. This should give you a good overview of the technology and hopefully show you why I'm so excited about its possibilities when undertaking large developments projects.

You can register for this free session simply by adding a comment to the Visug announcement post. Here's the official announcement:

The Guidance Automation Toolkit provides a means for a solution architect or lead developer to deploy consumable guidance to developers.

If you've ever published a 40-page document full of guidelines, do's and don'ts on how to structure solutions and projects in your company and what the namespaces and class names should be, if the developers need to be able to start new projects quickly, if you have a need for code generation, or all of the above - this toolkit is for you. Microsoft is itself using this technology to publish their Software Factories, like the Web Service Software Factory, so that says something about the quality and future of this fine piece of software.

In this presentation, Jelle will show you how this works and how to build your own automated guidance so you too can benefit from its huge number of features.

Hope to see you there!

Tuesday, July 25, 2006 5:35:15 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Type Name Suffixes#

Back when I was preparing my "Best Practices In Framework Design" talk for the Developer & IT Pro Days in February, I got the following question during my dry run: “Why do you need to suffix exceptions (e.g. ArgumentException) and attributes (e.g. ObsoleteAttribute) but not enums (e.g. Color, not ColorEnum)?”.

I didn’t really have an answer other than "because Microsoft said so" at that time, but I just discovered the reason behind it while re-reading the excellent Framework Design Guidelines book. It’s the following guideline that makes the difference:

Consider ending the name of derived classes with the name of the base class.

So you should suffix exceptions and attributes, simply because they inherit from the System.Exception and System.Attribute base classes:

public class ArgumentException : Exception { ... }
public class ObsoleteAttribute : Attribute { ... }

Enums - and delegates as well for that matter - should not be suffixed because they don’t (explicitly) inherit from an enum or delegate base class:

public enum Color { ... }
public delegate void ClickEventHandler(...)

Behind the scenes, the compiler will make an enum a subclass of System.Enum and a delegate of System.MulticastDelegate, but you don’t explicitly state this in most languages and in fact you can’t build inheritance hierarchies with these types. So they don’t need to follow the guideline.

Maybe it doesn't really matter that much to you, but I thought it was a nice piece of background information on the "why" anyway :-)

Thursday, July 20, 2006 9:03:22 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

GAS06: Generating Classes#

[This is episode 6 of the Guidance Automation Series]

Last time, we added references to Enterprise Library and made sure the build output was copied to the configuration tool directory. Now to make the application block show up in this configuration tool, it needs to register some classes and commands so that the tool knows how to display it. Without implementing the full works here, let's see how we can already generate some classes that will serve as an excellent starting point to implement the rest of the application block.

Adding Default Classes

Actually, we've already seen how to add classes in the first episode of this series: the AssemblyInfo.cs file was added to the project by including it in a ProjectItem inside the project's vstemplate file and by including it in the C# project file (so that MSBuild will build it). The only extra feature we'll be using here is to change the target file name of each file to include the name of the application block.

To make it possible to add the application block in the configuration tool, we have to define a ConfigurationDesignManager class and register it in the AssemblyInfo.cs file. So we need to update that file and add a bunch more, including a CommandRegistrar, a NodeMapRegistrar and a root settings node. For the purpose of all these classes and how they should be implemented, I'll gladly refer to Mark Seemann's excellent article on "Speed Development With Custom Application Blocks For Enterprise Library" again. All we need to do here is provide the minimum amount of information to get it registered with the configuration tool and leave the rest up to the actual developer of the application block.

So we add all the necessary files to the project template, include them in the C# project file and register them in the vstemplate file as such:

<ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$CommandRegistrar.cs">CommandRegistrar.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$ConfigurationDesignManager.cs">ConfigurationDesignManager.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$NodeMapRegistrar.cs">NodeMapRegistrar.cs</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="$ApplicationBlockName$SettingsNode.cs">SettingsNode.cs</ProjectItem>
<ProjectItem ReplaceParameters="false" TargetFileName="$ApplicationBlockName$SettingsNode.bmp">SettingsNode.bmp</ProjectItem>

Notice the TargetFileName attribute here, to specify a new name for the file so that it includes the name of the application block as it was entered by the user. Also note that we're adding a bitmap file here (without replacing any parameters, to make sure we're not accidentally messing with the binary data), which is also added in the C# project file but then as an embedded resource:

<ItemGroup>
  <EmbeddedResource Include="$ApplicationBlockName$SettingsNode.bmp" />
</ItemGroup>

That's all there is to it. If we now register and run the Guidance Package, we'll get a project that includes all of these files. If we build it (without touching anything), it will automatically get copied into the configuration tool's directory (because of the post-build event), and we can already add the application block to an application directly!

Generating Classes

Now that we already have a root node in the project, suppose we want to add more nodes for the configuration tool. The number of nodes in the application block depends on what the developer has in mind and could even be unknown when the application block is being created. So in stead of adding more classes at the time the solution is unfolded, let's add a command to Visual Studio that allows us to generate a class on-demand. This can be done by adding a RecipeReference to a Visual Studio Template or by adding a separate template for the class in the Templates\Items directory.

  • With a RecipeReference, you have full control over the process: when the recipe is executed, you can choose to show a dialog box prompting for more information or you can go directly to work by executing actions. A drawback is that you also need to include the actual actions to generate the class content and add it to the project, although this isn't all too hard.
  • With a Template Reference, you will always get the "Add new item" dialog box that allows the developer to choose the new class' filename. This can be a drawback if you need to control the filename as well, but this mechanism does make it easier to generate the class and add it to the project.

Since adding the class as a Template Reference is very similar to adding a solution as a Template Reference (like we've already been doing all along), let's implement the new nodes as RecipeReferences. To allow the developer to add dynamically created items to a project, we need to attach a reference to a recipe in that project's vstemplate file. In this case, we'll add the following information to the vstemplate file of the DesignTime project:

<WizardData>
  <Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0">
    <References>
      <RecipeReference Name="AddConfigurationNode" Target="/" />
    </References>
  </Template>
</WizardData>

The References node has been added to register the AddConfigurationNode recipe to the root of the project (specified by the target). If there would be subdirectories, you could scope the target down by setting Target="/Nodes", for example.

The AddConfigurationNode recipe needs to be defined in the package's main xml file as such:

<Recipe Name="AddConfigurationNode">
  <Caption>Configuration Node...</Caption>
  <Description>Adds a Configuration Node class.</Description>
  <HostData>
    <Icon Guid="FAE04EC1-301F-11d3-BF4B-00C04F79EFBC" ID="4542" />
    <CommandBar Name="Project Add" />
  </HostData>
  <Arguments>
    <!-- User Input -->
    <Argument Name="NodeName" Required="true">
      <Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library"/>
    </Argument>
    <!-- Derived Arguments -->
    <Argument Name="CurrentProject" Type="EnvDTE.Project, EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
      <ValueProvider Type="Microsoft.Practices.RecipeFramework.Library.ValueProviders.FirstSelectedProject, Microsoft.Practices.RecipeFramework.Library" />
    </Argument>
    <Argument Name="TargetFile">
      <ValueProvider Type="Evaluator" Expression="$(NodeName).cs">
        <MonitorArgument Name="NodeName" />
      </ValueProvider>
    </Argument>
    <Argument Name="TargetNamespace">
      <Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library"/>
      <ValueProvider Type="Evaluator" Expression="$(CurrentProject.Properties.Item('DefaultNamespace').Value)" />
    </Argument>
  </Arguments>
  <GatheringServiceData>
    <Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0">
      <Pages>
        <Page>
          <Title>Configuration Node</Title>
          <Fields>
            <Field ValueName="NodeName" Label="Node Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." />
          </Fields>
        </Page>
      </Pages>
    </Wizard>
  </GatheringServiceData>
  <Actions>
    <Action Name="GenerateClass" Type="Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates.TextTemplateAction, Microsoft.Practices.RecipeFramework.VisualStudio.Library"
				Template="Text\ConfigurationNode.cs.t4" >
      <Input Name="TargetNamespace" RecipeArgument="TargetNamespace" />
      <Input Name="NodeName" RecipeArgument="NodeName" />
      <Output Name="Content" />
    </Action>
    <Action Name="AddClass" Type="Microsoft.Practices.RecipeFramework.Library.Actions.AddItemFromStringAction, Microsoft.Practices.RecipeFramework.Library"
				Open="true">
      <Input Name="Content" ActionOutput="GenerateClass.Content" />
      <Input Name="TargetFileName" RecipeArgument="TargetFile" />
      <Input Name="Project" RecipeArgument="CurrentProject" />
    </Action>
  </Actions>
</Recipe>

The recipe definition starts off with the regular caption and description elements. The HostData section defines where the recipe should be shown in Visual Studio; in this case we add it only to the project's "Add" context menu (where the "Add new item" and "Add existing item" menus also live) and supply it with an appropriate icon from the C# Project dll again. Then, there's a regular argument that the developer can fill in through the wizard, but the more interesting part is the definition of the other arguments: we're using a new type of value provider that returns the currently selected project in Visual Studio (so we can add the new class to it). There are also some more uses for the ExpressionEvaluatorValueProvider: a simple one to define the filename based on the class name, and a pretty complex one that finds the default namespace from the current project by drilling down into its properties (the nested expression is evaluated at runtime and executed through reflection). Finally, the actions section defines two distinct stages: a first one to generate the class content by executing the TextTemplateAction, which transforms a T4 text template using a quite advanced text processing engine. Afterwards, the generated class content is added to the currently selected project by using the AddItemFromStringAction.

Finally, the template itself looks like this:

<#@ template language="C#" #>
<#@ assembly name="System.dll" #>
<#@ property processor="PropertyProcessor" name="TargetNamespace" #>
<#@ property processor="PropertyProcessor" name="NodeName" #>
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Configuration.Design;

namespace <#= this.TargetNamespace #>
{
    internal sealed class <#= this.NodeName #> : ConfigurationNode
    {
    }
}

This templating syntax looks quite a lot like ASP.NET, so creating and understanding these templates should be relatively straight-forward. We first declare some properties that are inputs from the recipe declaration above. These will actually become strongly-typed properties on the page template object, so we can refer to them later on by using this.NodeName, for example. At this point, the generated class is extremely simple, but you can actually perform very powerful code generation with this T4 templating engine by inlining arbitrary C# or VB.NET code within these ASP.NET-like tags that will drive the code generation process.

Notice that you also need to register the assemblies you require for the code generation process. In this case, we just register the System.dll assembly. If, for example, we needed to use the Visual Studio automation dll (EnvDTE.dll) in the template, we would need to add the following declarations:

<#@ assembly name="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies\EnvDTE.dll" #>
<#@ import namespace="EnvDTE" #>

Failing to do so will give you the following nasty error: "CS0246: Compiling transformation: The type or namespace name 'EnvDTE' could not be found (are you missing a using directive or an assembly reference?)"

With this in place, it's now possible to add a new configuration node through the project's context menu:

Where Are We

At this point, we know how to add default classes to a generated project that have a customized name. Furthermore, we've touched the surface on the very powerful T4 text templating engine that's part of the Guidance Automation framework. Next time, we'll define a custom action that generates a strong name key and configure the projects to automatically be signed with it. We'll also automatically change the solution's file name to something more meaningful.

Download the source code for the current state of the Guidance Package.

Monday, July 03, 2006 8:39:13 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

 

GAS05: Tuning the C# projects#

[This is episode 5 of the Guidance Automation Series]

Last time, we left off with two empty projects that had a project reference between them. If you're still not too thrilled by all that, let's take it one step further: we'll add binary references to Enterprise Library and make sure that the project output is always copied to the Enterprise Library Configuration Tool directory (otherwise, the application block won't show up in the configuration tool). The first one involves knowing where to find those references, the second one involves executing a post-build command that copies the project output to the proper location.

Adding Binary References

Adding a binary reference in itself is pretty straight-forward. The project references are stored in the C# project file (which is an MSBuild file) and can therefore easily be added in the project's template. The only minor difficulty is that we need to provide the hint-path, i.e. the physical location where the assemblies can be found.

In this case, we'll be adding references to Enterprise Library assemblies, so we need to know the directory where Enterprise Library was installed. By default, this is in C:\Program Files\Microsoft Enterprise Library January 2006\bin, but it could have been installed elsewhere so we better verify this with the user. The easiest way, of course, is to make this a recipe argument, just like with the application block's name and namespace:

<Argument Name="EnterpriseLibraryPath" Required="true">
  <ValueProvider Type="Evaluator" Expression="C:\Program Files\Microsoft Enterprise Library January 2006\bin" />
</Argument>

Notice that we're using the ExpressionEvaluatorValueProvider again, but this time without a complex expression. Used in this way, it just sets a default value for the argument that can be modified by the user later on. If we now want to add a binary reference to the Microsoft.Practices.EnterpriseLibrary.Common.dll assembly, for example, we can just add the following lines to the itemgroup containing the references:

<Reference Include="Microsoft.Practices.EnterpriseLibrary.Common, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL">
  <SpecificVersion>False</SpecificVersion>
  <HintPath>$EnterpriseLibraryPath$\Microsoft.Practices.EnterpriseLibrary.Common.dll</HintPath>
</Reference>

Notice that the $EnterpriseLibraryPath$ parameter will automatically be replaced by the directory that the user chose in the wizard. So finally, the wizard's page definition will also need to include the new field:

<Field ValueName="EnterpriseLibraryPath" Label="Enterprise Library Path">
  <Editor Type="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</Field>

This time, we're also providing an editor that allows the user to select the directory more easily using a folder browser window.

Adding A Post-Build Command

Now that we've referenced Enterprise Library, it's also a good time to make sure that the project output is always copied to the Enterprise Library directory. Otherwise, the application block is quite unuseable. The easiest way to do this is to add a post-build command to both projects that simply copy the project's assemblies to the EnterpriseLibraryPath the user has already chosen. Just like with the binary references, this is just a matter of modifying the MSBuild project file to include a new property group as such:

<PropertyGroup>
  <PostBuildEvent>$PostBuildCommand$</PostBuildEvent>
</PropertyGroup>

Creating the post-build command itself is a little trickier: it depends on the EnterpriseLibraryPath argument, and needs to build a more complex string based on that argument's value. The post-build command itself (as defined in Visual Studio) is simply:

COPY /Y $(TargetPath) "<Enterprise Library Path>"

Where we need to replace <Enterprise Library Path> with the value of the EnterpriseLibraryPath argument. So, how can we provide this value to the $PostBuildCommand$ argument? By building our own Value Provider, of course.

We start off by defining the argument itself:

<Argument Name="PostBuildCommand">
  <ValueProvider Type="JelleDruyts.EnterpriseLibraryGuidance.ValueProviders.PostBuildCommandValueProvider, JelleDruyts.EnterpriseLibraryGuidance"
                 EnterpriseLibraryPathArgument="EnterpriseLibraryPath">
    <MonitorArgument Name="EnterpriseLibraryPath" />
  </ValueProvider>
</Argument>

Notice that the value provider is now of our own type, namely the PostBuildCommandValueProvider we'll be building, and that it monitors the EnterpriseLibraryPath argument (since it depends on it). Furthermore, since the value provider needs access to the value of the EnterpriseLibraryPath argument, we also need to specify that argument's name, which is done through a custom EnterpriseLibraryPathArgument attribute on the ValueProvider node.

The code for the Value Provider isn't all that difficult, either. It's a class that inherits from the ValueProvider base class and that also implements the IAttributesConfigurable interface to support that custom EnterpriseLibraryPathArgument attribute. It can then override the OnArgumentChanged method that gets called when a monitored argument has changed, and re-generate the post-build command as such:

public override bool OnArgumentChanged(string changedArgumentName, object changedArgumentValue, object currentValue, out object newValue)
{
    // Get the name of the argument that defines the EnterpriseLibraryPath.
    string enterpriseLibraryPathArgumentName = m_Attributes[EnterpriseLibraryPathArgumentAttribute];
    
    // Get the dictionary that contains the recipe arguments.
    IDictionaryService dictionary = this.GetService<IDictionaryService>(true);

    // Get the current value of the EnterpriseLibraryPath argument.
    string enterpriseLibraryPath = (string)dictionary.GetValue(enterpriseLibraryPathArgumentName);

    // Create the post-build command.
    newValue = string.Format("COPY /Y \"$(TargetPath)\" \"{0}\"", enterpriseLibraryPath);
    return true;
}

Now we have an argument containing the post-build command that automatically contains the proper directory based on the Enterprise Library directory the user has chosen in the wizard dialog:

Where Are We

We've seen how to add binary references to the C# projects by modifying the MSBuild files and gathering user input through the wizard. We've also built a custom Value Provider that depends on the value of another argument, and used that as a post-build command. Next time, we'll see how we can generate some classes so that the application block can already be added to the Enterprise Library Configuration Tool.

Download the source code for the current state of the Guidance Package.

Thursday, June 29, 2006 8:19:25 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

GAS04: Adding Project References#

[This is episode 4 of the Guidance Automation Series]

Last time, we did a whole lot of work just to generate one very empty C# project. Just think of that as the healthy dish before the dessert: it's probably necessary but not really what you've been waiting for. This time, we'll really start using the power of Guidance Automation to generate a second project and add a project reference to it.

Adding Another Project

Now that we have seen how to generate the runtime project for the Enterprise Library Application Block, we'll just do the same thing to generate the design-time project. By convention, this has the same name as the runtime project, with ".Configuration.Design" added at the end. This is easily done in the solution's vstemplate file by adding a second project to the ProjectCollection node as such:

<ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$.Configuration.Design">Projects\DesignTime\DesignTime.vstemplate</ProjectTemplateLink>

For this to work, we also need the DesignTime.vstemplate file in the proper location, but that's pretty easy to do: we can just copy the Runtime.vstemplate file and associated project files and make a few basic modifications to change the name. So with very little additional work, we have now generated two empty projects already.

Now of course, this design-time project will need information from the runtime project (e.g. the configuration types of the application block that are used by the runtime but need to be configured in the design-time project), so we need to add a project reference to it. Project references are actually stored in the referencing project's project file, but this isn't something we can generate out-of-the-box like before because it uses the GUID from the referenced project. This GUID is generated by Visual Studio at the time the solution is unfolded (remember the $guid1$ parameter) and we don't have access to that GUID outside the scope of that file. But Visual Studio knows how to add Project References, so we actually want to have the environment perform some action for us.

Actions

Actions are actually a big part of Guidance Automation, and they represent pieces of code that can run at the end of a recipe (and in other places too, but let's stick to the current problem). There are a lot of predefined actions in the Guidance Automation framework, but you can also define your own. To use an action, it can be registered in the Actions section in the recipe xml file, so that each one will be run in sequence at the end of the recipe. Actions have input and output arguments that can be configured, and it is important to note that these are strongly typed, which means that you're really passing objects from and actions and not just strings. The input arguments can either come from the recipe (e.g. a value the user has entered in the wizard), or they can be the output of another action.

Adding The Project Reference

To add a Project Reference, the predefined AddProjectReferenceAction seems to be just what we need here. It takes two input arguments: the referring project and the referenced project. Both are of the type EnvDTE.Project, meaning that they represent the actual project automation objects in Visual Studio. So we need a way to retrieve these projects, and that's where another action, aptly named GetProjectAction comes into play. This searches for a project based on its name and puts the project in an output argument so we can chain the two together as follows:

<Actions>
  <Action Name="GetRuntimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library">
    <Input Name="ProjectName" RecipeArgument="RuntimeProjectName" />
    <Output Name="Project" />
  </Action>
  <Action Name="GetDesignTimeProject" Type="Microsoft.Practices.RecipeFramework.Library.Actions.GetProjectAction, Microsoft.Practices.RecipeFramework.Library">
    <Input Name="ProjectName" RecipeArgument="DesignTimeProjectName" />
    <Output Name="Project" />
  </Action>
  <Action Name="AddProjectReference" Type="Microsoft.Practices.RecipeFramework.Library.Solution.Actions.AddProjectReferenceAction, Microsoft.Practices.RecipeFramework.Library">
    <Input Name="ReferringProject" ActionOutput="GetDesignTimeProject.Project" />
    <Input Name="ReferencedProject" ActionOutput="GetRuntimeProject.Project" />
  </Action>
</Actions>

Notice that the AddProjectReference action has inputs that come from the other two actions, and these in turn have inputs that come from the recipe. Now if you remember, the user didn't specifically provide the name of the projects to be generated, only the namespace and the name of the application block. We can use these arguments to derive the actual project names using the built-in ExpressionEvaluatorValueProvider.

Value Providers

A value provider is a class that provides values to arguments. Just like a Converter can convert and validate arguments, value providers can put values into arguments. Some of the built-in value providers can return the currently selected class in Visual Studio or a certain project by its name, but there's also a pretty powerful ExpressionEvaluatorValueProvider that can return values based on other arguments through expressions.

In this case, we simply want to build up the name of the runtime project by appending the two fields the user has entered:

<Argument Name="RuntimeProjectName">
  <ValueProvider Type="Evaluator" Expression="$(ApplicationBlockNamespace).$(ApplicationBlockName)">
    <MonitorArgument Name="ApplicationBlockNamespace" />
    <MonitorArgument Name="ApplicationBlockName" />
  </ValueProvider>
</Argument>

By also supplying the MonitorArgument values, the expression will automatically be re-evaluated if one of the monitored arguments has changed. We can now base the design-time project's name on this argument by appending a suffix to the project name as follows:

<Argument Name="DesignTimeProjectSuffix">
  <ValueProvider Type="Evaluator" Expression="Configuration.Design" />
</Argument>
<Argument Name="DesignTimeProjectName">
  <ValueProvider Type="Evaluator" Expression="$(RuntimeProjectName).$(DesignTimeProjectSuffix)">
    <MonitorArgument Name="RuntimeProjectName" />
    <MonitorArgument Name="DesignTimeProjectSuffix" />
  </ValueProvider>
</Argument>

Side note: it would also have been possible to use the expression "$(RuntimeProjectName).$(DesignTimeProjectSuffix).Configuration.Design" directly (without creating the DesignTimeProjectSuffix argument) but unfortunately there's a problem with the expression parser that makes the wizard very slow in this case.

Now that we have the names for both projects stored in arguments, we can also replace the project names in the vstemplate files that were still made up of the application block's name and namespace individually. E.g., we can replace ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$" by ProjectName="$RuntimeProjectName$".

Where Are We

At this point, we've seen how to generate a second project with a project reference by using actions that are chained together. We've also seen how to use value providers that put values into arguments and how to use these values in actions. Next time, we'll see how to build our own value providers and have them interact with other arguments. Furthermore, we'll add binary references to some of the Enterprise Library assemblies and add post-build events to the projects. Stay tuned!

Download the source code for the current state of the Guidance Package.

Wednesday, June 28, 2006 8:06:58 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

 

GAS03: Generating A C# Project#

[This is episode 3 of the Guidance Automation Series]

Last time, we left off with an empty guidance package that we will now be using to build the Enterprise Library guidance package. To scope it down a bit, we'll only be supporting C# in this example - but of course the same principles apply to other languages. As a first step, we'll set up the package so that it will generate two projects: the runtime project that contains the logic for the application block, and the design-time project that contains the user interface logic for the configuration tool.

Templates And Recipes

Before diving in, you need to know that a guidance package is basically a combination of Visual Studio Templates and Guidance Automation Recipes.

  • Visual Studio Templates are the standard way to define types of solutions, projects and classes that the developer can create in Visual Studio (e.g. they show up in the Create New Project/Item dialog boxes). Visual Studio Templates are defined as .vstemplate files in the Templates directory of the project, in a subdirectory that defines the type of template (e.g. Solution or Projects).
  • Recipes are automated activities that follow a series of instructions to execute some action that a developer would otherwise need to do manually (e.g. creating a number of projects in Visual Studio and adding references to them). Recipes are defined in the main xml file (i.e. not the TypeAlias.xml file but the xml file named after your guidance package), which means configuring this part will require some xml tinkering - although fortunately there is pretty good IntelliSense support in Visual Studio.

The two are linked by adding a recipe reference to the vstemplate file that will make sure the recipe is executed at the time the solution is "unfolded" (which is just a fancy term to say "created" in Visual Studio).

The Solution Template

Armed with this knowledge, we can now create a basic vstemplate file in the Templates\Solutions directory that contains the template for the Application Block solution:

<VSTemplate Version="2.0" Type="ProjectGroup" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
  <TemplateData>
    <Name>Application Block</Name>
    <Description>Guidance Package that creates a new Enterprise Library Application Block.</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>91</SortOrder>
    <Icon>ApplicationBlock.ico</Icon>
    <CreateNewFolder>false</CreateNewFolder>
    <DefaultName>ApplicationBlock</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
  </TemplateData>
  <TemplateContent>
    <ProjectCollection>
      <ProjectTemplateLink ProjectName="$ApplicationBlockNamespace$.$ApplicationBlockName$">Projects\Runtime\Runtime.vstemplate</ProjectTemplateLink>
    </ProjectCollection>
  </TemplateContent>
  <WizardExtension>
    <Assembly>Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate</FullClassName>
  </WizardExtension>
  <WizardData>
    <Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0" Recipe="CreateApplicationBlock">
    </Template>
  </WizardData>
</VSTemplate>

There are a few interesting things to note here:

  • The TemplateData section covers the metadata for the solution: its icon, name and description as they appear in the New Project dialog box, the project type, and some other properties.
  • The TemplateContent section defines the structure of the solution; in this case we define a project collection (we'll be adding another project later on) that contains one project template for the runtime part of the application block. When the solution is created, the template defined in the Runtime.vstemplate file will also be "unfolded".
  • The project's name is defined as "$ApplicationBlockNamespace$.$ApplicationBlockName$"; this is the first appearance of parameters (enclosed in $-signs). These values will be replaced at runtime by arguments from the Guidance Recipe, which we will define later on, to form the project name in Visual Studio.
  • The Template node in the WizardData section establishes the link between the vstemplate and the corresponding "CreateApplicationBlock" recipe that needs to be defined in the main xml file.

Guidance Automation Tip #2: All Visual Studio template files (vstemplates and their contents such as project files, code files and images) must have their "Build Action" set to "Content" and "Copy to Output Directory" to "Copy Always". The Guidance Automation Toolkit adds an item in the Templates directory's context menu that will do this for you.

The Project Template

At this point, we have a template for the solution that contains a link to a project template and to a recipe. The Runtime.vstemplate project template looks like this:

<VSTemplate Version="2.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
  <TemplateData>
    <Name>Application Block Runtime Project</Name>
    <Description>A project template for the runtime part of an Application Block.</Description>
    <Icon Package="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" ID="4547" />
    <ProjectType>CSharp</ProjectType>
    <SortOrder>20</SortOrder>
    <CreateNewFolder>true</CreateNewFolder>
    <DefaultName>Runtime</DefaultName>
    <ProvideDefaultName>true</ProvideDefaultName>
  </TemplateData>
  <TemplateContent>
    <Project File="Runtime.csproj" TargetFileName="$ApplicationBlockNamespace$.$ApplicationBlockName$.csproj" ReplaceParameters="true">
      <ProjectItem ReplaceParameters="true">Properties\AssemblyInfo.cs</ProjectItem>
    </Project>
  </TemplateContent>
  <WizardExtension>
    <Assembly>Microsoft.Practices.RecipeFramework.VisualStudio, Version=1.0.51206.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Assembly>
    <FullClassName>Microsoft.Practices.RecipeFramework.VisualStudio.Templates.UnfoldTemplate</FullClassName>
  </WizardExtension>
  <WizardData>
    <Template xmlns="http://schemas.microsoft.com/pag/gax-template" SchemaVersion="1.0">
    </Template>
  </WizardData>
</VSTemplate>

Some highlights from this file:

  • The icon from the package {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} is the ID of an icon in the C# Project dll, typically located at C:\Program Files\Microsoft Visual Studio 8\VC#\VCSPackages\csproj.dll. This dll can be opened in Visual Studio so you can pick another icon from this dll if you want, but you can also use a custom icon like we did in the solution's template above.
  • The TemplateContent section now defines a project that is created from the Runtime.csproj file. This is a regular C# project file that can again contain some parameters that will be replaced at runtime (if the ReplaceParameters attribute is set to true, anyway).
  • Within the project, we also include a Project Item (typically a file within the project), which will in this case be a boilerplate AssemblyInfo.cs file that the project will already contain.
  • There is no link to a recipe in the Template node here, since this project will only be unfolded by the solution template defined above - which already contains a reference to a recipe.

The Project File

This is the content of the Runtime.csproj file:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>8.0.50727</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>$guid1$</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>$safeprojectname$</RootNamespace>
    <AssemblyName>$safeprojectname$</AssemblyName>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <DocumentationFile>bin\Debug\$safeprojectname$.xml</DocumentationFile>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <DocumentationFile>bin\Release\$safeprojectname$.xml</DocumentationFile>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project>

Again, some interesting points to look at here:

  • There is no magic here, this is just an MSBuild project file for C# that you can tweak as much as you want.
  • Not only can we use the $ApplicationBlockName$ and other parameters we defined in the recipe, but we also have some standard parameters that can be used (see Template Parameters on MSDN for more information). The $guid1$ parameter will be replaced by a unique GUID (you can define up to 10 different GUIDs like this), the $safeprojectname$ will be replaced by the name provided by the user in the New Project dialog box, with all unsafe characters and spaces removed.
  • Both for the Debug and Release configurations, I've set TreatWarningsAsErrors to true (to enforce the best practice that warnings should be taken seriously) and I've enabled the output of an XML documentation file. The combination of these two settings also enforces that all public members must have XML comments, another best practice I always try to enforce.
  • The ItemGroup section defines that the AssemblyInfo.cs file we included in the project template is part of the project and needs to be compiled.

The Recipe

Now that we have all the templates in place, we still need to define the recipe that will enable the user to automate the creation of the solution and its projects. In its simplest form, you can think of a recipe as a wizard that collects user data - but as we will see later on in this series, it can do a lot more. We've currently defined two custom parameters in the template files: $ApplicationBlockName$ and $ApplicationBlockNamespace$. This means we need to collect this information from the user when the solution is being created. The following recipe definition does just that:

<?xml version="1.0" encoding="utf-8" ?>
<GuidancePackage xmlns="http://schemas.microsoft.com/pag/gax-core"
    Name="JelleDruyts.EnterpriseLibraryGuidance" 
    Caption="Enterprise Library Guidance"
    Description="Provides guidance around the creation of Enterprise Library Application Blocks"
    Guid="2cac5b9c-a04f-4a49-8a56-3ee5d63bd83f" 
    SchemaVersion="1.0">
  <Recipes>
    <Recipe Name="CreateApplicationBlock">
      <Caption>Create a new Enterprise Library Application Block</Caption>
      <Arguments>
        <Argument Name="ApplicationBlockName" Required="true">
          <Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.CodeIdentifierStringConverter, Microsoft.Practices.RecipeFramework.Library" />
        </Argument>
        <Argument Name="ApplicationBlockNamespace" Required="true">
          <Converter Type="Microsoft.Practices.RecipeFramework.Library.Converters.NamespaceStringConverter, Microsoft.Practices.RecipeFramework.Library" />
        </Argument>
      </Arguments>
      <GatheringServiceData>
        <Wizard xmlns="http://schemas.microsoft.com/pag/gax-wizards" SchemaVersion="1.0">
          <Pages>
            <Page>
              <Title>Application Block Information</Title>
              <LinkTitle>Application Block</LinkTitle>
              <Help>
                Enter the Application Block name and namespace.
              </Help>
              <Fields>
                <Field ValueName="ApplicationBlockName" Label="Application Block Name" InvalidValueMessage="Must be a valid .NET identifier (e.g. it shouldn't contain spaces or special characters)." />
                <Field ValueName="ApplicationBlockNamespace" Label="Application Block Namespace" InvalidValueMessage="Must be a valid .NET namespace identifier (e.g. it shouldn't contain spaces or special characters)." />
              </Fields>
            </Page>
          </Pages>
        </Wizard>
      </GatheringServiceData>
    </Recipe>
  </Recipes>
</GuidancePackage>

Some remarks on this file:

  • Recall that the "CreateApplicationBlock" recipe that is defined here is referenced by the solution's template, so this is the recipe that will be used to gather the data from the user.
  • The Arguments section defines all the recipe arguments that are defined, and can be replaced in the template using parameters. In this case, we have two arguments, ApplicationBlockName and ApplicationBlockNamespace, which are both required. They also have converters associated with them that can validate the arguments (e.g. to make sure they are valid .NET identifiers or namespaces). If the argument is invalid, it will immediately become visible to the user. Likewise, a required argument will have a light yellow background in the wizard, so it's immediately clear which arguments need to be filled in and which can be skipped.
  • The GatheringServiceData section contains a wizard with one page and two fields, one for each argument we want to collect.

Running The Guidance Package

Now that we have all the parts covered, we can finally register and test our package. It will show up as the "Application Block" project type in Visual Studio (with the proper icon and description):

When we create the solution, a wizard will pop up that allows us to enter the information required to generate the projects. Notice that the fields have a light yellow background and that we get an error icon and a tooltip if some argument is invalid:

When we correct the information and press Finish, our first skeleton project for the Enterprise Library Application Block has been created:

Note that the project was correctly named using the combination of the namespace (JelleDruyts.EnterpriseLibrary) and name (ServiceAgents) that was entered in the wizard.

Where Are We

At this point, we've seen how to define Visual Studio Templates that accept parameters and create solution files, project files and project items. We can already impose some guidance and best practices by tweaking the projects (e.g. to treat warnings as errors). We've also seen how to define a recipe that gathers information from the user to pass along to the templates.

You might say, "well, that's an awful lot of work to create a blank project", and you would be absolutely right. But of course, this is just the beginning. And now that we have the basics covered, the fun can really start! Stay tuned for the next episode, in which we'll take this guidance package a step further.

Download the source code for the current state of the Guidance Package.

Tuesday, June 27, 2006 9:35:19 PM (Romance Standard Time, UTC+01:00) #    Comments [8]  | 

 

GAS02: Creating A Guidance Package#

[This is episode 2 of the Guidance Automation Series]

In this first real part of the Guidance Automation Series, we'll start off slowly and easily... If you want to follow along, be sure to install both the Guidance Automation Extensions (GAX) and the Guidance Automation Toolkit (GAT).

Once you've installed GAT, you'll notice a new project type in Visual Studio called "Guidance Packages". This will contain all the packages that were installed on this machine. At this time, a first package is even already registered: the "Guidance Package" package.

See, creating a guidance package from scratch yourself would be quite a hassle, so this über-package will generate a sample package for you that you can use to immediately start building your own. People that can handle more recursive thinking than me often call this a Software Factory Factory, because it creates something that will in turn create something else.

Anyway, if you create a new project using this "Guidance Package" package, you'll get quite a lot of code that shows off a big number of the available features, as well as two additional projects that together form the setup project for your package. You can now build this entire solution, right-click the guidance package project and select "Register Guidance Package" from the context menu.

When the registration process has finished, you're ready: your first guidance package has been built and deployed on your local machine. You can test it by opening another instance of Visual Studio, where you should now notice your own package in the "Guidance Packages" project type.

Using the MSI that was built by the setup project, you can now easily deploy this Guidance Package to any developer pc that has the Guidance Automation Extensions installed.

Guidance Automation Tip #1: Always keep your guidance package open in one dedicated instance of Visual Studio and test the package in new instances every time.

Notice that registering the package took quite some time, and unfortunately this is life in the Guidance Automation world: everytime you want to verify or test your package, you'll have to register it and start another Visual Studio to test it in. So my development cycle typically looks as follows: make modifications to the package, build the package, register the package, open another Visual Studio, test the package, make modifications to the package, ... In other words: think twice before you think your modification is ok :-)

The good news is that with this new release, a new "Quick Register" option has been added to the context menu, as you can see in the screenshot above. You can use this to drastically speed up the development process if you don't have any new recipes or templates that need to be registered or if you didn't change any HostData sections that define the integration with Visual Studio. If you did any of these things, you will need to re-register the entire package, though.

Now that we have the basics covered, let's remove all the pre-generated parts from the package that aren't needed so that we're left with a clean and empty Guidance Package. This basically means deleting all the files in the package except the two xml files in the project root: these form the package definition and are used by GAX to register it. This empty solution will be the basis on which we'll build the Enterprise Library Guidance Package in the next installment of this series.

Download the source code for the current state of the Guidance Package.

Monday, June 26, 2006 9:09:55 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

GAS01: Introduction To The Guidance Automation June 2006 CTP#

[This is episode 1 of the Guidance Automation Series]

Great news: the June 2006 CTP of Guidance Automation (GAX and GAT) has just been released! Although it's still labeled a CTP (because it's unsupported), I personally think of this as the 1.1 release of a great tool I've been using extensively the last couple of weeks.

If you've never heard of the Guidance Automation Extensions (GAX) and the Guidance Automation Toolkit (GAT), this is the elevator pitch straight from the Guidance Automation community site:

With the Guidance Automation Toolkit, you can make reusable code and pattern assets directly available in Visual Studio 2005. It is designed to simplify integrating reusable code into applications. This lets architects automate processes, and guide their developers through complex procedures that would otherwise need to be thoroughly documented.

So in other words: the Guidance Automation Toolkit provides a means for a solution architect or lead developer to deploy consumable guidance to developers. If you've ever published a 40-page document full of guidelines, do's and don'ts on how to structure solutions and projects in your company and what the namespaces and class names should be, if the developers need to be able to start new projects quickly, if you have a need for code generation, or all of the above - this toolkit is for you. Microsoft is itself using this technology to publish their Software Factories, like the Web Service Software Factory, so that says something about the quality and future of this fine piece of software.

Just to get some basic vocabulary right: the Guidance Automation Extensions (GAX) is the runtime part that needs to be installed on the machines of the developers that want to use the Guidance Packages. The Guidance Automation Toolkit (GAT) is the authoring part that helps you create the Guidance Packages and the setup projects to install them.

New in this release of GAX are the Guidance Navigator window, which provides a separate window listing all the recipes in the currently loaded guidance package (thereby greatly enhancing their discoverability) and the fact that the state of the guidance package (which actions can be performed where in the solution, for example) isn't persisted in the per-developer .suo file anymore but in a separate .gpstate file so team development becomes possible. A very handy new feature of GAT is the Quick Register action that can drastically speed up the development process.

In a series of posts I'll commonly call the Guidance Automation Series (or GAS for short, if you will), I'll be showing you how to build a Guidance Package with this new release for a sample scenario that's related to that other big project from the Microsoft patterns & practices group: Enterprise Library 2.0. I'll be implementing a Guidance Package that allows you to quickly start building your own application blocks that plug in nicely with the rest of Enterprise Library. The point is not to provide a full-blown Guidance Package with all the bells and whistles required to start writing these application blocks, the idea is to show you how to create the Guidance Package - so you can go the extra mile of adding all the bells and whistles yourself :-) If you need more details on how to build the application blocks themselves, I can highly recommend the "Speed Development With Custom Application Blocks For Enterprise Library" article by Mark Seemann that was just published on MSDN.

So, if you're into this kind of stuff, keep reading this blog :-)

Monday, June 26, 2006 8:50:14 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

IIS virtual directory names and mapped file extensions#

Just a quick note to mention a little quirk in Internet Information Services (IIS) I discovered recently: when you have a virtual directory name that ends in a registered file extension, you'll get an HTTP 404 error when browsing to that directory.

We were using http://localhost/MyCompany.MyProject.Svc/MyService.asmx as our web service endpoint, but that kept giving us HTTP 404's. At first, we thought it had to do with the dots in the name (causing us to change our guidelines and drop part of our naming consistency) but I later found out it has to do with the .svc file extension being mapped to the ASP.NET isapi dll. So even though it's clearly a directory name and not a file, we still got the 404.

So we changed our guidelines (once more) to MyCompany.MyProject.Svc.WebServices and all was fine again...

Blog | Programming | .NET | ASP.NET | Quirks
Sunday, June 25, 2006 10:45:29 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Unable to load oramts.dll? Try the release version#

We were quite amazed recently when we discovered the root cause of the following error message:

System.DllNotFoundException: Unable to load DLL 'oramts.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E).

We were getting this error on our application servers (not on our developer workstations) when using distributed transactions from .NET 2.0 with Oracle 9i (coordinated by MSDTC).

It turns out that the version of oramts.dll (which provides DTC transaction support for Oracle) as installed by the Oracle 9i Release 2 CD is a debug build, rather than a release build. (Let that sink in for a moment: Oracle is shipping debug builds of their software...). As you can see in the following screenshot of the dll in DependencyWalker, this debug build has a dependency on MSVCRTD.DLL, which is a debug build of the Microsoft C Runtime Library:

This debug version is installed on our workstations (because we have Visual Studio installed), but of course not on our application servers, causing the loader exception shown above. Copying the MSVCRTD.DLL file to the System32 directory solved the problem - temporarily of course (you don't want to be installing debug builds all over the application servers because of an Oracle dll).

Anyway, if you suffer from this issue as well, you can get a patch from Oracle containing the proper dll. You can easily tell the difference by looking at the file size: 137 KB for the release build versus 192 KB for the debug build.

Sunday, June 25, 2006 10:21:07 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

The Visual Studio 2005 Toolbox, UserControls and Solution Folders#

There's a pretty inconvenient bug in the Windows Forms designer in Visual Studio 2005, which apparently is a known issue but not scheduled to be fixed anytime soon. Here's the scoop and a (slightly sucky) workaround...

If you have a User Control in your project, it will automatically get added to the Toolbox window so you can drag it onto your designer surface. However, it doesn't show up in the Toolbox if the project is inside a Solution Folder (which is a virtual folder in your solution). It would be really nice if it were also possible to drag the user control from the Solution Explorer onto the designer surface, but unfortunately that's not supported.

So a possible workaround is to take the simplest type of control available (I tend to go for a plain old Panel) and add that to the form in stead of the user control you really want to add. Then switch to the designer-generated code (e.g. MyForm.Designer.cs) and change both the declaration of the panel (e.g. "private System.Windows.Forms.Panel panel1;") and the instantiation (e.g. "this.panel1 = new System.Windows.Forms.Panel();" inside the "Windows Form Designer generated code" region) to your user control type (e.g. MyNamespace.MyUserControl).

It sucks, but it works.

Blog | Programming | .NET | Quirks | VS.NET | WinForms
Friday, June 23, 2006 5:20:59 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

 

A fix for "Could not access network location" when installing a Visual Studio Addin#

Just a quick hint: if you're installing addins for Visual Studio and the installer quits with a "Could not access network location "\\SomePath\Visual Studio\Addins", this probably means your "My Documents" folder is stored on a network drive, causing the installer to fail (for some reason I haven't yet discovered).

I found a pretty quick workaround, which is temporarily changing the location of the "My Documents" folder to a path on a local drive. You can do this by changing the registry key (all usual disclaimers apply, I didn't touch your computer so I didn't break it) "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurentVersion\Explorer\User Shell Folders\Personal". Note that the installer won't actually use this path to store anything, so it's safe to use any path you deem fit. If the addin installed properly after this hack, don't forget to change the registry key back to its original value and you should be fine.

I haven't tried it for other similar issues, but it might even work with any other type of software that breaks with the same error message.

Blog | Programming | .NET | VS.NET
Wednesday, June 21, 2006 5:59:39 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Creating a setup package with multiple optional custom actions#

I've recently needed to create an installer with custom actions that only need to be run when the user selects certain options in the installation wizard. It's pretty easy to do once you know the pitfalls, but it took me some time to figure out so I figured I might as well post it for future reference...

One option is to create a separate assembly for each custom action that can be selected, and then setting its Condition property so that it is only executed if the user chose the corresponding option.

However, this could result in quite a lot of extra assemblies (currently three in my case, but it might become more), which I try to avoid to make it easier to deploy and use these custom actions. So I wanted one assembly containing all the installer classes that could need to be run, and then decide inside that assembly which ones to execute based on the user's choice. This is what worked for me:

  • Pass in the user's choices (e.g. the property values of the checkboxes) through the custom action's CustomActionData property, e.g. "/OPTION1=[OPTION1] /OPTION2=[OPTION2]" in case there are two checkboxes with OPTION1 and OPTION2 properties. Also pass in all other data that needs to be available for the individual installers (note that these may conflict, in which case you'll have to create different assemblies anyway).
  • Mark each individual installer with the [RunInstaller(false)] attribute. Otherwise, the installer will be executed no matter what. Side note: my first attempt at solving this problem was to check inside each installer's Install method if it needed to be executed based on the context parameters, but this caused all sorts of problems with the savedState dictionary.
  • Create one main installer class with [RunInstaller(true)]. This is the one we will add the decision-making logic in, and which will keep the master savedState dictionary.
  • In this main installer, override the Install method and, before calling base.Install, add the individual installers to its Installers property depending on the parameters that were passed in. Also, remember which installers were added (which options were selected) by putting them in the stateSaver object. Example:
    if (this.Context.Parameters["OPTION1"] == "1")
    {
      this.Installers.Add(new Option1Installer());
      stateSaver["OPTION1"] = "1";
    }
    It's important to only call base.Install after this, because the base implementation will then install all the child installers that were just added.
  • Override the Commit and Rollback methods, and before calling the base class implementation, add the installers again depending on the values you put in the savedState object. This is necessary because these methods will be called on new instances of your installer class, so you need to re-add them to the Installers property. Example:
    if (savedState["OPTION1"] == "1")
    {
      this.Installers.Add(new Option1Installer());
    }
  • Do the same thing for the Uninstall method, this way you are sure that you're only uninstalling the parts that were actually installed. This is important, because when uninstalling, the parameters that are passed in do not remember the user's initial choices (i.e. OPTION1 might be set to "1" when uninstalling, even if the user didn't select that choice at installation time).

This is what worked for me, if there are simpler ways of achieving the same thing, don't hesitate to make suggestions :-)

Blog | Programming | .NET | VS.NET
Wednesday, June 21, 2006 5:55:35 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Feline Beta#

I think I've been spending a wee bit too much time with beta software lately...

Last night, I woke up from a dream that involved my... [dramatic pause] cat. So I was getting a new beta of my cat for some reason, and I noticed that all of a sudden she had a white piece of fur (normally, she's all black (black!)). And all I could think about whas the fact that my cat seemed to have a regression bug.

Go figure.

I think I'll have to go blow the dust off of my VB6 box just to get my life back on track. See you after my rehab :-)

(Now that I think about it, I never suspected I'd ever write a post about my cat. Oh no, I'm becoming a Dear Diary blogger!)

Thursday, June 15, 2006 6:45:34 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

More on the Architect role and title#

As a response to my thinking about the role of the Technical Solutions Architect, Hans Verbeeck makes a good point on his blog on the snobism of Architecture:

It's like everyone needs to be one. Especially the top developers seem to be targeted. If you are a lead developer then you stand a big chance of being labeled an Aspiring Architect by some folks within Microsoft. IMHO, you don't need to be an architect to be architecting. I would even say that probably most of the good architectural work comes from Lead Developers who have a deep deep udnerstanding of what software is all about.

The architect title is perhaps so over-used that it's lost its meaning, and I think that's the source of a lot of confusion and opportunism. I've always had a bit of a love/hate relationship with the architect title, but I guess people (including me) need some way of categorizing their qualifications in a name that's well understood.

Clearly, there's something very different about what guys like Don Box and Chris Anderson do with internal frameworks such as WCF and WPF, and what I do as an external consultant on enterprise (business) frameworks. But within the boundaries of terminology that's out there, I positioned myself somewhere in the triangle.

I actually like Hans' image a lot about being positioned outside the triangle - which also puts me more outside the business domain:

So this certainly makes sense to me.

About the snobism and whether or not you should wear the architect title: I forgot about it until now but I think the solution already exists: the Microsoft Certified Architect Program should be - or become, as it's still in its early stages - the governing instance that hands out the "architect" credentials (in the Microsoft-space anyway). While a regular Microsoft certification - in my opinion - doesn't say that much about the real-life experience or qualifications of someone, this architecture certification is backed by a Review Board and a pretty heavy qualification process. This should make the certification much more credible...

But in the end, I think you'll always have to prove your worth on the dance-floor, if you know what I mean. So I'll just keep doing my moves and let's keep this party rolling :-)

Wednesday, May 31, 2006 12:07:28 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

The Technical Solutions Architect Role#

I've been doing quite some thinking about my role in the IT business lately, and a few recent posts have accelerated this even more... First, we have Joris Poelmans sharing and aggregating some thoughts about architects, architecture and the different types of architects (architect personas, if you will). And then we have Hans Verbeeck pointing us all to the Skyscrapr site, which is a site that serves as "your window on the architectural perspective".

All of these categorize architects in different roles in the business. Generally speaking, there are three personas most people seem to agree on (with my highly summarized descriptions that don't do the original posts any justice):

  • Enterprise Architect: responsible for the long-term strategic vision, thinking about the big picture together with the business and technical people; sets the direction for methodologies, frameworks and tools.
  • Solutions Architect: responsible for the design of applications and services within (part of) an organisation; works closely with the developers to make sure the goals of the business are achieved.
  • Infrastructure Architect: responsible for the data center, deployments and maintenance; makes sure the infrastructure on which the business applications run is reliable, manageable, scalable, performant and secure.

Since you're never only one of these profiles, Simon Guest adds to this idea by positioning yourself somewhere in the triangle formed by these personas, to indicate your relative strength in one particular area. If I were to call myself an architect (and sometimes - not only in my sleep - I do), I think I'd put myself somewhere here:

So I believe I'm mainly the Solutions Architect: driving the technical solutions to meet the business' needs. The problem for people that are in the consultancy business - like me - is that all these roles are defined within the boundaries of the enterprise. All of them assume that you are effectively part of the business. Since my role is typically shorter-term for each individual client (providing guidance and experience in the technology field rather than in the actual business domain), I often don't need (or want) to know too much about the actual business. The benefits of me having intimate knowledge about it are negligible most of the time, both for myself as for the customer.

So in that regard, I'd like to propose the term Technical Solutions Architect to describe what people like me do: this role involves setting up technical architecture, guidance, frameworks and documentation, performing code reviews, coaching, deciding on programming languages and technology stacks - all within a certain horizontal technical scope that may or may not be aligned with any vertical business structure.

What do you think? Would you agree? What role do you think you are?

Tuesday, May 30, 2006 9:08:43 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Closing locked files that break the Team Build#

I was having some trouble the other day with our Team Build server when building a project that is used by an open Visual Studio instance.

Our setup is that we build our enterprise framework on the build server and then copy it to a public share so it can be used by the client projects. Now when somebody is actively developing a project in Visual Studio that uses this enterprise framework, we receive something like the following error in the build log when building the framework:

Copying drop to final destination
Copying folder "\\BuildServer\Drop\Framework_20060530.2\Release" to "D:\PublicShare\References\EnterpriseFramework\Latest".
D:\Build\Team\EnterpriseFramework\BuildType\TFSBuild.proj(168,5): error : A task error has occured.
D:\Build\Team\EnterpriseFramework\BuildType\TFSBuild.proj(168,5): error : Message = Access to the path 'D:\PublicShare\References\EnterpriseFramework\Latest\EnterpriseFramework.Common.xml' is denied.

So the open Visual Studio instance on the developer's pc seems to keep a lock on the XML file (for IntelliSense) on the build server, causing the build to fail. Now I don't think it's very nice of Visual Studio that it locks the file, but apparently that's the way it is.

So here is the unclean (you won't catch me calling anything I do 'dirty' of course) workaround I used to get rid of this sticky problem. In the build script, right before copying the files to the public share, I execute the following task:

<Exec Command="net session /delete /y" />

This simply forces a delete of all connected client sessions, closing all the handles of the files that are opened remotely. Problem solved - as long as we don't have the race condition where somebody would lock another XML file just between that command and the actual copy, but what are the odds... The only minor side effect is that the opened Visual Studio instance loses IntelliSense for some of the files it had locked, but we can live with that. At least it's better than breaking the build, right?

Tuesday, May 30, 2006 7:37:36 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Visual Studio 2005 Web Application Projects Released!#

I don't know about you, but I've never been very fond of the new Web Project System in ASP.NET 2.0. I'm sure it has its advantages for some types of users but I've definitely had more than my share of problems with it. Specifically the way that it sometimes (and randomly) starts checking in binaries in Source Control has shortened my life with at least 4 months. Furthermore, having each page compile into its own assembly (by default anyway) and therefore not having a nice place to put assembly-level attributes or define strong naming that work on the entire website has surprised me (in a negative way, that is). What I don't get is that Microsoft had abandoned a familiar and working concept (the Web Application model used in the previous versions of Visual Studio) and replaced it entirely without supporting the old way of working. To me this has always been a major breaking change, and a cause of much frustration.

Well, the pain has now officially ended: the Visual Studio 2005 Web Application Projects has been released, meaning that the old model is back with a vengeance. With a free download from Microsoft, you can get your familiar working environment back in Visual Studio 2005, but with lots and lots of improvements over the previous versions of course (not even mentioning the fact that it's now ASP.NET 2.0 with all its goodness around it).

Some highlights:

  • A project file (which I don't consider a feature, more a bugfix over the previous model)! This fixes the problems with the references getting checked in, and lots of other things.
  • Because of the project file, you also have pre- and post-build events again (the new Web Project System doesn't support them).
  • You can create working Web Setup projects again, containing the content and primary output of the project. With the new Web Project System, you have to download and install a separate update (the Visual Studio 2005 Web Deployment Projects).
  • It builds one assembly by default. This fixes the problem with the assembly-level attributes and strong naming.
  • The Global.asax file isn't ugly inline script anymore but a proper class in a code-behind file. The "Application_Start" and similar methods are still "magic" though, discovered through reflection I assume, in stead of properly overriding them from the base class or something.
  • No more Frontpage Extensions required. Be gone, _vti_*!
  • Built-in Development Web Server. Be gone, IIS! Well, not for enterprise scenarios of course, but it's still a very nice feature :-)
  • MSBuild support.
  • Better web publishing options.
  • ...

I've immediately installed and tried it and it works great! One specific detail that I think is a very nice feature (just to show that it's not a quick fix of the old model to run on Visual Studio 2005 but a real full-blown upgrade) is the "Create Virtual Directory" button in the project settings, which allows you to setup a virtual directory in IIS without having to go to the IIS console:

Great!

Anyway, this add-on has made me very happy with ASP.NET 2.0 again. It's the little things that make the difference, but the big things in this case made my day :-)

Blog | Programming | .NET | ASP.NET | VS.NET
Thursday, May 11, 2006 8:30:11 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

InfoPathHelper v1.1: add offline support to InfoPath!#

It's been a while since I released the original version of InfoPathHelper, a small reusable .NET library you can use to add offline support to InfoPath forms. I still think it's a nice little utility, and it makes me proud that even the InfoPath folks think enough of it to link to it on their Office Developer Center!

Unfortunately, I've always found it hard to really promote it since there was this one nasty bug that didn't make me very proud: it would sometimes blow up when caching data from a Web Service. But today, I'm happy to announce that I've fixed that bug and added a few other tweaks as well so I believe it's now a fully functional product!

I'll repeat the elevator pitch from my original post here just to round up what it does:

InfoPathHelper is a small reusable .NET library you can use to add offline support to InfoPath forms. That's right, you're finally able to make InfoPath a real smart client that is offline capable of submitting forms and querying data sources, without any extra security requirements whatsoever.

For the end user, the form will appear to continue working when the machine is offline or when the target you're submitting your form to is unavailable (a Web Service, a Sharepoint site, ...). The next time the machine goes online, any pending requests will be submitted. The same goes for querying data sources (files on a network share, Web Services, ...): if they're not available, the data will be taken from the local cache until the next time it can be refreshed.

For a developer using the InfoPath 2003 Toolkit for Visual Studio .NET, it boils down to inheriting from the CachingInfoPathForm base class, changing your submittal options to use form code, deleting the messy generated boilerplate code (you get cleaner properties and events for free in stead) and calling a few methods depending on your needs. The following example shows how to enable offline support for a form that has a submit data adapter named "WebServiceSubmit" and a data object named "Countries".

public class MyCachingForm : CachingInfoPathForm
{
    [InfoPathEventHandler(EventType=InfoPathEventType.OnSubmitRequest)]
    public void OnSubmitRequest(DocReturnEvent e)
    {
        // Attempt to submit the request and return a value to indicate if it worked.
        e.ReturnStatus = ProcessSubmitRequest( "WebServiceSubmit" );
    }

    [InfoPathEventHandler(EventType=InfoPathEventType.OnLoad)]
    public void OnLoad(DocReturnEvent e)
    {
        // Attempt to submit any cached requests and query a data object.
        SubmitCachedRequests( "WebServiceSubmit" );
        QueryDataObject( "Countries" );
    }
}

If you're wondering where the cached data is stored without needing any extra security requirements: welcome to the magical world of Isolated Storage :-)

Changes in v1.1:

  • Strong-named the assembly. This is needed for the offline caching to work properly, otherwise a published form would apparently get a new directory in the Isolated Storage cache each time the form is run.
  • Added the AllowPartiallyTrustedCallers attribute to the assembly. This is needed to allow the now-strong-named code to run within InfoPath.
  • Fixed the bug where a "This DOM cannot be loaded twice" COM exception would be thrown when caching data from a Web Service.

Thanks to David Downs at Microsoft for reporting and solving the issue with the isolated storage, and thanks to both David Downs and John Boesen for coming up with part of the solution for the bug with the Web Service!

Enough talk, here's the goodies:

The JelleDruyts.InfoPathHelper 1.1 library and documentation (125,12 KB)
The JelleDruyts.InfoPathHelper 1.1 library, source code and documentation (244,17 KB)

Tuesday, May 09, 2006 7:16:12 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

 

MSDN Connection & Book of the month discount#

Our local MSDN Belux guys have created an "MSDN Connection" site where you can get an RSS feed of MSDN news, access to developer centres, downloads, events, and much more. Not only does that sound pretty interesting in itself, but members also get a free subscription to the Dutch ".NET Magazine" and a 40% discount on the book of the month.

In May, this book is no less than Jeffrey Richter's most excellent "CLR via C#" (the second edition of his must-have "Applied Microsoft .NET Framework Programming"). I was just about to order a copy, so count me in for that discount!

And all of this for free... Great work guys!

Thursday, May 04, 2006 9:40:41 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Faking a current HttpContext#

I was recently looking for a way to unit test a piece of security code that would retrieve the User principal from the current HttpContext in an ASP.NET scenario. Now it was just a plain unit test, not a web test, so I didn't actually have an HttpContext. But how hard could it be to cook one up, right?

So I first tried to set the HttpContext.Current property, but IntelliSense popped up to say: "Gets the HttpContext object for the current HTTP request". So I figure it's a read-only property (because the phrase starts with "Gets...", where read-write properties typically start with "Gets or sets...") and I can't do it directly.

Then I found a post by Steve Maine that seemed to allow setting the current HttpContext by injecting it into the messaging CallContext of .NET Remoting. While that looks like a bit of black magic, I'm never too shy to throw in some snake blood and salamander eyes if it makes my code compile so I tried that, but it didn't seem to work right away. It did, however, provide me with a good way of creating a dummy HttpContext in the first place, which I also needed to do:

SimpleWorkerRequest request = new SimpleWorkerRequest("/dummy", @"c:\inetpub\wwwroot\dummy", "dummy.html", null, new StringWriter());
HttpContext context = new HttpContext(request);

So we just create a dummy worker request that the HttpContext requires and we pass it into the constructor.

Because the black magic wasn't working for me, I looked at the HttpContext class in my favorite .NET tool of all time, and quickly realized that the property was settable anyway [1]. So I guess that salamander had a lucky day:

HttpContext.Current = context;

And we're done. A valid current HttpContext for use in unit tests.

[1] Side comment: property documentation that doesn't follow the "Gets..." versus "Gets or sets..." pattern is pretty frustrating. It could have saved me quite some time if the documentation would just say "Gets or sets the HttpContext object for the current HTTP request"...

Blog | Programming | .NET | ASP.NET | Samples
Wednesday, April 05, 2006 10:07:31 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Free chapter from Framework Design Guidelines book#

It seems that you can download chapter 4 from the splendid book "Framework Design Guidelines" by Brad Abrams and Krzysztof Cwalina, for free at theserverside.net! Go get it!

This is the book that my "Best Practices In Framework Design" talk on the Developer & IT-Pro Days was based on, and the chapter covers design guidelines for types (classes, structs and interfaces) so that should give you a very good feel of what the book is all about. I heartily recommend the entire works, of course, but if all my positive feedback hasn't convinced you entirely yet then you can now go take a look for yourself :-)

Wednesday, April 05, 2006 9:45:39 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Enterprise Library Caching without configuration#

I've been using Enterprise Library 2.0 quite a lot lately, and although the configuration part is truly excellent and easy to use, sometimes you just want to use some block without going through configuration. In my case, I wanted to use a cache somewhere in a piece of framework code without requiring users of that code to add anything "magic" to their application configuration to make it work. So with a little help of Brian Button, I cooked up the following code to create a standard CacheManager with an in-memory backing store:

DictionaryConfigurationSource internalConfigurationSource = new DictionaryConfigurationSource();
CacheManagerSettings settings = new CacheManagerSettings();
internalConfigurationSource.Add(CacheManagerSettings.SectionName, settings);
CacheStorageData storageConfig = new CacheStorageData("Null Storage", typeof(NullBackingStore));
settings.BackingStores.Add(storageConfig);
CacheManagerData cacheManagerConfig = new CacheManagerData("CustomCache", 60, 1000, 10, storageConfig.Name);
settings.CacheManagers.Add(cacheManagerConfig);
settings.DefaultCacheManager = cacheManagerConfig.Name;
CacheManagerFactory cacheFactory = new CacheManagerFactory(internalConfigurationSource);
CacheManager customCache = cacheFactory.CreateDefault();

So this piece of code in fact creates the configuration objects directly and passes them on to the CacheManagerFactory to use so it doesn't go about reading any configuration files. Pretty simple once you get the hang of it, and pretty powerful for the cases you want to use Enterprise Library "behind the scenes".

Check out Brian's blog for an example on Exception Handling without using an external configuration file. By the way: thanks for all your work on EntLib and all the best in your career move, Brian!

Blog | Programming | .NET | EntLib | Samples
Tuesday, March 28, 2006 9:06:35 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Developer & IT Pro Days 2006#

It's been a hectic and busy couple of weeks leading up to the Belgian Developer & IT Pro Days in Ghent last week, but it was all worth it! Steven and I had a lot of fun presenting our sessions, and it was great to meet a lot of interesting new people and to talk to some people who I hadn't seen in a while.

I know that we haven't been able to upload the final versions of the slides to the official site on time, but a lot of you have been asking for them so here they are:

Best Practices In Framework Design (1.85 MB)

Best Practices For Application Development (2.12 MB)

During this last presentation, we showed demos of Enterprise Library 2.0 and a very cool (IMHO) implementation of the Command Pattern using Enterprise Library to add some aspects such as logging and security. You can find the source for the first demo (Enterprise Library only) here:

Demo 01 - Enterprise Library.zip (1.46 MB)

Steven has posted the second demo along with the source code for the Command Pattern on his blog, so you can get that from there!

In conclusion, it was a great event that really made all the efforts of the past weeks worthwile. So a lot of thanks to the organizing team (great job Tom, David, Ritchie!) and for all that attended!

Hope to see you again next year!

Tuesday, March 14, 2006 9:15:32 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

My article is headlining on MSDN#

Now it may not get me the same amount of groupies as headlining on Rock Werchter, but apparently my article on The Command Pattern in Windows Presentation Foundation is currently the top headline on the home page of the Windows Vista Developer Center at MSDN! Woohoo!

Thanks David for pointing it out to me!

Blog | Programming | .NET | WPF
Monday, March 06, 2006 12:03:12 PM (Romance Standard Time, UTC+01:00) #    Comments [10]  | 

 

Article: The Command Pattern In Windows Presentation Foundation#

I'm happy to announce that my second article just got published on the Microsoft Belux website:

The Command Pattern In Windows Presentation Foundation

Windows Presentation Foundation (formerly codenamed "Avalon"), or WPF for short, is a brand new Microsoft framework for developing very rich and powerful Windows applications. There is much to be said about Windows Presentation Foundation and its numerous new and enhanced capabilities, but this article will in stead focus on an old trusted friend, who has finally been given a dedicated room in the big house of Windows User Interface development: the "Command" pattern. This design pattern basically abstracts all actions the user can perform in an application into the notion of "commands"; it has been implemented in many different ways on top of various UI frameworks, but now, it has finally made it into the gut of the system itself.

Don't hesitate to let me know if you liked or disliked the article!

Blog | Programming | .NET | WPF | Articles
Tuesday, February 14, 2006 8:41:42 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Speaking at Microsoft Developer & IT Pro Days 2006 in Belgium!#

A lot of people have already noticed that I'm on the speaker agenda for the Microsoft Developer & IT Pro Days 2006 in Belgium, but here's the official word: I'm proud to be your host for 2 sessions in the Development Best Practices track! I'm giving the second one together with Steven Wilssens, so that should be a lot of fun.

Best Practices in Framework Design: The Art of Building a Reusable Class Library

March 8, 2006 - 09:00 - 10:15

This session presents best practices for designing frameworks, which are reusable object-oriented libraries. The guidelines are applicable to frameworks ranging in size and in their scale of reuse from large system frameworks to small components shared among several applications. Topics covered include the design, background and motivation for: naming conventions, namespaces and assemblies, types (structs, reference types, generic types), members, designing for extensibility, usage guidelines, and general library design principles. Attend this session to learn more on the best practices for building a reusable class library.

Best Practices for Application Development

March 8, 2006 - 10:45 - 12:00

This session presents best practices for developing robust enterprise applications on the .NET platform. The guidelines are an accumulation of years of experience in building frameworks and enterprise applications. We will show you best practices around exception management, logging, configuration, authorization, data access, unit testing, code analysis, documentation, daily builds, caching and testing. When attending this session you will also learn about recurring design patterns, general development guidelines and conventions.

Furthermore, Steven will also be presenting a very interesting session on Source Control:

Best Practices for Advanced Source Control: Beyond CheckOut and CheckIn

March 8, 2006 - 14:30 - 15:45

This session presents best practices for advanced source control management. We will examine different branching strategies and provide you with the necessary guidelines that will help you adopt the right Software Configuration Management branching mode. These best practices will help you increase overall product quality and process efficiency, reduce the incidence of software failures, and improve organizational performance. We will have a look at Team Foundation Source Control and answer following questions: What's new with merging and branching in VSTS and why is it better? What is shelving? What about continuous integration and how can you implement a gauntlet system with VSTS?

See you there!

Friday, February 10, 2006 12:49:23 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Xml Documenter Macro#

Although I've argued before that Typed DataSets aren't evil, there's one thing that bothers me a lot when dealing with them in a library project: the code that gets generated doesn't contain any XML comments. In itself, that isn't so bad (after all, which useful comments could be generated from an XSD schema?), but if the project is configured to emit an XML documentation file, you get a gazillion warnings for each member that lacks a comment. I like to keep my build warning-free to avoid "known harmless warnings" from hiding "new and important warnings" (if I can first get my stuff to build without errors, at least). You could go about and put comments on the generated file manually, but any change on the dataset's schema file will trigger the code to be regenerated so you've lost all your hard work. Hence I created the following quick and dirty solution: a Visual Studio macro that runs over all members and puts a placeholder comment (containing just the member's name) on top. Anytime the code gets regenerated, just run the macro and you're set. I'm sure this should be able to run as a post-build step or something too, but if you're lazy like me (and you're a programmer too, so you're lazy by definition), you'll already be happy you got this done.

Oh, and if you're really lazy, you can use this macro to document all your members automatically - although I'd add some random word-generator to make it look just a little more convincing (to fool the QA-guys at least).

So here's the source (with special thanks to the MSDN article on XML Comment Macros):

Imports EnvDTE
Imports System.Diagnostics
Imports System
Imports System.Xml
Imports System.IO
Imports System.Collections
Imports System.Text
Imports System.Text.RegularExpressions

Public Module XmlDocumenter

    Public Sub InsertGenericSummaries()

        SetCommentsRecursively(DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements)

    End Sub

    Private Sub SetCommentsRecursively(ByVal elements As CodeElements)

        For Each element As CodeElement In elements
            ' Get a comment block.
            Dim doc As XmlDocument = GetCommentXml(element)
            Dim summaryTag As XmlElement = doc.SelectSingleNode("/doc/summary")
            If summaryTag Is Nothing Then
                ' Create <summary> tag
                summaryTag = doc.CreateElement("summary")
                summaryTag.InnerXml = element.Name
                doc.FirstChild.PrependChild(summaryTag)
            End If
            SetCommentXml(element, doc)

            ' Recurse over members.
            Try
                SetCommentsRecursively(element.Members)
            Catch ex As Exception
                ' Ignore exceptions (quick and dirty hack to skip elements that have no Members property)
            End Try
        Next

    End Sub

    ' Code borrowed from http://msdn.microsoft.com/msdnmag/issues/05/07/XMLComments/default.aspx
    Private Sub SetCommentXml(ByVal element As CodeElement, ByVal doc As XmlDocument)
        Dim sb As StringBuilder = New StringBuilder
        Dim sw As StringWriter = New StringWriter(sb)
        Dim xw As XmlTextWriter = New XmlTextWriter(sw)

        Try
            xw.Indentation = 1
            xw.IndentChar = Char.Parse(vbTab)
            xw.Formatting = Formatting.Indented

            Dim rootNode As XmlNode
            If element.Language = CodeModelLanguageConstants.vsCMLanguageVB Then
                rootNode = doc.FirstChild
            Else ' CSharp
                rootNode = doc
            End If

            rootNode.WriteContentTo(xw)
            Try
                element.DocComment = sb.ToString()
            Catch ex As Exception
                ' Ignore
            End Try
        Finally
            xw.Close()
            sw.Close()
        End Try
    End Sub

    ' Code borrowed from http://msdn.microsoft.com/msdnmag/issues/05/07/XMLComments/default.aspx
    Private Function GetCommentXml(ByVal element As CodeElement) As XmlDocument

        Dim doc As XmlDocument = New XmlDocument

        Dim xmlStr As String = element.DocComment
        If Not xmlStr Is Nothing AndAlso xmlStr.Trim() <> String.Empty Then
            If xmlStr.StartsWith("<doc>") = False Then
                ' Ensure single root node
                xmlStr = "<doc>" & xmlStr + "</doc>"
            End If
            xmlStr = Regex.Replace(xmlStr, "^(\s*<doc>\s*){2,}", "<doc>")
            xmlStr = Regex.Replace(xmlStr, "(\s*<\/doc>\s*){2,}$", "</doc>")
            doc.LoadXml(xmlStr)
        Else
            Dim rootTag = doc.CreateElement("doc")
            doc.AppendChild(rootTag)
        End If

        Return doc
    End Function

End Module

Easy, effective, and ugly. Use at your own risk, it might eat your lunch sandwich.

Blog | Programming | .NET | VS.NET | Samples
Monday, January 16, 2006 8:49:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Article: Customizing generated Web Service proxies in Visual Studio 2005#

I'm proud and honored to see that my first article has been published on the Microsoft Belux website:

Customizing generated Web Service proxies in Visual Studio 2005

As the title implies, it talks about how you can customize the Web Service proxies that are generated by the "Add Web Reference" dialog box in Visual Studio 2005 or the WSDL.exe tool through a new mechanism introduced in .NET 2.0 called "Schema Importer Extensions".

Don't hesitate to let me know if you liked or disliked the article or the proposed solution!

Articles | Blog | Programming | .NET | VS.NET | Whidbey
Tuesday, November 29, 2005 9:47:26 PM (Romance Standard Time, UTC+01:00) #    Comments [8]  | 

 

Setting a default value for a color property#

Here's a tip on how to set the default value of a color property in the Visual Studio designer.

If you have a component that has a BackColor property, for example, and you want to indicate to the Property Grid that its default color is defined as the RGB values 255/255/192 (light yellow) then you might have some trouble. You can initialize a color at runtime from these RGB values by calling Color.FromArgb(255, 255, 192), but since there's no matching constant, you can't pass this into the DefaultValueAttribute constructor:

[DefaultValue(Color.FromArgb(255, 255, 192))] // Won't compile, needs a constant value
public Color BackColor { get { ... } set { ... } }

You can try using the overloaded constructor to pass in the type of the object and an initialization string that can be parsed by a TypeConverter (just as when you type it directly into the property grid), but for some reason I've yet to discover that won't work if you use the RGB values:

[DefaultValue(typeof(Color), "255; 255; 192")] // Won't work

The trick is to use the hexadecimal value of the RGB values as such:

[DefaultValue(typeof(Color), "0xFFFFC0")] // Works, 0xFF=255, 0xFF=255, 0xC0=192

Note that you can also define system or well-known colors like this:

[DefaultValue(typeof(Color), "Red")] // Works, Red=0xFF0000

Also, check out my post on specifying default values for enums.

Blog | Programming | .NET | Quirks | VS.NET
Wednesday, November 23, 2005 9:02:35 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

My definition of TDD#

There's been a lot of fuss lately around the Guidelines for Test-Driven Development (TDD) that's just been published on MSDN.

I don't want to join the mud-slinging fest since I don't feel I can adequately represent the Agile community here, but this seemed like a good time to mention how I see TDD:

Test-Driven Development is like going to the grocery store with a shopping list.

Hear me out.

If you need to go grocery shopping, you can take what I'll conveniently call the "bachelor approach", which means going to the store, traversing all the aisles, throwing everything in your cart that looks remotely delicious, and not knowing in advance how much you'll wind up spending. As you mature (which is an optional step), you'll notice that you're spending way too much money on stuff you don't actually need or can't use. So in time (or until you meet someone with more experience), you'll probably start thinking in advance about what you actually need, making a list before going to the store and then buying only what's on that list. That way, you'll have a much better idea in advance what you'll be spending and as you do this more and more, you won't get caught with groceries you don't need or have to throw away.

As I see it, this is just what TDD offers: by defining the specifications for your software (the shopping list) before implementing them (actually shopping and buying), and then writing only the minimal amount of code that's necessary to meet these requirements (only buying what you have on your list), your budget will be more predictable and you lower the risk of feature creap by not going wild on features you don't need.

That said, the "bachelor approach" can of course be more fun, and yield some pleasant surprises. But in serious enterprise software development, this is often a luxury that you can't afford.

Update: Sam Gentile has just posted a more accurate definition of TDD (mine isn't 100% waterproof but I think it helps transmit the message), and Microsoft's Rob Caron has announced they'll be fixing the original article that caused the upstir. Now if Microsoft would only pull all the pages off of MSDN that show how incredibly "cool" and "productive" it is to go straight from your GUI to the database, while failing to mention that there are reasons why you would want additional layers in between. The definition of n-tier should really include the precondition "where n > 2".

Tuesday, November 22, 2005 11:58:59 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

A Generic Singleton Implementation#

The Singleton design pattern is one of the most widely known patterns in the software industry. In .NET 2.0, it's easy to provide a generic implementation of this pattern so you won't ever have to write the code that makes it happen again. Even though there's not much code involved, it can be tricky to get right if you consider thread safety and laziness of instantiation.

If you use the class below, accessing a singleton of any class is as simple as calling Singleton<Foo>.Instance. The Foo type only needs to support a default constructor and never worry about the details of the singleton itself. Here's the implementation of the generic Singleton class:

/// <summary>
/// Provides a global point of access to a single instance of a given class.
/// </summary>
/// <typeparam name="T">The type to provide a singleton instance for.</typeparam>
/// <remarks>
/// <para>
/// The singleton instance can be accessed through a static property,
/// where the type of the singleton is passed as a generic type parameter.
/// Subsequent requests for the instance will yield the same class instance.
/// </para>
/// <note>
/// The singleton is thread-safe and lazy (i.e. it is created when the instance
/// is first requested).
/// </note>
/// </remarks>
/// <example>
/// The following example gets a singleton instance of a <c>Foo</c> class:
/// <code>
/// Foo singleInstance = Singleton<Foo>.Instance;
/// </code>
/// </example>
public static class Singleton<T> where T : new()
{
    /// <summary>
    /// Gets the singleton instance.
    /// </summary>
    public static T Instance
    {
        get
        {
            // Thread safe, lazy implementation of the singleton pattern.
            // See http://www.yoda.arachsys.com/csharp/singleton.html
            // for the full story.
            return SingletonInternal.instance;
        }
    }

    private class SingletonInternal
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit.
        static SingletonInternal()
        {
        }

        internal static readonly T instance = new T();
    }
}

Note the implementation using a private class with an explicit static constructor, this is to provide both the thread safety as the lazy instantiation. More details can be found in the "Implementing the Singleton Pattern in C#" article by Jon Skeet.

Monday, November 07, 2005 7:11:50 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

 

Downloading Visual Studio 2005 RTM#

The RTM version of Visual Studio 2005 has finally arrived! Downloading the goodies is still quite a pain though, I couldn't get any download started through MSDN Subscriptions yesterday, my colleague had more luck after trying a few times this afternoon but then he had a little hickup as well:

Fortunately, File Transfer Manager can resume :-)

Other than that, it seems that the Visual Studio Team Suite Trial Edition that was available yesterday has already been removed from the Subscriptions download page, as well as the Beta 3 Refresh of Team Foundation Server (also spotted by Sam Gentile). What's up with that?

Blog | Programming | .NET | VS.NET | Whidbey
Friday, October 28, 2005 3:50:25 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Are you ready for the launch?#

In just two weeks, Microsoft Belux is organizing what I personally think is the biggest launch event in the last couple of years: on November 10, SQL Server 2005, Visual Studio 2005 and BizTalk 2006 will finally be released to manufacturing!

Developers and IT professionals are invited to celebrate the launch of the new versions of Visual Studio, SQL Server and BizTalk Server together with us. The event will start with a keynote session by S. Somasegar, Corporate Vice President at Microsoft Corporation.

Further some of the best speakers and subject matter experts worldwide will entertain you. Make sure you bring along your most burning SQL Server 2005 or Visual Studio 2005 questions.

Later in the evening we offer a walking dinner and a party to remember...

You can find more information and subscribe for the event on the Microsoft Belux Launch site.

I've been asked to hang out at the Ask-The-Experts area as a Visual Studio Expert, so if you're there: come say hi and ask me some tough questions :-)

See you there!

Blog | General | Programming | .NET | VS.NET | Whidbey
Wednesday, October 26, 2005 6:36:12 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Copy To Output Directory#

Here's a tiny new feature in Visual Studio 2005 that caught my attention:

Pretty nice, now you don't need a post-build step with macros anymore to get some files into the output directory.

It doesn't seem to work all-the-way though: when running a Test Project, this setting won't copy the file into the running test's current directory. Other than that, it's still nice to see the "little things" that so often make the difference...

Blog | Programming | .NET | VS.NET | Whidbey
Thursday, October 20, 2005 10:22:08 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Generics & Invariance: An Interesting Problem#

Setting The Stage: Invariance

I had a rather interesting problem using generics in C# 2.0 today, but before I dig in to the actual issue, let me briefly introduce you to the "invariant" nature of generics.

As you know, you can certainly assign a string to a variable of type object because string is a subclass of object:

string s = "some value";
object o = s; // No problem

Now suppose you have a List<object> and a List<string>, which are generic types describing a list of objects and a list of strings, respectively. Given that string is a subclass of object, you might assume that you can assign this List<string> to a List<object> as well:

List<string> strings = new List<string>();
List<object> objects = strings; // Fails

The compiler, however, rudely disagrees with you: error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.List<object>'. This is because generic types in .NET are said to be invariant, which means that they don't adhere to the same inheritance tree as their type parameters (string and object in this case).

Although similar in nature, arrays don't share this nature and are said to be covariant, which means that the following code is perfectly legal:

string[] strings = new string[] {};
object objects[] = strings; // No problem

For more information and the reasoning behind this, check out Rick Byers' excellent post on Generic type parameter variance in the CLR.

The Problem Manifestation

With that in mind, imagine the following class hierarchy:

So there are two interfaces describing a document (IDocument) and a cabinet (ICabinet) which basically holds a generic list of these IDocuments. The Document class implements the IDocument interface in the obvious way. The interesting part is the Cabinet class:

public interface ICabinet
{
string Name { get; }
string Location { get; }
List<IDocument> Documents { get; }
}
public class Cabinet : ICabinet
{
  private string name = null;
  private string location = null;
  private List<IDocument> documents = new List<IDocument>();
 
  public string Name { get { return name; } }
  public string Location { get { return location; } }
  public List<IDocument> Documents { get { return documents; } }
}

To implement the interface, the Cabinet class has to expose the List<IDocument> through the Documents property, and since generics are invariant, this means that the return type cannot be List<Document> (note the use of the class here, not the interface). This implies that the private documents backing field must also be a List<IDocument> in stead of a List<Document>, for the same reason.

Now there's currently no link between the concrete cabinet type and the concrete document type and I really want this Documents property on my Cabinet class to expose a List<Document>, so that's a problem. The reason I need this, is that my cabinet class will be exposed over a Web Service and must therefore be serializable. As you might know, serializable types cannot publicly expose interface types because the serializer cannot handle them. But there could be other valid reasons why you would want the concrete cabinet type to return the concrete document classes.

Trying To Solve The Problem

I came up with a moderately satisfying solution... I implemented the private backing field and the Documents property using the "class version", and explicitly implemented the interface as such:

[Serializable]
public class Cabinet : ICabinet
{
  private string name = null;
  private string location = null;
  private List<Document> documents = new List<Document>();
 
  public string Name { get { return name; } }
  public string Location { get { return location; } }
  public List<Document> Documents { get { return documents; } }
 
  [XmlIgnore]
  List<IDocument> ICabinet.Documents // Explicit implementation
  {
    get
    {
      return this.Documents.ConvertAll<IDocument>(
        delegate(Document target)
        {
          return (IDocument)target;
        }
      );
    }
  }
}

The explicit interface implementation will not only "hide" the property from normal consumers of the class and resolve the obvious naming conflict of the two Documents properties, but the xml serializer will also ignore the explicitly implemented property (and just to be sure, I also applied the [XmlIgnore] attribute to it). If this property is queried anyway, it will convert the "class version" to the "interface version" using the generic ConvertAll method, which returns a new list of another type by converting each list item through an anonymous method. In this case, a simple cast is enough because they are covariant. This yields the following picture:

An Additional Problem

There's still an issue with this approach, though: if you come in through the interface and try to manipulate the documents list (for example, if you add an item to it), you will be working on this freshly-created copy of the list and your changes won't show up in the original list:

ICabinet cabinet = new Cabinet(); // Declared as the interface
cabinet.Documents.Add(new Document()); // Won't work as expected
Assert.AreEqual(cabinet.Documents.Count, 0);

Luckily, I had the option to change the interface to return a ReadOnlyCollection<IDocument> so that modifications weren't even possible and the problem went away. Luckily.

But if I weren't as blessed, I would probably have given up and steered away from these generic types altogether in this scenario since they generate quite some issues when used in inheritance hierarchies as you've seen. I'm a big fan of generics and they've surprised me in many interesting ways (just look at the sheer beauty of that ConvertAll combined with the anonymous delegate above), but this time I think I've stretched them too far...

Blog | Programming | .NET | Quirks | Whidbey
Tuesday, October 18, 2005 8:11:21 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Two Cool Tools#

I've said before that I'm not a "linking out" kinda guy, but these two tools are just too cool not to mention.

CR_Documentor

CR_Documentor is a free addin for Visual Studio (works on Visual Studio 2003 and 2005!) that shows a live preview of how the XML documentation under your cursor would look like in a help file.

I'm a real documentation freak, so this is pure heaven for me!

PowerTrace

PowerTrace is a free library that contains the TerminalTraceListener class, which "opens a port and allows you to connect to your app through Telnet so you can watch your traces in the lab or even across the globe" (quoting the November 2005 edition of MSDN Magazine's Bugslayer column).

I think that's a pretty clever concept: you're not wasting any disk space with trace files, in stead you can connect to a running app when there's trouble. You don't receive a history of the last x minutes or trace lines when you login though (which you will probably need to properly diagnose the problem) but it's still a great idea to add this functionality to production systems.

Blog | Programming | .NET | VS.NET | Whidbey
Tuesday, October 18, 2005 5:36:41 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Junior Mistakes#

I'm currently having fun at a client migrating the custom .NET framework I've helped build 3,5 years ago to the wonderful world of .NET 2.0. One of the more confronting things about it is that I stumble across pieces of code that I'm not really proud of or would do totally different today.

I'll spare you the details, but I think the following code snippet that I just came across says enough:

Public Class DataSetHelper

    Private m_Data As DataSet

    Public Sub New(ByVal data As DataSet)

        If data Is Nothing Then
            Throw New NullReferenceException("Class cannot be instantiated with its DataSet set to null.")
        End If
        m_Data = data

    End Sub

    ' Snip...

End Class

That just feels like throwing an OutOfMemoryException yourself... In my defense: at least I was doing argument checking ;-)

Time sure has flown since I was thrown into .NET 1.0... What's your favorite junior mistake?

Update: as a co-worker correctly pointed out, I should probably elaborate a bit on why this is a mistake...

Throwing a NullReferenceException is something that you should never do yourself. Just like throwing that OutOfMemoryException. It's a task that is reserved for the CLR when it finds that some code is invoking a method on a reference that has no object instance (a.k.a. it's null). To quote the MSDN documentation on NullReferenceException: "The exception that is thrown when there is an attempt to dereference a null object reference. Note that applications throw the ArgumentNullException exception rather than the NullReferenceException exception discussed here."

So if you want to validate that arguments which have been passed into a method, are null but shouldn't be, then you should simply throw an ArgumentNullException with just the parameter name and nothing more:

If data Is Nothing Then
    Throw New ArgumentNullException("data")
End If

For more fun with NullReferenceExceptions, don't miss my winning contest entry on Brad Abrams' pop quiz.

Tuesday, October 18, 2005 12:18:19 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

HtmlEditor 1.0#

As a proof-of-concept for the HtmlEditbox control in my just released Tools Library 1.2, and also because I really like having a light-weight html editor around, I wrote a little HtmlEditor application that's capable of editing html documents in a WYSIWYG fashion with only a few but all (hopefully) functional features:

  • Most standard features you find in basic html editors: text formatting, coloring, alignment, lists, indenting, hyperlinks, ...
  • Traditional file support (New, Open, Save, Save As) with proper change tracking and a prompt to save the file if and only if there are changes when you attempt to close it.
  • Open From Url: opens an html page which is directly downloaded from a Url.
  • View Modes: view and edit the html as raw text, in WYSIWYG mode, or as a non-editable preview of the result.
  • Clean: rids the html of redundant tags - specifically the &nbsp; tags that drive me mad.
  • Commands: allows the current selection to be surrounded by user-defined html tags, e.g. to quickly make the selection a <h1> or <pre> block (the list of commands can be customized in the configuration file).

Most of these features are supported directly in the reusable HtmlEditbox control, the application is basically just a wrapper around it with support for files and menus. Check out the screenshot below:

You can download the tool below. Don't hesitate to contact me for suggestions or bug reports!

The JelleDruyts.HtmlEditor 1.0 executable, without Mshtml (125,01 KB)
The JelleDruyts.HtmlEditor 1.0 executable, with Mshtml (1,69 MB)
The JelleDruyts.HtmlEditor 1.0 executable and source code, with Mshtml (3,39 MB)

Monday, October 17, 2005 11:05:08 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Tools Library 1.2#

Because I just released E = m c² 1.3 to the general public, I'm also officially posting the 1.2 version of my Tools library that it uses. In this version I added an HtmlEditbox (which is an embeddable html editor) and a DirectoryTraverser. This library now contains the following set of .NET classes:

  • AutoLinkLabel
    This LinkLabel will automatically check for http and https url's and email addresses inside its Text. It creates a link for each of those which will launch their system's default program when clicked.
  • DirectoryTraverser
    A class that traverses directories starting from a root path and raises events when directories and files are found.
  • EncryptionSurrogate
    This SerializationSurrogate will encrypt (while serializing) or decrypt (while deserializing) all fields of a certain type that are marked with the SerializeEncryptedAttribute.
  • EncryptionSurrogateSelector
    This SurrogateSelector will make all types that have the SerializeEncryptedAttribute on one or more of its members use the EncryptionSurrogate.
  • FlagEditor
    This class provides a design time editor for flag enums.
  • FormattedTraceListener
    This TraceListener will format output lines with additional information. Each line is prefixed with the current timestamp and the class and method that traced the message.
  • HtmlEditbox
    A general-purpose Html edit box that allows a user to edit Html in a WYSIWYG fashion.
  • InputBox
    A general-purpose InputBox that allows a user to enter a string of information.
  • MaskedEditor
    This class provides a design time editor for masked text, e.g. passwords. It will show a textbox with a password char in a dropdown so you can edit the value of the property that has this UITypeEditor.
  • MaskedTypeConverter
    This class provides a TypeConverter that converts a given object to a masked string, e.g. a password string to a series of '*' characters with the same length as the original string.
  • NotifyIcon
    A NotifyIcon that supports balloon popups.
  • StatusBarPanel
    This StatusBarPanel supports an embedded Control.

Enjoy!

The JelleDruyts.Tools 1.2 library and documentation, without Mshtml (257,67 KB)
The JelleDruyts.Tools 1.2 library and documentation, with Mshtml (1,82 MB)
The JelleDruyts.Tools 1.2 library, source code and documentation, with Mshtml (3,53 MB)

Blog | Programming | .NET | Tools | Download
Monday, October 17, 2005 10:51:45 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Notepad Bugreport#

The shock! There's actually a bug in Notepad! Now there's an application that's been around for literally decades (since Windows Executive 1.0, apparently, whatever that may be), arguably one of the most-used Windows apps, a tool with hardly any features at all, and still it isn't bugfree!

Here's the extensive repro for your reference:

  1. Open Notepad.exe (the original, not the unsupported third-party copycat Notepad2 mind you).
  2. Make sure Word Wrap (under the Format menu) is off, because for some reason that will hide the status bar (I guess Word Wrap makes it "too hard" to calculate the line and column).
  3. As you will see, the status bar is crucial in this scenario, so make sure the status bar is visible (through the View menu this time, it's such a crucial feature that it lives in its very own menu).
  4. Edit a text file, watch the status bar update the line an column numbers in real time (whow).
  5. Make sure you're not on the first line and first column (that would spoil the fun).
  6. Now simply save the file.
  7. Be dazzled! Lo and behold: the status bar is incorrect at this point! The line and column are both falsely said to be 1 although the cursor is really somewhere else:

If you too are suffering from this bug, you can work around it by moving your cursor around so the real-time status bar rendering engine kicks in again - but I find this behaviour simply unacceptable. Please, please fix this, Microsoft, I'm not afraid to use one of my four superfluous Incident Support calls that I got with my MSDN Universal Subscription to get a quickfix on this!

Update: I've checked the Beta 1 build of Windows Vista today and the bug is still in there. I guess they're looking at at least 3 months delay in their release schedule if they want to get this fixed...

Friday, October 14, 2005 7:58:25 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Assert.AreEqual not working properly for Arrays#

As you might know, Visual Studio 2005 Team Test Edition will feature testing tools to perform unit testing, load testing, functional testing, ... It's great to see Microsoft really getting serious about providing these much-needed tools where third parties such as NUnit have been filling the gap all these years.

Anyway, while implementing some unit tests with the Release Candidate earlier today, I noticed that the following test failed:

string[] list1 = new string[] {"Test"};
string[] list2 = new string[] {"Test"};
Assert.AreEqual<string[]>(list1, list2);

After some digging, it appears that the AreEqual method doesn't special case for arrays, it just performs an Object.Equals(list1, list2). In turn, this will result in list1.Equals(list2) and I would expect the equality check for arrays to actually compare the array contents (object equality) in stead of checking if they're the same object (object identity). Especially since there is an Assert.AreSame method which should serve the latter purpose. In fact, I can understand that AreEqual would call Object.Equals and AreSame calls Object.ReferenceEquals, but I figured that System.Array would have overridden the Equals method to perform the equality check. Apparently, I was wrong.

So I had to revert to a manual check to see if both arrays have the same length and contents:

Assert.AreEqual(list1.Length, list2.Length);
for(int i = 0; i < list1.Length; i++)
{
  Assert.AreEqual(list1[i], list2[i]);
}

NUnit has worked around this problem by putting similar logic into the Assert.AreEqual method, which makes more sense in my mind.

Thursday, October 13, 2005 10:17:02 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

ASP.NET 2.0 References#

I've been using the Release Candidate of Visual Studio 2005 lately, and I'm very pleased with the overall look and feel of the application. There are still some issues left to work out (I get the occasional crash and burn), so I hope they'll all be gone by the time they RTM in the week of November 7th...

One of the things I really wished they would have fixed, though, is the way references are handled in ASP.NET 2.0 websites or WebService projects. This is what I had to say about references in June 2004, and it still holds true:

Although certainly not always possible, the ultimate solution to problems with references has always been using Project References. So that's what I'm doing now, but in ASP.NET they tend to switch from project references to fixed binary references that don't get updated anymore. And you wonder why you get runtime errors... So you need to check those references regularly and reset them to project references. Joy! What's even funnier is that an ASP.NET site doesn't have a project file anymore, which would be fine if there was nothing to remember for an ASP.NET project. But what about these references? I'm sorry to have noticed that, but they're stored in the solution file. Bad, bad, bad... If you have multiple solutions with the same ASP.NET project in them, it would depend on the solution file which kind of references the project would use. If you really need project-settings, revive the project file guys and recall the "no project file needed"-hype. What's one file extra going to do anyway?

Apart from the project references randomly becoming binary references (I've seen it happen again in the RC!) I still think it's an even bigger problem that project properties are put into the solution file. If you want proof, this is a part of a solution file that contains a WebService project:

Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = http://localhost/TestWebService/,
"http://localhost/TestWebService", "{F7389224-931A-4652-A0E2-3A932D1B40C6}"
 ProjectSection(WebsiteProperties) = preProject
  ProjectReferences = "{BC53180F-394F-4F31-A61E-BFBC4DBF9B32}|BusinessLayer.dll;"
  Debug.AspNetCompiler.VirtualPath = "/TestWebService"
  Debug.AspNetCompiler.PhysicalPath = "..\..\..\..\POC\TestWebService\"
  Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\TestWebService\"
  Debug.AspNetCompiler.Updateable = "true"
  Debug.AspNetCompiler.ForceOverwrite = "true"
  Debug.AspNetCompiler.FixedNames = "false"
  Debug.AspNetCompiler.Debug = "True"
  Release.AspNetCompiler.VirtualPath = "/TestWebService"
  Release.AspNetCompiler.PhysicalPath = "..\..\..\..\POC\TestWebService\"
  Release.AspNetCompiler.TargetPath = "PrecompiledWeb\TestWebService\"
  Release.AspNetCompiler.Updateable = "true"
  Release.AspNetCompiler.ForceOverwrite = "true"
  Release.AspNetCompiler.FixedNames = "false"
  Release.AspNetCompiler.Debug = "False"
  SlnRelativePath = "..\..\..\..\POC\TestWebService\"
 EndProjectSection
EndProject

The rest of the settings are also project-specific (hence the ProjectSection) so I think it's all unacceptable. But I guess it's too late to change any of this...?

Update: I did some thinking and some of this might actually make sense... As the line in the solution file above actually states, it's a Project Reference. Since project references refer to other projects within the same solution file by definition, this is actually quite logical. I tried to do the same thing with a non-project reference (i.e. a binary reference) and it's not stored in the solution file. But before you start cheering, be aware that the reference isn't stored anywhere at all. The assembly is just copied over to the website and you'll have to update it manually if the reference changed. Not too brilliant either... The fact that project references tend to switch to binary references might have something to do with adding the website to a new solution which doesn't contain the referenced project, but I'm not so sure the reason is that simple.

Blog | Programming | .NET | ASP.NET | VS.NET | Whidbey
Thursday, October 13, 2005 8:23:50 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Deleting source-controlled items#

Now here's a dialog box in the Visual Studio 2005 RC I'm very happy with:

Finally, no more orphaned files in your Source Control Repository when you delete files from your project locally!

Blog | Programming | .NET | VS.NET | Whidbey
Thursday, October 13, 2005 9:51:34 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Releasing E = m c² 1.3#

E = m c² (apart from a silly little formula relating an object's mass to its energy content) is a pet project I've created a few years ago and that I'm still using everyday. It's basically a tray utility that can check various sources for messages, then filter and redistribute them. A typical example is checking your mailbox and showing you a popup window if there are new messages, but the scope of this tool goes beyond that since it's fully extensible using plugins. For example, you can check RSS files for new blog posts, be reminded of events on certain times, log messages to a database, play a soundfile when new messages arrive, send a summary email, run a program, ... Basically, you can make E = m c² do all you can think of!

Although I still have a lot of plans and cool ideas for the tool, it's been over 2 years since my last update, and that's mostly because I haven't had much time to work on it and I was always postponing the release of a new version due to a lack of big changes. So with Visual Studio 2005 and .NET 2.0 around the corner, I guess I won't be continuing it as it is right now but I'm moving all the cool new features I have in mind over to the 2.0 release which will be built upon Visual Studio 2005 and possibly WCF/Indigo.

Hence, this is the final release of the 1.x branch, if you care enough to give it a spin then don't hesitate to let me know how you like it!

The E = m c² 1.3 executable and documentation (251,54 KB)
The E = m c² 1.3 executable, source code and documentation (380,87 KB)
The E = m c² 1.3 Plugin Pack and documentation, without Mshtml (301,93 KB)
The E = m c² 1.3 Plugin Pack and documentation, with Mshtml (1,86 MB)
The E = m c² 1.3 Plugin Pack, source code and documentation, with Mshtml (3,75 MB)

New features on the core executable:

  • Added checkbox "Add to all distribution channels" next to "Add Plugin" button so you can opt out of this default behaviour.
  • Fixed bug where plugins weren't disposed when new settings were applied.
  • Made the plugin properties panel show categories.
  • Made sure the Filters treenode in the settings screen is now always in between the Publishers and Subscribers nodes.
  • About screen now closes on any keypress.
  • Tray icon is now gray when the application is disabled.
  • New features in the plugin pack:

  • Added "Toast" plugin.
  • ContentFilter now works in Block or Pass mode and can be made case sensitive.
  • Added Categories to the plugin settings.
  • Renamed Ping plugin to ServerStatus because it now also supports checking an url to see if a certain server is online.
  • Added XslFileName to RssWriter plugin.
  • Blog | Programming | .NET | Indigo | Whidbey | Emc | Download
    Tuesday, October 11, 2005 7:19:47 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Static Classes And The HasShutdownStarted Property#

    During the pre-conference session on Framework Design Guidelines, Brad Abrams was kind enough to share the story behind the infamous HasShutdownStarted property on the Environment class with us. If you're unfamiliar with that property, take a look at its original definition below:

    public sealed class Environment
    {
    private Environment() {}
    public bool HasShutDownStarted { get { /*...*/ } }
    }

    Now see if you can spot the "minor issue" with this property...

    The funny thing is, this code actually shipped in the initial release of the .NET Framework, but obviously it needed to be QFE'd (Quick Fix Engineered). So Brad did a little investigation to see how it could have happened that an effectively uncallable property made it into the framework. Running through the engineering callstack, he found out that:

    • The property was implemented and unit tested by the developer.
    • It was subsequently tested and approved by the tester.
    • It was documented along with "working" sample code.
    • And finally it was reviewed and accepted by the Program Manager who owned the feature (in his defense, he had originally specified that it should be static).

    According to the story, this incident triggered the static classes feature that is new in C# 2.0 (which declares a class as having only static members) to prevent this from happening again.

    By the way, declaring a class as static makes the compiler mark the class both sealed (so it cannot be inherited) and abstract (so it cannot be instantiated) under the covers; furthermore, the compiler checks that everything on the class is effectively static. It doesn't create a private constructor, as you might think, because there's no real point to do that anymore (instantiation is already prevented by defining the class as abstract) and to avoid the small metadata overhead induced by the additional constructor.

    Blog | General | Programming | .NET | Quirks | Whidbey | PDC05
    Friday, September 16, 2005 5:15:49 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    PDC05 Pre-Con: The Art Of Building A Reusable Class Library#

    Attending a pre-conference session is a bit like foreplay for the brain: it's not about going ahead and diving into the action, it's gearing up the body to prepare for the main dish.

    In that sense, I decided to cheat on the wife a little. So last Monday, in stead of going to the SOA pre-con I originally planned, I followed my instinct and went to one about a pet peeve of mine: API design. So I spent the entire day exploring "The Art Of Building A Reusable Class Library" by Brad Abrams and Krzysztof Cwalina, who are both very knowledgeable on the subject in their roles at Microsoft where they review the public API's of the entire .NET Framework. In fact, they just have a book out on the subject called "Framework Design Guidelines" and - lucky me - they handed out free copies for their attendees. Nice!

    The reason I chose this session, is that I'm quite passionate about building reusable libraries which have a clear, consistent and self-explaining API. I've been involved in designing reusable components and frameworks before, and it's indeed quite challenging to meet these goals.

    There are quite some factors to take into account when designing a framework, and they've been summarized in the session in four key topics:

    • The Power Of Sameness
    • Framework Design Matters
    • Tools For Communication
    • The Pit Of Success

    The Power Of Sameness

    When driving a new type of car, there's no point in reading the owner's manual: you just know how to operate a car, the seatbelt, the turn signals, ... because they all work the same way. This principle should be applied as much as possible to software as well: if your users know how to use previous versions or other parts of your program or framework, they will easily get started with a new version or with an undiscovered part of your framework.

    In framework design, this means that you must adhere to consistent naming guidelines, patterns (like prefixes and suffixes, take the "I"-suffix for interfaces and the standard Exception and Attribute suffixes for example), to lower the learning curve of your API.

    One important point of attention is to "optimize globally" in stead of locally, which means that even a justified deviation from the guideline shouldn't always be allowed, even if there are very good reasons to do so. In other words: you should dare to make one part "less good" to keep the overall system consistent and thus better. A practical example of a violation of this principle is the ArgumentNullException: the general pattern is that all exceptions take the message as their first parameter, but the ArgumentNullException takes the parameter name as its first (because that makes much more sense in this case). In retrospect, this shouldn't have been allowed, since it breaks the common usage scenario that developers are used to.

    Framework Design Matters

    A well-designed framework must, above all, be simple. To achieve this simplicity, you need to think like your users. A very convenient way of getting in their heads is to actually write code samples for the main scenarios first, and then defining the object model to support these code samples. Another way to make sure your framework is simple to use is to factor the namespaces to only include the most used types and move the advanced types into their specialized namespace so they don't clutter the view for the most commonly used scenarios.

    A well-designed framework must be explicitly designed. This means that you should create an API specification, review the scenarios with expected users, peers and non-experts, and finally review the API design.

    A well-designed framework is part of an ecosystem. Your API won't only show up in code, it should also be designed to look well in IntelliSense, in the Debugger, in the Properties Window, ... Also take care to make your framework CLS Compliant, since there are other languages in the ecosystem that might want to consume it.

    A well-designed framework must be integrated. Special points of attention here are to apply the proper abstractions (e.g. something I do a lot: don't explicitly declare fields and arguments as a Hashtable when an IDictionary is enough for your needs), and watch out for type name conflicts (it's not because it's in a separate namespace that a generic type name such as "Message" won't get in the way of your users).

    A well-designed framework must be designed to evolve. This is particularly hard, since versioning and building for the future means taking into account the unknown. In this sense, you should favor using classes (likely abstract classes) over interfaces since they are easier to version: adding members is a non-breaking change for classes, but not for interfaces. Also make sure to control your extensibility points, such as virtual methods, and only open them up if there is a good reason to do so.

    A well-designed framework must be consistent. This, of course, relates to the Power Of Sameness mentioned before, and is key to a good user experience. Specific rules include having consistent naming guidelines and using common patterns and idioms (like the Async pattern, the Dispose pattern, ...).

    Tools For Communication

    Consumers of your framework can't read your mind, so you have to communicate your intent. This can take the classical form of documentation, of course, but even if you have 500 pages worth of documentation, your API can still reek royally and your customers won't be happy. The layout of your namespaces, the naming patterns in your types, the exceptions you define, ... all form a common vocabulary that allows your consumers to feel familiar with your API (and of course, if your API adheres to the guidelines published by Microsoft, they'll feel right at home right away if they know their way around the .NET Framework).

    A few interesting highlights out of the different artifacts of that vocabulary:

    • Namespaces are just an organizational element, they have nothing to do with implementation (so there doesn't have to be a one-on-one link between the namespace name and the assembly name, for example).
    • Classes are a conceptual model for a thing so they should represent something with one single semantic meaning.
    • A struct is a domain specific extension of the intrinsic type system, so typically they should be smaller than 16 bytes and, quite importantly, immutable.
    • Exceptions should be defined wisely, and a new type should only be defined if the user would like to differentiate the way to handle this exception versus other exceptions you've defined.
    • An enum is a container for named constants; you should take care to explicitly assign the enum values and make their name singular, e.g. Color for a list of colors (except when they're [Flags] type of enum, in which case their name should be plural, e.g. AnchorStyles).
    • A constructor should be lazy and only capture state, make sure not to do too much work in the constructor.
    • A method exposes an action or operation, it doesn't return instance state as such.
    • A property is a logical backing field for instance state, but take care that it's not retrieved using an expensive operation (like going to the database), in which case you're better of with an explicit method.
    • A field is just an implementation detail, and it should never be exposed anyway.
    • An event is raised (not fired or triggered) to inform a subscriber of something that happened, and you should carefully follow the common event naming pattern to avoid confusion.

    The Pit Of Success

    The final part talked about a very interesting concept: you should guide the consumers of your framework into just "doing the right thing" if they don't really know what to do in a given situation. Doing the right thing shouldn't be hard as climbing a mountain or running through a desert - instead it should be as easy as just falling into a pit. Enabling this Pit Of Success can be accomplished by avoiding the Perilous Summit Of Complexity and the Desert Of Confusion. The first can be summarized as "making the simple things simple, and the hard things possible". The second means making consumers fall into doing things the right way by avoiding leading them down confusing and possibly wrong paths. A good way to put this into practice could be to provide easy-to-discover convenience methods for the most common scenarios (e.g. the new File.ReadAllLines method in the .NET Framework 2.0 that hides all the filestream plumbing in one simple method call).

    Finally, one other way to achieve the Pit Of Success is to remove unnecessary features - thereby reducing the surface area and the accompanying room for mistakes, which, in the end, adds value to your framework. Or to put it another way: you can actually reduce the value of your framework by adding features. So you should do as little as possible now (but no less) to ensure room for extensibility in the future.

    So this is the compressed takeaway I got out of this excellent pre-conference session. If you found this interesting (I sure do), I can highly recommend the book "Framework Design Guidelines" which is full of practical guidelines, do's and don'ts, along with their reason and additional annotations. Learn from it, and apply the guidelines to your own API's so your consumers will be happy to use what you've built.

    Blog | General | Programming | .NET | PDC05
    Friday, September 16, 2005 5:01:58 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    PDC05: The Third Keynote#

    In the third and final keynote, Bob Muglia talked about the server side of things in the roadmap that matters for developers. This was a much shorter session, but covered some interesting topics such as management, Monad, WinFX, Active Directory, ...

    Some interesting notes from the keynote:

    Management

    • WS-Management, which I've also seen in action at TechEd, has been defined as a unification of remote management of hardware, operating systems and application. It even enables remote management of WMI services cross-platform.
    • MMC 3.0 has been announced to host managed components, so you can finally write MMC Snap-Ins in .NET!

    Monad

    As you might already know, Monad is the codename of a new object-based command line language.

    • It has been created in .NET.
    • It works with thin commandlets.
    • In integrates the command line, COM and .NET

    Active Directory

    Active Directory has been around for a few years providing identity management and single sign-on across the enterprise. Now with Federation Services it will extend this single sign-on principle to work between different enterprises.

    Longhorn Server

    The server edition of Windows Vista should be ready in 2007. Of course, there are a lot of interesting enhancements and new features but a few highlights were pointed out:

    • Terminal Services will be accessible through firewalls, and it will support USB device redirection. Nice!
    • TxF is the codename for the Transactional Filesystem, which adds transaction support to an NTFS filesystem. This means you can finally have atomic changes to filesets (which can be very handy for certain Source Control scenarios and, why not, Transactional FTP?)
    • IIS 7, but this actually deserves a point of its own :-)

    IIS 7

    One of the most common problems with IIS to date has been the centralized and opaque nature of the IIS Metabase (which holds all the configuration data for the IIS server). It required an administrator to update the settings, and you were in serious problems if the metabase ever got corrupted. So it was very exciting to hear that the metabase is now officially dead. All configuration is now persisted in XML configuration files, and this even trickles down to the web.config files of the individual websites. Take for example the task of adding a default document to be served for a website, which can now be defined in its own web.config as such:

    <configuration>
      <system.webServer>
        <defaultDocument>
          <files>
            <add value="MyHomePage.aspx" />
          </files>
        </defaultDocument>
      </system.webServer>
    </configuration>

    Furthermore, IIS7 is completely modular, so you can remove modules that you don't use (e.g. CGI) and rewrite modules that you're not happy with (e.g. the DirectoryListingModule) - and all of this on an individual website basis and without restarting the server or IIS! This is very powerful and opens up a lot of interesting scenarios.

    So that concludes the last keynote, I'm off to some more in-depth sessions!

    Blog | General | Programming | .NET | PDC05
    Thursday, September 15, 2005 7:06:49 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    PDC05: The Second Keynote#

    Introduction

    Although the first keynote on Tuesday already ran way over time, Eric Rudder still found plenty more to talk about in the second keynote today. The main announcements were:

    • Windows Workflow Foundation: this natural extension to the .NET Framework enables developers to incorporate both System Workflow as Human Workflow into their applications. Microsoft is actually using this themselves in their next versions of SharePoint and Office "12".
    • Microsoft Expression: this is a suite of designer tools (codenamed Acrylic, Sparkle and Quartz) aimed at graphics and web designers.
    • Visual Studio Tools for Applications (VSTA): think of this as the next version of Visual Basic for Applications (VBA), but of course now within the managed .NET runtime. So this technology enables application customization in .NET, and it's already committed to by its integration into Office InfoPath "12".

    Windows Workflow Foundation

    The main statement that caught my attention was that "Workflow is everywhere": every "if" statement you write is a branch that effectively represents a lightweight workflow step; every webpage transition is a choice you make and can be modeled in a workflow.

    So Windows Workflow Foundation sets out to capture complex workflows, expose them to developers and enable on-the-fly extensions to them for added flexibility. Although this sounds impressive, in the end, all it is is "just another .NET namespace": you can execute all of this through managed classes. Furthermore, you can design the workflows right in Visual Studio, very much like you can model Biztalk orchestrations right now.

    As a small remark (but I think this is quite big news), it was noted that Microsoft will be providing the tools to embed these workflow designers inside your own applications, so your end users will also benefit from this runtime.

    As a demo, we were shown how this currently works: there's a new project type in Visual Studio, you can define the state machine in a visual designer which is persisted as a .xoml file and you can program the individual activities through managed classes. Furthermore, the entire development experience supported since you can visually set breakpoints in your workflow designer and it will just work in the debugger. Incredibly powerful!

    Microsoft Expression

    Acrylic is a tool that allows you to create vector-based graphics and export them as XAML.

    Quartz Web Designer allows a web designer to create ASP.NET ASPX and master pages in a rich designer environment that supports XML/XSLT and even ASP.NET controls with their full design-time power (such as smart tags). It even features a built-in web server that runs your website locally.

    Sparkle is basically a XAML designer that uses the same project and MSBuild system as Visual Studio.

    The last two tools actually make me wonder how large the overlap is with Visual Studio: since they share so many features (designers, web server, project system, build system, ...), I would think that the tools are just customized and "skinned" versions of Visual Studio, but targeted at non-developers...

    Visual Studio Tools for Applications

    The VSTA platform looks promising, and we were shown a demo that showed the runtime in action in a special build of AutoCAD, but there wasn't really much info available on the runtime or on the capabilities of the platform. I'm sure we will hear much more about this in the near future, since VBA is still one of the most used extension platforms for a lot of applications.

    Office 12

    During the second part of the keynote, more time was dedicated to showcasing the upcoming Office "12" release. The user experience seems much improved, but unfortunately, we won't be able to get our hands on it for a few months yet. For PDC attendees, this is truly a shame, since we're all quite thrilled about it and they're hyping it all up, but we need to wait a few months before we can actually start using it...

    Some random notes from the Office 12 part:

    • SharePoint (Windows SharePoint Services as well as SharePoint Portal Server) is positioned as the core element of the entire Office System.
    • Office 12 will have blogs and wiki's as part of the collaboration framework. You heard it right, blogs and wiki's!
    • You can think of the new FrontPage as a designer or management tool for SharePoint, e.g. you can use it to define new workflows (which are of course also persisted as .xoml files).
    • You can finally use InfoPath forms over the web! Another cool feature was that you can now store and share design snippets for reusable parts of your InfoPath forms. Nice!

    So that wrapped up the second keynote, less interesting (apart from the Windows Workflow Services) but nevertheless lots of promise for the future.

    Blog | General | Programming | .NET | PDC05
    Thursday, September 15, 2005 12:07:23 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    PDC05 Keynote#

    Introduction

    The keynote for this year's PDC traditionally started off with a roadmap of the Microsoft platform from the times of yore. In waves of 10 years, we received new foundations to build upon:

    • In 1975, the software industry really took off as a consumer-oriented market.
    • In 1985, there was MS-DOS and the PC that we could take for granted.
    • In 1995, Windows '95 established the new GUI capabilities that we still use today.
    • In 2005, we can really depend on the internet, .NET, XML and WebServices to build connected applications.

    Windows Vista

    The keynote went on to talk about the three C's of Windows Vista: Clear, Confident and Connect. Things that caught my attention in those areas were:

    Clear

    • The ALT-TAB window looks much nicer, with live previews of the windows in a sort of carroussel mode.
    • You can also get a very nice 3D view of the open windows.
    • If you hover over a taskbar item, you get a nice little live preview of the associated window (a bit like the little inline Windows Media Player toolbar).
    • Search is embedded everywhere, also in the Start Menu, where you can quickly filter all items collected from your start menu, your applications, your favorites, ...
    • A virtual folder in Windows Explorer is actually just a persisted query stored in an XML file, so you can actually open the virtual folder in notepad and see the XML.
    • The sidebar is back! I heard some rumors that it would disappear but it's there and still looking as nice as before. The individual panels on it are now called "gadgets" (I don't think that was how they were called before.)
    • There's also a little something called "Sideshow", which looked kind of like a PDA built into the case of a laptop where you get quick access to your email, calendar, ... without booting your laptop.

    Confident

    • There's built-in support for Parental Control, so you can define which games can be played by who for example.
    • A lot of work has been put into anti-phishing. For example, there's a new Dynamic Protection Service, which blocks websites which have been marked as phishing sites. You can easily mark sites as phishing, so they will be blocked for everyone automatically; you can also request to unblock sites you believe are falsely blocked. A team is actively maintaining the list, so if they can keep this up-to-date, it could be a viable solution to the problem.

    Connect

    • IE7 supports tabbed browsing, as we already knew. The pretty cool extra feature is that you can get a PowerPoint-like slide overview of all your open windows and manage them through there.
    • IE7 has built-in support for discovering RSS feeds and subscribing to them. The only problem I have with it, is that it heavily uses the orange XML icon we all know now, but I wouldn't want this to become too mainstream. XML does so much more than RSS that it's just stupid to use it as a "marketing icon" for it. Why not just use an orange RSS icon?
    • Microsoft is also anticipating RSS to be used for much more than just subscribing to feeds: businesses will depend on a syndication format more and more as they connect to their partners and suppliers. One example was the new version of their CRM solution, Microsoft Dynamics CRM, which uses RSS to let a user subscribe to changes in the backend CRM system.

    Office 12

    After touring Windows Vista, Office 12 was next up. The target of Office has always been to "Get Better Results Faster" and there sure seem to be a lot of productivity enhancements. As a fun fact, Word 1.0 had 100 commands (in the menu bars and toolbars), Word 2003 has over 1500! So with that in mind, we basically got a quick tour of Word, Excel and PowerPoint running under Vista and it truly looks wonderful.

    Windows Vista and Office 12 are both scheduled for release the second half of 2006, so that's getting pretty close already. I'm actually very much looking forward to getting all this power, so I might start really using Windows Vista as my main OS. I'll keep you posted if it works out :-)

    Pillars Of Longhorn

    In the second part of the keynote, Jim Allchin came out to revisit the four pillars of Longhorn as they were originally set out at the PDC two years ago (Indigo, Avalon and WinFS on top of a common Core). They still seem to be in the current version of Vista although, for a long time, I thought they were gone: Indigo and Avalon were backported to Windows XP SP2 and Windows 2003, and WinFS seemed to be postponed to Longhorn Server. Here's how they look today:

    Core

    • The security system has been adapted so that users can more easily run in least-privilege accounts.
    • SuperFetch is a service that monitors your application usage over time (seconds, hours, days, ...) to see which applications you use most and pre-loads them so they start up much faster.
    • If you stick in a USB drive, the system will notice this and start using it as extra RAM. Funky!
    • Another attempt has been made to reduce the number of reboots by 50%. In fact, it seems they can now shut down part of the system to replace dll's while the rest of the system keeps running.

    Presentation (formerly Avalon)

    • They've officially announced the Atlas technology, which is basically an AJAX framework integrated into ASP.NET 2.0 and Visual Studio 2005.
    • The Windows Presentation Foundation consists of both the engine (allowing applications to run on screen real estate varying from devices to laptops to 21" screens to wall-sized video screens) and the framework (the managed classes you can use to build your application).
    • Newly announced is WPF/E, which stands for Windows Presentation Foundation/Everywhere. This is a lightweight stripped subset of the WPF runtime for use on devices, and is based on XAML and JScript (so no support for C# or other managed languages to code your application in).

    Communication (formerly Indigo)

    • Relatively new to the Windows Communication Foundation (for me anyway) is InfoCards, which is a federated claims-based identity system. Important to note is that the OS manages all your identities and use one type of window and thus one user experience to log you on to different remote systems.
    • Another feature shown is People Near Me (PNM), which seems to have locality-based information about the people you know.

    If you think both these features seam a little vague, I can't blame you: they weren't really explained very much yet and I'm not sure what they do exactly or how to use them (but there will be plenty of sessions to explain later this week).

    WinFS

    Actually, this part wasn't covered today, so I'm assuming the "Windows Storage Foundation" (wild guess on the future official name) will still only appear in the Longhorn Server timeframe.

    Lap Around Vista

    By far the coolest new feature that was announced today is LINQ, or .NET Language Integrated Query. This means you can write SQL-like selects with filtering and sorting as first-class citizens in the C# language. I'll definitely be spending time exploring this feature but in short, it means that you can query any object that implements IEnumerable<T>. Look at Dan Fernandez's blog for more info on LINQ and a code sample. Really, really cool!

    Another surprise was the return of ObjectSpaces (once again). This time, using this long-awaited Object/Relational Mapping framework looked very similar to XML serialization in some way: you can decorate your types with attributes that define their mapping onto the database and you're pretty much done.

    To conclude, Don Box, Anders Hejlsberg, Chris Anderson and Scott Guthrie came out to run a lap around Vista. The demo started with Anders Hejlsberg building a LINQ query doing a cross select of the currently running processes with an ObjectSpaces-fronted database containing process description. So that was one query, in native C#, that did an in-memory join between objects and the database in a very easy and recognizable format.

    After that, Don Box exposed this information as an RSS stream through an Indigo service. A custom PoxBinding stripped off the SoapEnvelope at the top and as such, just doing an HTTP/GET on the service returnd a valid RSS stream, which was consumeable from IE7 out of the box.

    To go even further, Scott Guthrie made ASP.NET consume this feed through an Atlas client, which means that it was queried asynchronously and with a pretty slick UI.

    Chris Anderson concluded by showing the new Avalon rendering in action to show the items in the feed through fancy 2D and 3D views with minimal amounts of code and XAML.

    All in all, this was a very impressive demo, although some parts were obscure and highly customized (the PoxBinding, the special plumbing to consume the Indigo service from JScript, some of the XAML formatting, ...).

    Wrap-up

    At the very end, Hillel Cooperman showed us a demo application called Project Max that was built to showcase all these different components: it's a photo-sharing app that you can actually use and download at the Project Max Homepage. Looks good!

    As a sidenote, the live transcript that was running while the speakers were talking was pretty cool, especially the fact that the person typing the transcript was pretty fast but apparently not very technical. While trying to keep up with the speaker, things like WinFS were consistently transcribed as WinFX (understandeable but critical mistake in this case), RSS became RSF and lots of other small mistakes. But the funniest one was where "things like RSS" was transcribed as "things like our asses" :-)

    Anyway, that pretty much concludes the keynote. A great start for a promising conference!

    Blog | General | Programming | .NET | PDC05
    Wednesday, September 14, 2005 12:45:03 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    PDC05: Ready To Rumble!#

    Just like the rest of us who flew in on the special EMEA@PDC charter yesterday (check out David's blog for the list of Belgians), I arrived in L.A. without problems, all ready and geared up for the PDC 2005! The community is looking good, the vibe is definitely here, and I'm looking forward to spending a week in braincooking mode.

    Here are some of my pictures from the first day (check out the other blogs for much better ones):

    Departure in Amsterdam

    Flying over Greenland

    Flying over L.A.

    Arrival at LAX

    Downtown L.A. seen from the bus to the hotel

    The welcome reception at the pool

    And I fully intend to keep making this kind of crappy pictures with my phone so stay tuned for some more low-res fuzzy images of PDC05 - because that's how it feels over here anyway ;-)

    Blog | General | Programming | .NET | PDC05
    Sunday, September 11, 2005 4:26:54 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Introducing Steven Wilssens#

    My colleague Steven Wilssens is one of the most knowledgeable people I know personally (on .NET related topics anyway ;-) ), and he's just started blogging! So be sure to keep an eye on his blog for some solid information about all things .NET, Indigo (WCF), Software Configuration Management, Team System and lots more!

    On a job interview we were conducting together a few weeks ago, the applicant couldn't really explain the difference between passing arguments "by value" and "by reference", so Steven just used the can of soda in front of him as an analogy: if I pass the can by value to you and you drink from it, I'll notice it afterwards and get mad at you for stealing my drink (assuming the can is a reference type, like an ICollection of soda molecules if you will). If I pass it to you by reference, you can drink as much as you want - you can replace the entire can altogether so I won't notice :-)

    So that's the kinda guy he is, check him out!

    Sunday, August 21, 2005 6:36:52 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Article: Applied Visual Studio 2005 Enhancements In WeFly247#

    My first "official" article just went online here on my website: "Applied Visual Studio 2005 Enhancements In WeFly247".

    There's a link to the article in the Beta Experience Newsletter that was just sent out today to everyone who subscribed to it (so welcome, all, and stick around for more!). So if you have a special interest in Visual Studio 2005 and .NET 2.0 be sure to check out the Beta Experience!

    And keep reading my blog, of course :-)

    Thursday, July 28, 2005 12:02:40 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    WeFly247 now available in Australia#

    Cool, if you live in Australia, you can now order WeFly247 as well (via frankarr). You can order just WeFly247, or WeFly247 with the Visual Studio 2005 Beta!

    That's the first country outside of EMEA that I've heard of that's also giving away free copies. Good thing we localized it to Aussie :-) Enjoy!

    Update: New Zealand is also giving it away! (Via Nigel Parker, thanks Frank!)

    Thursday, July 14, 2005 7:01:15 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    TechEd Day 2#

    Keynote 1: Ready For Business (Andrew Lees)

    The official keynote was a combination of a "show" and a talk, with Corporate VP Andrew Lees being regularly swapped out for a couple of (what I presume to be) actors in the typical Developer/IT Pro/User roles. I'm sure we've all been there as one of these roles, just not getting anything done from the other ones; but it's all about team work, and the speach was mostly about how the Microsoft products will help these teams work together. Along with a number of videos and flashy demos (not built in Avalon though, which is a shame) this was quite an entertaining keynote, but nothing shockingly new of course.

    There was one pretty cool demo about WS-Management (a new systems management protocol) that showed MOM sending management queries to a non-bootable machine (I'm still puzzled by which stack or hardware component). And since it's an open specification, it even worked on a Sun Solaris which they demoed live on stage. Nice!

    They also mentioned that Microsoft Learning is currently offering free skills assessments and free trainings on Visual Studio 2005 and SQL Server 2005. Really nice!

    ARC302 - Building And Using A Software Factory (Steve Cook, Annie Matthewman)

    While the presentation was pretty "dry", I got quite some thinking out of the whole Software Factory concept. I think of Software Factories as the two-steps-up evolution of the Enterprise Templates you might remember from a few years ago when Visual Studio .NET first came out. They're basically an abstraction from recurring enterprise-specific patterns, implemented as Domain Specific Languages (DSL's). In a basic sense, this boils down to project and code generation inside Visual Studio 2005, along with graphical editors and designers that drive this generation process. It can be as simple as providing an editor for the UIP Application Block's configuration file (which they demoed), to an entire code generation of your business layer based on a service description.

    They used the Guidance Automation Toolkit (GAT) to build their DSL editor, so it's good to think of this as a "Factory for Factories". This is a tool that will mostly be used by architects defining and implementing DSL's for an enterprise scenario. At first sight, creating a DSL using the GAT seemed doable for one-way code generation (I also took a Hands-On-Lab on it), but I wonder how hard it would be to implement it in a two-way synchronized mannor. Nonetheless, impressive stuff, but it will take some significant effort to implement this into your current software development strategy.

    ARC309 - Microsoft Visual Studio 2005 Team Edition for Software Architects: Developing Service-Oriented Systems (Eric Lee)

    This was an excellent overview on the Team System features for architects, mainly about the four Distributed System Designers that will help you create executable as well as deployable designs: the Application, System, Logical Datacenter and Deployment Designer. There isn't really a lot more to say about it other than the fact that Eric Lee is also a very good, enthousiastic and knowledgeable speaker. Great talk!

    CTS365 - Implementing "Indigo" Endpoints: Addresses, Bindings and Contracts (Christian Weyer, Steve Swartz)

    Steve Swartz did a nice job of explaining Indigo endpoints. He outlined the three types of contracts: the data contract (schema and versioning), the message contract (SOAP headers), and the service contract (behaviour).

    The way to version a data contract is stunningly easy in Indigo: just add the VersionAdded, MustUnderstand and IsOptional attributes to your data member and you're set to go:

    [DataMember(VersionAdded=2, MustUnderstand=false, IsOptional=true]
    public string SomeNewField

    The Indigo runtime will now have two-way support for this member, i.e. the new type will still work in the old service, and the old type will still work in the new service.

    The proposed way to version a service contract was to inherit your new ServiceContract interface from the old one:

    [ServiceContract]
    public interface ICalculator2 : ICalculator

    I'm not sure if this is really the way to go (it feels too much like MSXML, MSXML2, MSXML3, ...) but I'll give it some more thought before I make up my mind about this.

    Another point he made was that Exceptions are problems in code, whereas Faults are error responses from the server. Since these Faults are also a WSDL standard, there's explicit support for them in Indigo through the Fault class:

    DivideByZeroFault f = new DivideByZeroFault("Cannot divide by zero");
    throw new Fault<DivideByZeroFault>(f);

    There was also a cool demo showing the fact that you can return a Stream from a service operation, allowing infinite streaming data to be passed back (e.g. audio broadcasting, or webcam streams).

    Finally, on the behaviour part of Indigo services, developers tend to care mostly about the concurrency and instancing behaviours (can recurrant calls be made, is it a singleton or singlecall service, is session state available, ...), whereas deployers care more about throttling and metadata behaviours (how many concurrent calls and instances can I have, is the WSDL available online, is WS-MetadataExchange supported, ...). All these types of behaviours can be defined by simple attributes or configuration settings.

    All in all, a very nice talk showing some advanced capabilities of Indigo.

    CHT072 - Drilldown into Visual Studio 2005 Team System (Eric Lee)

    This interactive Chalk & Talk session gave a good demonstration about Team System, with Eric Lee showing how to create Team Projects and work in them, while answering questions from the attendees. Again, this guy seems to know pretty well what he's talking about, so it was a nice wrap-up for the day.

    (Apart from the fact that there were free drinks in the community hall afterwards, but I'd argue that the Dutch beer doesn't really count as free drinks ;-) )

    Monday, July 11, 2005 8:06:45 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    TechEd Day 1 - Building Connected Systems Today#

    After arriving a little late at TechEd in Amsterdam yesterday morning (due to the obligatory Dutch traffic jams), I went through the rather quick and painless registration process[1], quickly grabbed some healthy breakfast[2] and dove into the "Building Connected Systems Today" Preconference Session.

    The proposed new approach to building connected systems is to introduce a new 3-part service based system model, supplementing the more "traditional" 2-part Business and Technical Model with an intermediary "Service Oriented Architecture" Model. This SOA Model forms the bridge between the capabilities, processes and SLE's (Service Level Expectations) defined by the business, and the technical services offered by an implementation framework (e.g. Enterprise Services or WS-ReliableMessaging). So the SOA Model basically defines the Service Contract (implementation independent, by using WSDL and XSD), the SLA (Service Level Agreement) and optionally the Orchestration rules defining the business processes, again independent of the used technology.

    The Business Model can be created by adopting a new modelling strategy called "Motion", which is entirely based on this concept of Capabilities, that only describe the "what" (structural information) and not the "how" (dynamic process information). Actually, this business modelling part wasn't that interesting to me, as it seemed mostly a high-level work-in-development, and it seemed like "just another methodology" to me without much real practical value for me in my current position. But maybe it could mean a lot to people who actually know what BPR, Six Sigma, Lean, Zachman and TOGAF stand for. Currently, there's also no tooling to support this new methodology, but they're implementing it as a DSL (Domain Specific Language) in a Software Factory (talk about buzzwords that are bound to come back in the coming days).

    The SOA Model can be created by applying the "Pragmatic SOAD" approach (Pragmatic Service Oriented Analysis & Design), which is basically a higher-level mapping of our old-time friends: use cases, collaboration diagrams, message exchange patterns, canonical data, ... to model the actual Services.

    Finally, the Technical Model was shown as an implementation in C# which had endpoints as WebServices and as COM+ ServicedComponents, and which was partially created by tools and code generators powered by the GAT (Guidance Automation Toolkit, another hype in Software Factory land). Not a lot of surprises there, and I was actually much more impressed by Scott Hanselmans TechEd US session on a practical approach of using code generation from WSDL and XSD all the way to actual code and technical as well as functional documentation.

    One interesting slide showed the platform capabilities for the 2005 and Longhorn product waves, defining that Indigo, Avalon, Office 12, BizTalk Server 2006, SQL Server 2005 and WinFS would become increasingly important. I'm glad to see WinFS is still alive and on the roadmaps as there has been quite some confusion about its future. Alongside BizTalk Server, a service called WWS or "Windows Workflow Services" was mentioned, which I hadn't heard of before. It's basically a lightweight workflow server baked right into the operating system. Sounds interesting!

    Generally, I'd say the first day was a nice warm-up for the content to come later this week, but not much new just yet.

    [1] Goodies:

    Swag[] techedSwag = new Swag[] {
       backpack, tshirt, baseballCap, msdnMagazine,
       new FreeLicense[] { SQLServer2005, VisualStudio.NET2005StandardEdition, VirtualServer2005 },
       randomOtherStuff };

    [2] Cookies, muffins, coffee. Yum.

    Tuesday, July 05, 2005 11:45:28 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    TechEd WeFly247 Swag#

    Now that WeFly247 has finally shipped (as part of the Beta Experience) and rapidly gaining traction, each member of the team received a cool "Contributor Award" package for getting this sucker out of the door, containing a flight ticket, a miniature plane with a name-tag, a teddybear (just like the one in the Duty Free shop of the Passenger Website!) and some vintage stand-alone WeFly247 DVD's (not found in the Beta Experience package). Nice!

    WeFly247 Contributor Award

    So seeing it's TechEd in Europe next week and you're all running around looking for free food and swag in stead of attending the 400+ sessions packed with material that is determined to overflow your brain, I figured I might as well just hand some of these limited-edition DVD's out to whoever gives the best impression of the number two engine of a Learjet 60 that just ran out of kerosine.

    Oh, you'll also win if you just recognize me and beat it out of me (I'm not giving you my lunch money, though).

    So if you see me in Amsterdam this week, come and say hi!

    Sunday, July 03, 2005 7:22:37 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Blog'n My Way To The PDC05#

    A free ticket to PDC05 just by blogging, you say? Try and stop me!

    blogging my way to pdc

    Apart from the obvious reasons why I think I should attend the PDC (me being a consultant and blogger, focused on pretty much all things Microsoft, and .NET more specifically, which basically means I help sell your platform, e.g. by developing widely distributed end-to-end sample applications on .NET 2.0 Beta 2, for which I require all the information I can get as soon as I can get it, in order for me to generate money for your company and ultimately your paycheck, dear judge - think about that for a minute, I'm doing you a favor here dammit, so giving me a ticket is the least you can do... but I digress), here's the real deal:

    • I'm from Belgium, where, as you may or may not know, we make incredible chocolates and fantastic beer. I'll take as much with me as I can smuggle through customs, and throw a party for all you Channel 9'ers out there.
    • I was at PDC03 but forgot my undies after the most excellent "Women Who Code" BOF session, so I really need to get back and pick them up before they start to tear down the L.A. Convention Center on grounds of "asbestos smell".
    • I tipped my cab to wait for me at LAX when I flew out, and I'm anxious to see if he's still there.
    • I didn't get to show my weewee to Don Box at any conference yet, and I feel I'm just as entitled to show off my manlyhood as Rory is.
    • I'm planning on speaking at PDC07, so I'd like to check out the locations of all the electric outlets for my talk's setup. You don't expect me to fly out just to do that, do you?
    • I'm also planning on speaking at PDC05, but unlike last time, I'll try to wait until the guy with the mic has finished his talk.
    • My boss just bought a Porsche (really) so now he can't afford to buy me a ticket anymore. Do you think it's fair that I'm a victim of his mid-life crisis? (Hi Peter! Good thing my annual review is just over, eh?)
    • I missed the gig by the Band On The Runtime at the Standard Hotel last time, but I've been preparing a replacement Channel 9 drummer (sorry 'bout that Carl).
    • Despite popular belief (and the picture on my site), I'm actually a gorgeous young female with perfect measures, an IQ of 0x9D and a higher libido than the average rabbit on viagra. Where do you want to meet up?

    Right, that should do. See you there!

    Blog | General | Programming | .NET | PDC03 | PDC05
    Sunday, June 12, 2005 10:10:02 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    When ASP.NET 2.0 throws 404's at you...#

    If you're getting curious HTTP 404 errors in your ASP.NET 2.0 application, be sure to check for a zero-length "magic file" called app_offline.htm and delete that. If this file is present in your web site, then the entire application will be considered offline by the ASP.NET runtime.

    While certainly useful as such to quickly disable an entire site, it's not so great that the "magic file" can also "magically appear", especially since there's no indication as to why you're getting the 404, so you can spend quite some time chasing bugs that aren't there. Fortunately, post-beta 2 builds will be providing a proper explanation.

    The reason it can appear by itself is that it's used to "take the application offline" when copying a web site. If this operation crashes somewhere in the middle for some reason, the file doesn't get deleted (while we wait for a transactional filesystem anyway) and bite you in your, errr, isapi filter.

    Blog | Programming | .NET | ASP.NET | Whidbey
    Monday, May 23, 2005 11:10:53 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    Generics at the Indigo service boundary#

    It seems that Don Box is quite into continuations, iterators, anonymous methods and all sorts of other mysterious corners of programming lexicology lately. Some of the stuff he's showing is some pretty cool and nifty higher-order logic with C# 2.0, although I don't really see that happening in real-world enterprise applications any time soon. It's just another tradeoff between programming power and readability (which is, of course, directly related to maintainability).

    Throughout his posts, generics are ubiquitous - and rightly so, since they're one of the most powerful and appealing new constructs in .NET 2.0. Funny thing is, Indigo (his brainchild, one might say) doesn't really seem to dig generics at the service boundary: if I specify a generic interface as an Indigo [ServiceContract], it will apparently blow up at runtime...

    Take the following and run it into the Indigo/Avalon (affectionally called WinFX) Beta 1 RC that was just released:

    [ServiceContract]
    public interface IServiceProvider<T>
    {
        [OperationContract]
        T PerformService(T item);
    }
    
    public class ServiceProvider<T> : IServiceProvider<T>
    {
        public T PerformService(T item)
        {
            return default(T);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Uri serviceUri = new Uri("http://localhost/ServiceProvider");
            ServiceHost<ServiceProvider<int>> host = new ServiceHost<ServiceProvider<int>>(serviceUri);
            host.AddEndpoint(typeof(ServiceProvider<int>), new BasicProfileBinding(), "ep");
            host.Open();
            Console.WriteLine("Running. Press any key to stop...");
            Console.ReadLine();
        }
    }

    If I run this, all will seem fine. Until I request the wsdl (http://localhost/ServiceProvider/ep?wsdl): it gives me an HTTP 500 internal server error. Doing the same without the generic interface (replacing T with int directly) will work just fine.

    I wonder what went wrong. Probably something in the reflection part where they don't deal with generics properly yet...

    Anyway, it's great to finally have one machine with Visual Studio .NET 2005 Beta 2 and WinFX Beta 1 RC working together. A largely undiscovered land lies ahead, and I've got my backpack ready :-)

    Blog | Programming | .NET | Indigo | Whidbey
    Monday, May 23, 2005 10:11:47 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    WeFly247 Overview Webcast#

    Yesterday, we hosted a public webcast to give an overview of the WeFly247 project (which is part of the soon-shipping Visual Studio .NET 2005 Beta Experience, as I've mentioned before).

    I think it gave a pretty good impression of all parts of the project (overview, demos, architecture, code highlights), and it was certainly fun to do. Talking online into the void without any feedback whatsoever takes some getting used to, but it seems to have gone pretty well.

    If you missed it, you can replay the recorded show directly from the LiveMeeting site.

    Tuesday, April 19, 2005 11:50:38 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Geeks Have More Fun#

    A snippet from a mail conversation today to discuss dinner plans (names scrambled to protect the geeky; by the way, "GPL" is a well-known location):

    --- Original message by Unaf Pbearggr:
    > I rest my case.
    >
    > y0.
    >
    > --- Jelle Druyts wrote:
    >>
    >> And BOY would we be wrong :-)
    >>
    >> Pretty sweet though, should I blog this ;-)
    >>
    >> --- Original message by Unaf Pbearggr:
    >>> unaf and jelle @ vallet parking lot:
    >>>
    >>> Our car would be the one having the "geeks have more fun" sticker on
    >>> it.
    >>>
    >>> --- Jelle Druyts wrote:
    >>>>
    >>>> <ascii>0000 0110</ascii>
    >>>>
    >>>> --- Original message by Unaf Pbearggr:
    >>>>> >>>>> 0x12 # !
    >>>>>
    >>>>> Jelle Druyts wrote:
    >>>>>>
    >>>>>> GPL@0x12
    Wednesday, April 13, 2005 11:38:59 AM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    WeFly247 Developer Interview#

    Yesterday, Rob Macdonald recorded a "WeFly247 Developer Interview" with me to be put on the soon-to-be-released WeFly247 DVD. We discussed the implementation of the different applications I worked on and a bunch of new features in .NET, and I think it all went pretty well. So be sure to check it out if you get your hands on the DVD!

    Looking back, there's a lot of places where I sounded pretty "marketingy" and used terms such as "developer productivity", "very powerful", and just "cool", but talking about the killer features in the upcoming .NET 2.0 platform and the new Visual Studio .NET just made me step back a little and appreciate all the good stuff in there with fresh eyes again.

    While we have gone through a lot of pain with instable alphas and betas, in the end it's been a great experience and I can't wait to see the final version ship!

    Tuesday, April 12, 2005 2:49:55 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    WeFly247 Screenshots#

    Now that the word on WeFly247 is gradually getting out, here is some more teasing with some screenshots of the different applications in a near-finished state. Click on an image to get an enlarged version.

    The Login screen (reused by different Windows applications)

    The Passenger website (ASP.NET 2.0 application)

    Homepage (not logged in):

    City Information:

    Homepage (logged in):

    Preferences:

    Entertainment:

    Meal Service:

    Seat Service:

    Duty-Free Shop:

    Customs & Immigrations InfoPath Form:

    The Pilot Application (ClickOnce Windows Forms application)

    Startup Screen:

    Pre-Flight Check:

    In-Flight Monitoring:

    Problems:

    Problems (Professional Look):

    The Problem Report Word Document (VSTO Smart Document)

    Problem Report:

    Notice the Flight Number Smart Tag (which fills in the flight details) and the custom actions pane showing the open problems.

    Problem Report Research:

    This shows the Research webservice that can be used to diagnose problems in real time from within the Smart Document.

    Blog | Programming | .NET | VS.NET | WeFly247 | Whidbey
    Sunday, April 10, 2005 12:51:09 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual Studio .NET 2005 Beta Experience#

    Tom has already been teasing about the Visual Studio .NET 2005 Beta Experience, and since it's getting more and more traction, I can finally say we're shipping WeFly247 in the EMEA region (Europe, Middle-East, Africa)! It's already been distributed for Beta 1 at VSLive! in San Francisco, but this time we're shipping it inside the box with the upcoming Visual Studio .NET 2005 Beta 2 release all over EMEA.

    If you're new here, WeFly247 is a Microsoft project to showcase the new features of Visual Studio .NET 2005, and I'm very proud to have done most of the development for it. In fact, we've been very busy lately closing down the bits for Beta 2, which, as you can imagine, is pretty tough seeing Beta 2 doesn't exist yet ;-)

    We'll be running a series of webcasts on the project to get you all excited, so come tune in if you can. Here's the information straight from Learn247:

    Learn 247 .NET Learn 247 .NET


    We are pleased to announce that shortly we will be releasing the follow up to the highly successful Football247 and WeRock247 projects titled WeFly247.

    As per our previous projects we will release a multimedia training DVD that will contain over 60 hours of audio recordings and coding labs on Visual Studio 2005 and developer technologies and full source code to the entire WeFly247 suite of applications.

    The WeFly247 scenario focuses on a fictitious airline company and contains everything that a developer needs to know about building applications with Visual Studio 2005 beta 2.

    The applications for WeFly247 scenario are broken down into 3 areas covering all areas of development, including Windows Mobile, VSTO, Smart Client, and InfoPath and much more.

    • Pilot: Pre-flight inspection of the aircraft and in-flight monitoring of key systems.
    • Passenger: Entertainment system for in-flight entertainment.
    • Crew: Management system for managing passenger requests.

    As we get ready for the launch of the DVD and sample code, throughout April we will be running a series of WebCasts to highlight the technologies we have used in the WeFly247 solutions and give developers previews of the solution which we will release in April.


    18 April: 5pm - 6:15pm GMT

    WeFly247 Application Walkthrough and Technologies

    Come and learn about the WeFly247 project, learn about the technologies used to build the solution and the architecture design.

    This session will give you a chance to get an understanding of the entire project and see all the applications working together as well as hear from the developers of the applications that built each application.

    AGENDA:

    • WeFly247 Application Overview
    • WeFly247 System Architecture
    • Building the Pilot applications (Windows Forms, VSTO)
    • Building the Passenger application (ASP.NET 2.0)
    • Building the Crew application (.NET CF)
    • Wrap up / Q&A

    To access the meeting click here. We recommend connecting 30 minutes before the meeting takes place.


    JOINING THE WEBCASTS IS EASY:

    Step 1) Make a reminding in your calendar to remind you of the event (do it now so you don't forget).

    Step 2) Click the link to join the meeting.

    The meeting will take place online, audio will be streamed to your PC.

     © 2005 Learn247.net.     All Rights Reserved.

    Blog | Programming | .NET | VS.NET | WeFly247 | Whidbey
    Sunday, April 10, 2005 12:10:47 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Checking for .NET 1.1 Service Pack 1 in an MSI#

    If you're using a Setup Project to build an MSI installer for your project inside Visual Studio .NET, there are quite some options that allow you to control the installation process. One of the properties you can configure are launch conditions, which allow your installer to check some registry values, installed programs or files to determine if the application can be installed at all.

    Now there are a number of cumbersome methods to determine whether service packs are installed on the .NET Framework, but fortunately service pack discovery has been made much easier in .NET 1.1.

    If your application requires .NET 1.1 with Service Pack 1 to be installed, you can check the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v1.1.4322 registry key for the SP value (which is a DWORD). If its value doesn't exist, you don't have .NET 1.1 installed; otherwise, it contains the installed Service Pack (or 0 if you don't have any service packs).

    Now you can add a Search for the service pack in the installer and store it in a property named "SERVICEPACK":

    In the actual launch condition, you can check that property to make sure the service pack has been installed, and point them to the official .NET 1.1 SP1 download page if that's not the case:

    The tricky part is the Condition, which is set to NOT(SERVICEPACK="") AND NOT(SERVICEPACK>>"0"). Since MSI prefixes DWORD registry values with a # sign in front of them, the SERVICEPACK property will actually contain a "#0" or "#1" string, not the actual service pack number. So we first check if the property actually exists to check for .NET 1.1 (an empty string would be returned if the key didn't exist in the registry) and then we use the special substring operator >> to make sure the property doesn't end with "0". This way, it will work even for service packs later than SP1 (until SP10 arrives of course, but I don't expect that any time soon).

    Now if anybody launches the installer without the required service pack, they will get the following screen prompting them to download it:

    Blog | Programming | .NET | VS.NET | Samples
    Saturday, April 09, 2005 2:08:04 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    DataSets Are Not Evil#

    Ok that's it, everybody stop talking smack about the DataSet. As self-elected chairman of the inexistent "DataSet Preservation Fund", I feel urged to respond to years of incorrect positioning, biased thinking and over-simplified black-and-white views. I won't allow the "enterprise community" to make the DataSet the Clippy of .NET, so to speak.

    All joking aside, I've long been putting off writing about this topic since I wanted to make sure my opinion was balanced enough but David Boschmans pointed me at a recent MSDN article on Custom Entity Classes by Karl Seguin which strikes me as pretty typical so here's my two cents on the matter. I'll take the article as my guide to inject some of my opinions, so it should be helpful to have a quick scan of the article to see what it's all about. By the way, why this article appears in the ASP.NET Developer Center and not in an Enterprise Development Center or the like (think PAG) is unclear to me, since this kind of discussion should target a broader audience.

    Introduction

    First and foremost, as the disclaimer in the introduction states: the article only talks about untyped DataSets, not about strongly-typed DataSets. This is a very unfortunate limitation by the author in my opinion, since Typed DataSets do indeed solve most problems mentioned in the article, as well as provide extra benefits, and this type of simplification will only give more traction to the anti-DataSet camp.

    Alleged Problems

    • Lack Of Abstraction.
      The statement is made (a few times throughout the article actually) that a DataSet makes it impossible to decouple your code from the database structure. While true that you still deal with logical tables, columns and relationships, a Typed DataSet will make this much less visible because you can navigate all of these by just using strongly-typed properties and methods. The argument that changing a column name in the database schema would result in adapting your client code doesn't make sense, you can (and, for this reason, always should) use "AS" in your SQL query to abstract away the actual column name, so it won't "trickle down" to the calling layers. So if the UserId column would actually change to Id for example, the SQL statement would simply become:
      SELECT Id AS UserId, FirstName AS FirstName, LastName AS LastName FROM Users
    • Weakly-typed.
      The obvious statement is made that regular DataSets are untyped, the issues mentioned by the author are of course 100% correct. Hence the invention of Typed DataSets. They will eliminate all the raised concerns. This (along with the next paragraph) also renders the "Why Are They Beneficial" section superfluous.
    • Not object-oriented.
      Sure enough, if you create a Typed DataSet, you get a class that inherits from System.DataSet, which means you can't make it derive from another class anymore. But does this make it "not object-oriented"? This type of problem is often referred to in the Java world as not being a "Plain Old Java Object" (POJO), so in this case I'll refer to it as a "Plain Old .NET Object" (PONO). The problem seems big enough to have Indigo address this by not requiring your components to inherit from any base class (as is the case with Enterprise Services COM+ components, which need to derive from ServicedComponent). If this is not a serious issue for you (it's never been for me), nothing prohibits you from just subclassing that generated DataSet class to add functionality to it without resorting to external utility methods. In the upcoming 2.0 version of .NET, it will even be possible to add functionality to the class without the need for subclassing it by using the partial classes feature.
      The Scott Hanselman quote, taken from his rant against DataSets on service boundaries, makes a good point though: "DataSets are bowls, not fruit". I'm actually cool with Typed DataSets being "bowls with a picture of fruit on them", they're data containers (data transfer objects) after all. Custom entities aren't fruit either, they're another binary representation of the same concept, but they still don't smell like fruit.
    • NULLs.
      A point is made that "dealing with NULLs in DataSets isn't the easiest thing, because every time you pull a value you need to check if it's NULL". While I certainly agree that NULLs can be a pain, you could partially deal with this through annotations on your Typed DataSet, which allow you to define the value returned if the field actually contains a NULL value. Besides, a field having a functional value or not (i.e. being NULL or not) is something that affects all layers, and how you handle this in code will not be very different when using DataSets or custom classes. This is certainly true for value types (while we wait for nullable types anyway, gimme int? now!).

    Other points that make me go "hmmm"...

    • "Never return a class from the System.Data or child namespace from the DAL". If you are convinced that System.Data is the namespace for all server-side database stuff, then you're right. But I don't think that's the case, the provider child namespaces (e.g. System.Data.SqlClient) are server-side. I would allow System.Data.DataSet to pass through customs without a problem here.
    • On performance: "whatever processing time you are able to save probably doesn't amount to much compared to the difference in maintainability". This is really comparing apples and pears, as runtime and designtime have very different characteristics and requirements in a project. It certainly is true that a "blanket statement" is pretty useless here, but I would like to say that although the performance hit and XML overhead on the wire of a DataSet might be larger in absolute figures, you should be communicating with the service backend through chunky calls anyway and the main performance bottleneck will lie beyond the service facade most of the time anyway. But the best performance tweaks can only be done through one optimal path: measuring and interpreting live data.
    • "You can certainly assign nothing/null to [value types], but this will assign a default value. If you just declare an integer, or assign nothing/null to it, the variable will actually hold the value 0." Excuse me? Have you actually tried to run the "int i = null" statement through a C# compiler? This largely diminishes my trust in the author's, eh, authority on the subject of object oriented programming.

    Free Code

    So, what do you get with Typed DataSets? Free code mostly.

    • You can use the boilerplate DataTable.Select method for searching through a table (which is illustrated as "custom behaviour" for the custom entity classes - i.e. code you need to type or generate).
    • The remark that this Select call isn't strongly typed is again correct, but if you define a key on the UserId field in your Typed DataSet for example, you will also get a free typed method called FindByUserId which will select rows based on that key.
    • In ADO.NET 2.0, you'll get the data column names as strongly typed properties so you can use these in your code. The databinding example in the article actually isn't strongly typed itself: the call to DataBinder.Eval(Container.DataItem, "UserName") contains the column name as a plain string, so it would benefit from these properties here as well.
    • If you model relationships between tables, you get free methods and properties to navigate between parent and child tables. You can even use databinding expressions to navigate through these relationships.
    • As stated by the author, "the DataView's built-in support for sorting and filtering, although requiring knowledge of both SQL and the underlying data structure, is a convenience that is somewhat lost with custom collections". So this is basically free code again, and as an extra remark: of course you need to know the data structure, how would you build an application if you knew nothing at all about your data structures? The main point, again, is that you're not supposed to know the database schema here, just the logical data structure. And regarding sorting: if you call "UserName DESC" (which you can use as a sorting expression) knowledge of SQL and think that's too hard to grasp, then you can't really expect much from a developer anyway. There needs to be some way of pouring that sort expression into a language construct. Indeed, it would be better to add strongly typed methods to sort with a sort flag enumeration (Ascending/Descending) and a column name, but it's really not that bad, is it?
    • Data binding is far better supported with DataSets than with any other mechanism in the .NET Framework. Developer productivity is indeed a project feature.

    DataSet Issues

    So it can't all be good, right? There are indeed some issues with the DataSet and I can only hope they are fixed in upcoming versions of the framework. But the main point I'm trying to convey here is that these don't suffice to radically kill DataSets or tell everyone they're all Bad and Evil. Some real problems:

    • They behave bad on the wire when passing over a Web Service or otherwise serialized as XML. This is indeed a problem and a very good reason to avoid them, if (and only if) you need to interoperate with platforms other than the .NET runtime. Don't forget, a lot of times you have both ends under control so this is actually not always an issue.
    • They have a bloated wire format, since the XML representation can become very large very fast. If this is really a problem in your case and causes a measurable performance hit, you could opt for gzip compression over the HTTP transport protocol, or rethink the way you are using the services. The more or less "fixed" overhead will also become smaller in relative figures if the communication with the backend service is chunky in stead of chatty, so that's certainly an important point of attention. And you can consider using the GetChanges method to only include a diffgram of the changes since the DataSet was initially populated - but beware that this is incredibly platform-dependant though.

    Good Points

    Fortunately, the author makes some other very valid points as well.

    • "I realize that code generation sounds like something of a dream. But when properly used and understood, it truly is a powerful arsenal in your bag of tools - even if you aren't doing custom entities". This is very, very true.
    • "Making the switch to custom entities and collections shouldn't be a decision you make lightly". This is even more true. It has a major impact on the way your applications and services will be developed.
    • "Jimmy Nilsson has an overview of some of these alternatives in his 5 part series Choosing Data Containers for .NET (part 1, 2, 3, 4, and 5)". These articles are quite good, indeed. Recommended reading!

    Conclusion

    I think part of the real discussion here is: do we go for a full Object Oriented domain model (most likely with some kind of Object/Relational mapping tool that handles lazy loading, automatic persistence, etc.) or do we build a (Typed) DataSet model where you create different ad-hoc DataSets according to each separate use case (which means there is no unified "one fits all" model). I personally think the full OO model is very hard to maintain in a large corporate domain with many projects and dependencies. Combine this with Service Orientation where it's much harder to "deploy" your domain model to the outside world (since you can only share contract and certainly not type) and I tend to think the latter is more flexible. Either that or you'll have to re-map the service boundary messages to your domain model internally, which causes even more work (although this might be a good approach if that fits your existing internal OO model).

    To conclude, I believe in one thing: choose the right tool for the job. Given two viable options, choose the option that would solve the problem best under the constraints at hand. As Andres Hejlsberg often puts it when comparing performance between C++ and C#: given infinite time and resources, you'll do better in C++. Given infinite time and budget, I might agree that custom entity classes are the best possible solution. Unfortunately, that's not very realistic in real-world projects. Developer productivity and shipping should be considered two very important "features" of your project, and if DataSets help you accomplish these, then I would certainly advise (or at least consider) using them.

    Wednesday, March 23, 2005 11:15:27 PM (Romance Standard Time, UTC+01:00) #    Comments [17]  | 

     

    Indigo March CTP "Hello World" Server & Client#

    Yes! I just got my first Indigo sample up and running!

    I started off with a clean Windows XP SP2 installation, and I just installed the March CTP of Avalon/Indigo/WinFX (build 2.0.50110) without anything else - no Visual Studio .NET, no IIS, no nothing - so it's definitely back to basics here while I'm downloading the February CTP of Visual Studio .NET 2005... On the other hand, it's quite a lot of fun to just try and get anything working without any major productivity booster such as Visual Studio, IntelliSense or any form of actual documentation installed. For now, I'll consider notepad and ildasm.exe my best friends in the Indigo space. Long time no see, welcome back guys :-)

    The first thing to try was to get any of the prepackaged samples working. I unzipped the AllSamples file in the "C:\Program Files\Microsoft SDKs\WinFX\samples" directory and seeing I had no Visual Studio installed I just tried to run MSBuild on the solution files. Although I think this should work, it was complaining to me about not being able to copy some files or something. Weird.

    I then decided to take Clemens Vasters' Indigo Hello World sample and see what I could make of that. I just copied it into a new file and ran the compiler on it:

    csc /r:"C:\Program Files\Microsoft Indigo Preview\System.ServiceModel.dll" IndigoHelloWorld.cs

    Unfortunately, I got an error straight away: the BasicProfileHttpBinding class wasn't found. So here's ildasm to the rescue already. As it turns out, they recently must have renamed the class to BasicProfileBinding, so a simple change to the sourcefile fixes the problem and we're all set and compiled. Step 1 complete! The server code is now just:

    using System;
    using System.ServiceModel;

    namespace IndiHello
    {
      [ServiceContract]
      public class Hello
      {
        [OperationContract]
        public string SayHello(string name)
        {
          return "Hello " + name;
        }
      }

      class Program
      {
        static void Main(string[] args)
        {
          ServiceHost<Hello> host = new ServiceHost<Hello>(new Uri("http://localhost/hello"));
          host.AddEndpoint(typeof(Hello), new BasicProfileBinding(), "ep");
          host.Open();
          Console.WriteLine("Press ENTER to quit");
          Console.ReadLine();
          host.Close();
        }
      }
    }

    So with my newborn Indigo service now up and running on my machine, I figured I should be able to make this do something, right? So because I couldn't really think of anything else, I just took the ASMX approach, which is to surf to the endpoint (http://localhost/hello/ep) and observe the magic: Indigo provides a very similar model to ASMX with an informative html page if you access the endpoint via HTTP/GET and is so kind as to give a hint what you could do next. Not only does it tell you how to generate a client proxy for the service, but it also gives some basic info about how to use the proxy afterwards.

    So off I am building a client proxy using svcutil.exe (think wsdl.exe for Indigo) on the wsdl:

    "C:\Program Files\Microsoft Indigo Preview\svcutil.exe" http://localhost/hello/ep?wsdl

    This just creates a proxy class in a file named tempuri.org.cs (since I haven't changed the default namespace, it just takes the tempuri.org namespace as the base name) with the contract interface and some kind of channel interface (I'm not sure what that does yet but I'll find out sooner or later):

    [System.ServiceModel.ServiceContractAttribute()]
    public interface Hello
    {
      [System.ServiceModel.OperationContractAttribute(Action=http://tempuri.org/Hello/SayHello, ReplyAction="http://tempuri.org/Hello/SayHelloResponse")]
      [return: System.ServiceModel.MessageBodyAttribute(Name="SayHelloResult", Namespace="http://tempuri.org/")]
      string SayHello([System.ServiceModel.MessageBodyAttribute(Namespace="http://tempuri.org/")] string name);
    }

    public interface HelloChannel : Hello, System.ServiceModel.IProxyChannel
    {
    }

    public partial class HelloProxy : System.ServiceModel.ProxyBase<Hello>, Hello
    {
      public HelloProxy()
      {
      }

      public HelloProxy(string configurationName) : 
        base(configurationName)
      {
      }

      public HelloProxy(System.ServiceModel.Binding binding) : 
        base(binding)
      {
      }

      public HelloProxy(System.ServiceModel.EndpointAddress address, System.ServiceModel.Binding binding) : 
        base(address, binding)
      {
      }

      public string SayHello(string name)
      {
        return base.InnerProxy.SayHello(name);
      }
    }

    Looks good. Next up: adding a test driver. Simple enough, the service's informative html page shows me how to create a simple console app that uses the proxy:

    HelloProxy proxy = new HelloProxy();
    string result = proxy.Hello("some data");
    System.Console.WriteLine("The Hello service returned '" + result + "'");
    proxy.Close();

    So I just whipped that into the generated proxy class file, and tried to compile the bunch:

    csc /out:Client.exe /r:"C:\Program Files\Microsoft Indigo Preview\System.ServiceModel.dll" tempuri.org.cs

    Too bad, there seems to be a little bug in the way the service generates the html page (I bet you didn't spot that just now, neither did I), since the service operation isn't called "Hello", it's "SayHello". So a small change here as well, run the compiler again - which seems to give me an actual executable this time - and we're ready to try and run the client:

    C:\IndigoTest>Client.exe
    Unhandled Exception: System.InvalidOperationException: Could not find Channel element for contract Hello, Client, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
       at System.ServiceModel.Design.ConfigLoader.LoadChannelDescription(ChannelDescription channelDescription, ContractDescription contract, String configurationName, EndpointAddress address)
       at System.ServiceModel.Design.ChannelLoader..ctor(Type contractType, String configurationName, EndpointAddress address)
       at System.ServiceModel.ChannelFactory`1..ctor(String configurationName)
       at System.ServiceModel.ProxyBase`1..ctor()
       at Test.Main(String[] args)

    Whoops, that's not good. Here's that Channel thing again. But then again, I didn't really provide an actual uri where the client could locate the service. So I decide to use one of the generated HelloProxy constructors that seems to make sense: the one with an EndpointAddress and Binding.

    A quick look in Ildasm at the EndpointAddress class reveals a constructor that takes a uri string, so I'll just take that and then paste in the same binding as I used on the server side:

    HelloProxy proxy = new HelloProxy(
        new System.ServiceModel.EndpointAddress( "http://localhost/hello/ep" ),
        new System.ServiceModel.BasicProfileBinding() );
    string result = proxy.SayHello("some data");
    System.Console.WriteLine("The Hello service returned '" + result + "'");
    proxy.Close();

    Throwing the compiler at the code creates me a new executable, so I run that and voila:

    C:\IndigoTest>Client.exe
    The Hello service returned 'Hello some data'

    Wicked! My first Indigo client is talking to my first Indigo service over a basic profile HTTP binding, without even having IIS installed. Ain't that something...

    Here are the files if you want to try it out yourself: IndigoHelloWorld.cs (service), tempuri.org.cs (client)

    In retrospect, I could also have used the Indigo CTP SDK docs Don Box pointed at, but, well, it just didn't come to my mind. I was so happy to see ildasm again, I guess I didn't want to trade our new-found relationship for some long-distance relationship with a remote webserver ;-)

    Now that I have this basic stuff out of the way, I can start digging in the articles, posts and documentation for Indigo to get a better hands-on view of the API.

    Sunday, March 20, 2005 3:50:31 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    More on .NET Remoting and customErrors#

    Yesterday, David Boschmans blogged about a problem with .NET Remoting and customErrors we noticed at the customer's site earlier this week. It basically boils down to the fact that the customErrors tag in the <system.web> part of the web.config file is being read by the remoting infrastructure (if you're hosting in IIS anyway) to determine whether or not to flow remote exceptions to the caller. I find this very bizarre, as does David. Ingo Rammer left a little note in the comments about the way Indigo will handle this situation, and I'd simply like to add my opinion to the matter here.

    I think some confusion lies in the fact that .NET Remoting was designed to be as transparent as possible. I think of it this way: exceptions are part of the contract of a class; .NET Remoting is "only" a means to provide location transparency, so the remoting stack should always ensure that the exception is transferred to the client. There shouldn't even be a setting that influences this, as far as I'm concerned. Especially if that setting lives in the <system.web> section, since I expect that section to only affect ASP.NET sites. Up until now, that wasn't a section I would typically associate with the remoting stack.

    The same configuration setting does exist in the <system.runtime.remoting> section as well though, where I can somewhat accept its existance, but certainly not its default value ("remoteOnly") since that can effectively change the behaviour if you scale to multiple servers as David states. That means that if you host your client (e.g. a web app) and server (remoting backend) on the same physical machine, everything will appear to work correctly since the exceptions are transferred locally. If you decide to scale out and move your remoting layer to another server, you'll see an unexpected RemotingException where your own application exception used to be and your app won't work as it used to do (if you're using structured exception handling anyway).

    Now, concerning Ingo's comment... Indigo is different. I would actually expect Indigo to have a means to specify the way server-side exception information is exposed, because Indigo was never meant to be transparent - quite the opposite actually: "Boundaries Are Explicit", remember. And with Indigo, you don't typically "trust" the client, whereas with remoting you tend to have a lot more control over the caller so you would allow more trust about the exceptions being transferred. Anyway, I'm looking forward to seeing the new Indigo model in action, and how the [FaultContract] will interact with the various .NET and non-Microsoft clients out there...

    Blog | Programming | .NET | ASP.NET | Quirks | Samples | Indigo
    Thursday, March 10, 2005 9:24:55 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Batch building NDoc projects#

    If you're not using NAnt or another NDoc-enabled build tool for your build cycle, you might find this little batch file useful:

    @ECHO OFF
    REM Set the full path to the NDoc console executable.
    SET NDOC="C:\Program Files\NDoc 1.3\bin\net\1.1\NDocConsole.exe"
    REM Set the relative path to the output directory.
    SET OUTPATH=doc
    REM Loop over the NDoc project files (*.ndoc).
    FOR %%P IN (*.ndoc) DO (
     ECHO Generating documentation for "%%~nP"...
     ECHO.
     REM Run NDoc for the project file.
     %NDOC% -project=%%P
     REM Copy output files to the current directory.
     MOVE %OUTPATH%\*.chm .
     REM Delete output directory.
     RMDIR %OUTPATH%
     ECHO.
    )

    This will just loop over all .ndoc files in the current path and build the documentation for it into a 'doc' directory. Then it will move the generated .chm Windows help file to the current directory and delete the output directory again so you only have the .chm file left and not the temporary files.

    What I thought was pretty cool is that you can use batch files to loop over a fileset like *.ndoc using the "FOR %%P IN (*.NDOC) DO (...)" syntax. Inside the bracket scope, you have the %%P variable which will be expanded to the current file in the loop, and you can use %%~nP to get just the filename without the extension. There are a lot more of these substitution enhancements, just type "help for" in your favorite command shell.

    For some more batch magic, see Raymond Chen's 90-byte solution to search for a file in your PATH. Nifty!

    Saturday, March 05, 2005 3:27:47 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Branding Guidelines For .NET#

    I came across an internal email the other day that lays down some official branding rules for .NET. Since I'm a bit of a perfectionist in these matters (a "comma-copulator" if you would be so kind as to allow the rather strict translation from Dutch), I'm glad I've been adhering to the first rule pretty much from the beginning and the second for some months now, but it hurts my eyes to see Microsoft itself and other people use the .NET term incorrectly so thought I'd share these with you:

    • .NET is always in all caps. Never .net or .Net.
    • With two exceptions (ASP.NET and ADO.NET) there is always a space between anything and .NET. For example, Visual Studio .NET, not Visual Studio.NET.
    • Whenever possible, do not refer to ".NET applications" because it is imprecise and potentially confusing. That could mean one of two things: an application that "connects" via XML Web services and/or an application built on the Microsoft .NET Framework. It's better to be specific. In first mention, refer to applications built on the .NET Framework as ".NET Framework-based applications." At second mention it is ok to refer to these apps as "Framework applications." When referring to applications that connect using Web services protocols, use "Microsoft .NET-connected".

    That last rule is rather fuzzy and "marketingy" but if you're craving for more, this page on Microsoft.com provides a good overview of the guidelines: http://www.microsoft.com/mscorp/ip/trademarks/netguide.asp

    Saturday, March 05, 2005 2:58:36 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    TechEd Europe 2005#

    Hans Verbeeck, overall technical content owner of TechEd Europe 2005, has started planning what's on the agenda for this year.

    TechEd focuses on hot (nearly) available technologies so with the conference taking place in the beginning of July, that's going to be

    • Beta 2 of the .NET Framework 2.0 and Visual Studio .NET 2005 for developers,
    • Beta 3 of SQL Server 2005 for DBA's (I didn't even know there was going to be a third beta, I thought they were actually pretty close to shipping already),
    • SOA, software factories, domain specific languages for architects and
    • "Special tracks" (hmm that's pretty vague ;-) ) for IT Pro's.

    Not on the shortlist:

    • Indigo and Avalon, although there's a bundled CTP of both coming up in March (but it doesn't fit the "relevant within 6 months" criterium),
    • Windows Longhorn (if anybody still knows what's up with that anyway since it ran out of pillars to be based upon, but that's probably just my confusion, right?)

    Cool stuff, so if you dig this kind of content (along with labs, ask-the-experts, panels, Amsterdam's finest herbs, and - let's not forget - wicked parties) don't forget to add 4-8 July to your agenda!

    Friday, February 11, 2005 9:49:49 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Enterprise Library Lightning Talk#

    If you're coming to the DevDays (err "Developer & IT Pro Days", sorry, force of habit) in Belgium this week, don't miss the Community Evening on Tuesday!

    One of the things happening there is the Lightning Talks Session, where different speakers can spend a whopping 5 minutes on a Microsoft related subject. Thanks Bernard Vander Beken for organizing these!

    I'll be giving a quick intro to the Enterprise Library (entitled "Citations from the Enterprise Library") that was just released, so if you're there, come say hi!

    Blog | Programming | .NET | EntLib
    Monday, January 31, 2005 12:47:45 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ASP.NET Comments#

    I've just expressed my love for comments (which generated some interesting discussion as well) and a while ago I've been quite active in ASP.NET 2.0; so let me take the opportunity to raise a personal concern of mine when combining the two.

    Remember that one goal of ASP.NET 2.0 was to reduce the code needed for typical scenarios by 70%. That's a lot and I don't have any metrics yet, but they've certainly managed to eliminate a lot of keypresses that I hope I will never need to break my fingers on again. They've also overhauled the databinding system (just look at the ObjectDataSource) to reduce code and you can even use the Web.config file to declaratively create a strongly-typed Profile, or couple all your pages to a common basepage to include boilerplate code.

    So great! With all this, ASPX has definitely become a double-JITted programming language of its own - as Early & Adopter so beautifully put it during the last PDC.

    The only problem I have with it, is that when hiding more and more code behind markup, where do we document what we're doing? We don't. Do you? Thought so... So come on, be nice: comment your markup when it gets anywhere near semi-complex.

    And don't just write <!-- html comments --> either, but be sure to use the <%-- server-side comments --%> in ASP.NET, since they don't get sent out to the browser (which would increase your final page size and possibly even leak information about your business logic to the browser for everyone to see).

    Still, this doesn't do anything to promote itself as metadata (that can be used by tools) like the well-known XML comments, so it's still fairly useless apart from serving as in-line documentation. But then again, if you're writing structured code that needs structured comments, you should really put it in code anyway, and not in markup.

    Blog | Programming | .NET | ASP.NET | Whidbey
    Monday, January 24, 2005 8:04:35 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Upgraded to dasBlog 1.7!#

    I couldn't wait to upgrade to the new 1.7 version of dasBlog Community Edition (as it's called nowadays) because of all the great new features: far better performance, anti-spam measures, drafts (finally!), and lots more. Three cheers to Omar Shahine, Scott Hanselman, and everybody else who dedicated some precious time to it!

    Unfortunately, there seems to be a small bug in the drafts functionality, since posts that aren't marked as Public (i.e. drafts) still show up in the RSS feed. Whoops ;-) I'll be keeping an eye on the bug I submitted to the SourceForge tracker because I really want to start using drafts... Update: My eyes must be deceiving me, the draft version of this post worked perfectly without showing up in the RSS feed... It did fail on my test machine though... Anyway, I'm sure it was my fault then.

    If you're planning on upgrading too, you'll notice that you have to run an upgrade tool against your contents directory. The fix for the "Comments may disappear when moving content across GMT timezone boundary" bug I briefly mentioned before and submitted to Omar Shahine may have something to do with that since I believe that triggered the filename changes, sorry about that ;-)

    Still, I had a little problem upgrading my content, and I believe it's related to that bug. I pulled the content from my server (which is on time zone GMT-7) to my machine (GMT+2) and ran the upgrade tool. Everything seemed fine on my test machine, but I noticed that the comments on some (what appeared to be) random posts had disappeared. I dug a little deeper and noticed these posts were submitted quite early in the night so I suspected a time zone issue again. I reflectored (uhm, yes, that's a word) the upgrader exe to see what it was doing and I noticed it was using the local time zone for some date-time calculations. So on a hunch I switched my local timezone to the one of my server and everything worked just fine. Whew :-)

    So here's a word of advice: if you upgrade your content directory, be sure to temporarily set your local machine to the same time zone as the server the blog will be running on!

    Blog | General | Programming | .NET | ASP.NET
    Sunday, January 23, 2005 4:25:38 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    The Corporate Blogging Question#

    I've hardly been home at all this week so David scooped me: I've just started working as a .NET consultant and technical coach for the Belgian Compuware .NET team today! Thanks for the warm welcome, David, and a big thanks to the rest of the Compuware team as well! I'm looking forward to working with you all! As it goes, I already have one anecdote I'd like to share with the blogging community here.

    As it happens, Irene Dawson, the Senior Vice President of Compuware EMEA, was at the Belgian office today for a visit and a talk. There was room for some questions at the end of her talk and there was a tricky one in there: "what's the corporate policy towards blogging?". Good one, I might add. Who knows, maybe I'm already violating it here ;-)

    So forget what you know from your "little" inner circle here on the big semi-organised chaos that we call the "internet", but blogging still isn't known to everybody out there in real life :-) So as she was trying to assimilate the meaning of this new word, my new manager pointed her at me on my first day to quickly explain blogging to her on the spot. I think I managed to get some of its intricacies into her mind but a little preparation would've been nice though ;-) So I'll be explaining her the meaning and issues some more in detail through email; any definitions and precedents to help set the stage are welcome. Robert Scoble's Corporate Weblog Manifesto is already on the list but I'd like some more diverse viewpoints actually...

    Anyway, I'd heard a lot of nice things about Irene so it was great to meet her at last (she's a very warm and vibrant person) and she'll probably remember me as "the blogger" or something, but it was fun nevertheless :-) I guess it's one way to meet your Senior VP on your first day...

    Thursday, January 20, 2005 1:16:53 AM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    XML Comments Quick Reference#

    I'll come out and bluntly admit it: I don't mind writing XML comments in my code. Really. I can get pretty freaky about it too if you let me, and I always try to make sure that every comment is kept in sync with the code it's covering. Why? Because if you make the compiler (or a 3rd party tool for those languages that don't support it) emit an XML comments file, it gives you free and up-to-date IntelliSense and even full-blown technical documentation, duh!

    So if you're also constantly lost in the MSDN documentation trying to figure out which tags are allowed and what the syntax is, here's a quick reference for your amusement. Now start writing comments, please!

    Documentation Sections

    • <summary>

      A short description of the item; describe the members of the type here.

      <summary>text</summary>

    • <remarks>

      The "Remarks" section of the documentation; specify overview information here.

      <remarks>text</remarks>

    • <example>

      The "Example" section of the documentation.

      <example>text</example>

    • <seealso>

      A link in the "See Also" section of the documentation.

      <seealso cref="member">text</seealso>

    Member Documentation

    • <param>

      Describes a parameter to a method.

      <param name="name">text</param>

    • <returns>

      The return value of a method.

      <returns>text</returns>

    • <exception>

      The exceptions that can be thrown.

      <exception [cref="type"]>text</exception>

    • <value>

      Describes the value of a property.

      <value>text</value>

    • <permission>

      The permission applied to a member.

      <permission [cref="type"]>text</permission>

    Text Formatting

    • <c>

      Format characters as code within other text.

      <c>code</c>

    • <code>

      Multiline section of code.

      <code>code</code>

    • <see cref>

      A link to a type, member or field in the current compilation environment.

      <see cref="member">text</see>

    • <see langword>

      Undocumented. A link to a keyword in the language of the current compilation environment, e.g. null.

      <see langword="keyword"/>

    • <paramref>

      A reference to a parameter within other text.

      <paramref name="name"/>

    • <list>

      A list of items.

      <list type="[bullet|number|table]">
          <listheader>
              <term>name</term>
              <description>text</description>
          </listheader>
          <item>
              <term>name</term>
              <description>text</description>
          </item>
      </list>

    • <para>

      A paragraph within other text.

      <para>text</para>

    • <include>

      Refer to comments in another file.

      <include file='filename' path='tagpath[@name="id"]' />
    Blog | Programming | .NET | VS.NET
    Sunday, January 16, 2005 11:00:38 PM (Romance Standard Time, UTC+01:00) #    Comments [14]  | 

     

    Binding the child rows of a DataRow#

    Either I'm missing something or the ADO.NET team missed a fairly simple scenario... Imagine you have a single DataRow out of a table with a relation to another table. Now you want to bind a DataGrid (e.g. WinForms but that doesn't really matter here) to the child rows of that DataRow. Sounds easy, right?

    And sure, there's a handy GetChildRows method that returns an array of child rows for a given relation. Unfortunately, binding a grid to an array will make it display the properties of the DataRow object, not the actual content of the DataRow.

    So you need some way to get something bindable out of that parent row. You might create a new DataTable with the same structure and load it with the child rows but that's pretty cumbersome.

    If you have a DataRowView, then you can get a bindable DataView out of it using the CreateChildView method. Although you can't lookup or create a DataRowView directly, you can get it by looping over the DefaultView of the associated table. This is how it could roughly look in code:

    public DataView GetChildView( DataRow parentRow, DataRelation relation )
    {
        // Find the associated DataRowView of the parent data row.
        foreach( DataRowView rowView in parentRow.Table.DefaultView )
        {
            if( rowView.Row == parentRow )
            {
                // Create a child view through the DataRowView and the relation.
                return rowView.CreateChildView( relation );
            }
        }
        
        // No associated DataRowView found.
        return null;
    }

    Now the resulting DataView can easily be bound to a datagrid.

    Friday, January 14, 2005 6:13:38 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Tiny but great DataSet change in Whidbey#

    There's a quite lengthy post from the VB team about the data design time changes between Whidbey beta 1 and beta 2 (via 3Leaf Development).

    In there, I found a little gem that will solve one particular annoyance I have with the current Typed DataSet generator: while the columns are generated as strongly typed objects, they're declared as internal so they're no use outside the declaring assembly. Hence the column names in quotes in my previous posts about DataSets and Web Services:

    testData.Employee.Columns["ID"].ColumnMapping = MappingType.Hidden;
    testData.Employee.Columns["CompanyID"].ColumnMapping = MappingType.Hidden;

    This will fortunately change in Whidbey, allowing more strongly-typed and thus safer code:

    testData.Employee.IDColumn.ColumnMapping = MappingType.Hidden;
    testData.Employee.CompanyIDColumn.ColumnMapping = MappingType.Hidden;

    I had planned a rant on that but it seems I've been pre-empted by the team that figured it out on their own :-) At least, if the C# team is also doing this but I can hardly imagine that they wouldn't follow the pattern here. It's the small things that can really make a difference you know...

    Blog | Programming | .NET | VS.NET | Whidbey | Samples
    Thursday, January 13, 2005 10:08:58 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Pushing modified DataSets through Web Services#

    There seems to be a bug in the .NET Soap serialization stack when passing a DataSet with an expression column in it that uses a relation. Allow me to elaborate :-) But don't run off just yet if you don't care about the serialization stack, there's some useful stuff in there as well ;-)

    Imagine a typed DataSet "TestDataSet" that you're passing from your Service layer to your client UI layer through an ASP.NET Web Service. Say you have a table Employee and a table Company in it, and you want to show the employees in a DataGrid (e.g. WinForms but that doesn't really matter here) with the name of the company in a separate column. The service backend doesn't care about your needs to display the company name inline with the rest of the data, so all you have is the relation CompanyEmployee between the two tables.

    For display purposes, you can add a column to the Employee table with its Expression set to "Parent(CompanyEmployee).Name" and it will display just fine: it calculates the new column by navigating to the parent row through the CompanyEmployee relationship and then takes the Name column from that row. Bingo!

    TestDataSet testData = TestServiceAgent.GetTestData();
    testData.Employee.Columns["ID"].ColumnMapping = MappingType.Hidden;
    testData.Employee.Columns["CompanyID"].ColumnMapping = MappingType.Hidden;
    testData.Employee.Columns.Add( "CompanyName", typeof( string ), "Parent(CompanyEmployee).Name" );
    employeeGrid.DataSource = testData.Employee;

    Extra advantages: this value is automatically updated if the parent changes and since it's a calculated column, you can't edit the company name in the grid. Also note that you can hide columns by setting their mapping type to Hidden. That's just a simple trick you can use on the client side.

    The problem arises when you use this modified DataSet to update your entity in the backend again: you probably have some SaveTestData method on your service that accepts a TestDataSet to be saved and you just want to pass the changes of the modified DataSet along to it.

    TestServiceAgent.SaveTestData( testData.GetChanges() );

    Too bad, it will blow up in your face:

    Exception: System.Web.Services.Protocols.SoapException: Server was unable to read request. --->
    System.InvalidOperationException: There is an error in XML document (1, 12311). --->
    System.NullReferenceException: Object reference not set to an instance of an object.
       at System.Data.LookupNode.Bind(DataTable table, ArrayList list)
       at System.Data.DataExpression.Bind(DataTable table)
       at System.Data.DataExpression..ctor(String expression, DataTable table, Type type)
       at System.Data.DataColumn.set_Expression(String value)
       at System.Data.Merger.MergeSchema(DataTable table)
       at System.Data.Merger.MergeTableData(DataTable src)
       at System.Data.Merger.MergeDataSet(DataSet source)
       at System.Data.DataSet.Merge(DataSet dataSet, Boolean preserveChanges, MissingSchemaAction missingSchemaAction)
       at TestProject.TestDataSet.ReadXmlSerializable(XmlReader reader) in C:\TestProject\TestDataSet.cs:line 166
       at System.Data.DataSet.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader)
       at System.Xml.Serialization.XmlSerializationReader.ReadSerializable(IXmlSerializable serializable)
       at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read14_SaveTestData()
       --- End of inner exception stack trace ---
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
       at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
       --- End of inner exception stack trace ---
       at System.Web.Services.Protocols.SoapServerProtocol.ReadParameters()
       at System.Web.Services.Protocols.WebServiceHandler.Invoke()
       at System.Web.Services.Protocols.WebServiceHandler.CoreProcessRequest()
       at System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage message,
    WebResponse response, Stream responseStream, Boolean asyncCall)
       at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
       at TestProject.TestServiceProxy.SaveTestData(TestDataSet testData) in c:\testproject\testserviceagent.cs:line 291
       at TestProject.TestServiceAgent.SaveTestData(TestDataSet testData) in c:\testproject\testserviceagent.cs:line 162

    As you can see, the Soap/XML deserialization stack has some problems with the expression column. And this isn't just because the parent row could be missing in case you only sent the changed rows (which you should do using the GetChanges() method on the DataSet to get the diffgram): it happens even if you send the full DataSet back. If there's an expression column that doesn't use a relation, then there's no problem.

    There's a post on .NET 247 called "Serialize DataSet with ExpressionColumn" that shows somebody else running into this issue, and if you kept your foreign languages polished you can also check it out on the Russian GotDotNet site. But that's all I found on it so far, and none offer an explanation or solution.

    A possible workaround would be to remove all expression columns again before submitting the DataSet back to the Service layer, but that's pretty harsh, not too transparent, and it can mess up your UI if you're still using the DataSet on-screen.

    What I consider to be much better, is to create a new instance of the typed DataSet and merge the changes of the DataSet you're working with into it - making sure you ignore anything not in the DataSet's schema by using MissingSchemaAction.Ignore. This removes all the added clutter you don't need on the backend anyway and it makes sure you get a clean and valid copy of your typed DataSet out of the door.

    TestDataSet changes = new TestDataSet();
    changes.Merge( testData.GetChanges(), true, MissingSchemaAction.Ignore );
    TestServiceAgent.SaveTestData( changes );

    This way, everything works and the universe is happy again.

    And if you're worried about the cost of the extra DataSet you just created: it's on the client side so I'd consider the performance hit negligible as long as you don't constantly send this type of changes to the backend. Then again, if you are constantly doing this, then there's probably something wrong with the way you're using your service :-p

    Wednesday, January 12, 2005 2:31:23 PM (Romance Standard Time, UTC+01:00) #    Comments [5]  | 

     

    Fixing the IExtenderProvider in ASP.NET#

    Very, very cool: Fixing the IExtenderProvider in Visual Studio's ASP.NET designer.

    The fact that Extenders Providers don't work in ASP.NET has been bothering me for a very long time, and this article shows a way how to fix that once and for all. And all it needs is an extra attribute on your IExtenderProvider class. Sweet!

    In case you're wondering: extenders aren't supported in ASP.NET because the team didn't really see a scenario for it. Mainly because they're mostly used to hook into control events and that just doesn't really happen as much on ASP.NET like in WinForms.

    But you could still do useful things with it (translation and data binding jump to mind) so I was really bummed about that. I talked to Scott Guthrie about it on the PDC03, explained a few reasons why I'd use them and he agreed I had a good point but it was unlikely that they would actually implement it.

    Blog | Programming | .NET | ASP.NET | VS.NET | PDC03
    Tuesday, January 11, 2005 12:58:40 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Avoiding WebSite_1 when pulling an ASP.NET Site from SourceSafe#

    Today was frustration day. I got all cooked up a few times and got mad at

    • my next-door neighbour (for playing loud music at 2:30 AM)
    • some printer (for paperjamming 66% of the time)
    • Visio (for generally sucking; well either that or I'm just too much of an idiot to understand how to properly draw a UML sequence diagram in there)
    • aspnet_wp (for not finding some system dll's in its own runtime cache until I nuke the process)
    • SourceSafe (see Visio; and for the reason below)
    • Visual Studio .NET 2003 (for pestering me with the "Object type cannot be converted to target Type" CopyLocal bug again)

    Luckily, I also picked up what could be the trick to avoid an all-too-common SourceSafe mess.

    If you're getting a new solution from SourceSafe that includes an ASP.NET Web Site or Web Service you probably encountered this as well: chances are that you don't keep your sources under IIS's wwwroot but under your local SourceSafe working directory and then create VRoots (Virtual Roots) in IIS to the proper directory. If you did that before you opened the solution in Visual Studio .NET for the first time, you're doomed with a feeble attempt of SourceSafe to create the web for you, again. It notices that there's already a web with that name, creates a new one with the same name and a "_1" suffix and then complains that it can't bind to the proper web site. Here's a trick that worked for us:

    • Create the directory but keep it empty (don't get a local copy out of SourceSafe)
    • Create the VRoot to that directory with the proper name
    • Make sure Anonymous Access is enabled on the VRoot
    • Open the solution in Visual Studio .NET, SourceSafe should be getting the files in the right directory now
    • Disable Anonymous Access on the VRoot if required

    If you have other or better solutions, don't hold back and share them :-) I've seen a trick via David to avoid some other problems by making SourceSafe treat a web project as a class library, but I think the friction is pretty high on that one.

    And I'll still be holding my breath for the source control system included with Visual Studio 2005 Team System of course. That, and the fact that Visual Studio .NET 2005 doesn't need IIS anymore, or creates web sites under wwwroot by default. Shoosh, now be gone SourceSafe.

    Monday, January 10, 2005 10:09:55 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    3rd Belgian Geek Dinner: 21 January 2005 @ Brussels#

    Whaddayamean, you haven't let Roy know that you're coming to the 3rd Belgian Geek Dinner yet? Well what are you waiting for!

    This time, it's going to be in Brussels so no more excuses about the heavily feared construction site that is Antwerp! It looks like there's gonna be a fun gang so just come over, have a great Mongolian Barbeque and hang out with your fellow geeks!

    Hope to see you there!

    Thursday, January 06, 2005 5:14:25 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    PDC 2005!#

    Yes! It seems there'll be a PDC again in 2005 (via Scoble)! The new PDC05 site is already up although there's not a lot of content yet (meaning there's no content yet, apart from the date (and the fact that it's announced of course (which is most important))). In fact the news is so hot they didn't even have the time to update the PDC03 logo yet ;-)

    Oh, I so wanna go again, last year's PDC was a blast... I'm already marking that week as busy in my agenda, wink wink :-)

    Since Whidbey/VS2005 should be old news by then, I'm assuming this one will be all about Indigo and Avalon, and of course Longhorn (or what's left of it anyway), and they'll probably have some yet unrevealed tricks up their sleave as well. Bring it on!

    Blog | Programming | .NET | Whidbey | PDC03 | Longhorn | PDC05
    Wednesday, December 08, 2004 10:05:49 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Poor man's generics in .NET 1.x#

    I hadn't seen this little .NET 1.x trick before yet, so I thought I'd share it with you: in case you're really hurting from the lack of generics and you can't wait until sometime next year when VS2005 comes out, here you go... Very poor man's unflexible compile-time generics:

    using T = MyItem; // It's magic!

    public class PoorMansGenerics
    {
        public static T[] CreateArray(int size)
        {
            return new T[size]; // Incredible! Just like real generics!
        }
    }

    Yeah I know it's lame and it does only 2% of what real generics do, but it can save you some typing and I thought it was pretty funny so whatever :-) For example, for a copy/paste ready solution of strongly typed collections: just replace the MyItem stuff below with whatever you want.

    using T = MyItem;

    /// <summary>A collection of <see cref="MyItem"/> objects.</summary>
    public class MyItemCollection : CollectionBase
    {
        /// <summary>
        /// Creates a new <see cref="MyItemCollection"/>
        /// </summary>
        public MyItemCollection()
        {
        }
        
        /// <summary>
        /// Adds the given item.
        /// </summary>
        /// <param name="item">The item to add.</param>
        /// <returns>The position in the list where the item was added.</returns>
        public int Add(T item)
        {
            return List.Add(item);
        }

        /// <summary>
        /// Adds the given items.
        /// </summary>
        /// <param name="items">The items to add.</param>
        public void AddRange(T[] items)
        {
            for (int i = 0; i < items.Length; i++)
            {
                List.Add(items[i]);
            }
        }

        /// <summary>
        /// Inserts a given item.
        /// </summary>
        /// <param name="index">The position where to insert the item.</param>
        /// <param name="item">The item to insert.</param>
        public void Insert(int index, T item)
        {
            List.Insert(index, item);
        }

        /// <summary>
        /// Removes a given item.
        /// </summary>
        /// <param name="item">The item to remove.</param>
        public void Remove(T item)
        {
            List.Remove(item);
        }

        /// <summary>
        /// Determines whether this collection contains the given item.
        /// </summary>
        /// <param name="item">The item to search.</param>
        /// <returns><c>true</c> if this collection contains the given item, <c>false</c> otherwise.</returns>
        public bool Contains(T item)
        {
            return List.Contains(item);
        }

        /// <summary>
        /// Gets the index of the given item in the collection.
        /// </summary>
        /// <param name="item">The item for which to retrieve the index.</param>
        /// <returns>The index of the given item in the collection.</returns>
        public int IndexOf(T item)
        {
            return List.IndexOf(item);
        }

        /// <summary>
        /// Copies the elements in this collection to the given array.
        /// </summary>
        /// <param name="array">The array to which to copy the items.</param>
        /// <param name="index">The index at which to start copying.</param>
        public void CopyTo(T[] array, int index)
        {
            List.CopyTo(array, index);
        }

        /// <summary>
        /// Gets or sets the item at a given index.
        /// </summary>
        public T this[int index]
        {
            get
            {
                return (T)List[index];
            }
            set
            {
                List[index] = value;
            }
        }
    }

    Blog | Programming | .NET | VS.NET | Samples
    Monday, November 29, 2004 3:48:21 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    SmartPart for SharePoint v1.0 released#

    Jan Tielens - SharePoint Connoisseur Extraordinaire - just released v1.0 of his SmartPart for SharePoint.

    "SmartPart: A SharePoint Webpart that can host any ASP.NET user control. Create your webparts by using the VS.NET designer instead of coding everything by hand!"

    If you're into Webpart development, you're going to wanna kiss him ;-) Way cool!

    Wednesday, November 24, 2004 12:40:07 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Guidelines - a hidden feature for the Visual Studio Editor#

    Sara Ford (young, cool, smart, goodlooking techie - damn I'm in the wrong country) just revealed a hidden registry setting to enable guidelines in Visual Studio (via Scott Swigart).

    Guidelines are just vertical lines that run along a certain column in your text editor. This will of course prove a major productivity gain for COBOL.NET programmers since they're finally able to visually show lines along columns 8 and 12 :-)

    Blog | Programming | .NET | VS.NET
    Wednesday, November 24, 2004 12:31:15 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    InfoPathHelper: add offline support to InfoPath!#

    If you heard Yves and me talk about InfoPath and SharePoint on the TechNet session a while ago, you'll remember my promise that I'd get my InfoPath helper class I showed there out on the web sometime. Well, I finally got around to building it as v1.0 and wrapping it up nicely so here goes... A big thanks to Ned Friend from Microsoft for the original idea I based this on!

    InfoPathHelper is a small reusable .NET library you can use to add offline support to InfoPath forms. That's right, you're finally able to make InfoPath a real smart client that is offline capable of submitting forms and querying data sources, without any extra security requirements whatsoever.

    For the end user, the form will appear to continue working when the machine is offline or when the target you're submitting your form to is unavailable (a Web Service, a Sharepoint site, ...). The next time the machine goes online, any pending requests will be submitted. The same goes for querying data sources (files on a network share, Web Services, ...): if they're not available, the data will be taken from the local cache until the next time it can be refreshed.

    For a developer using the InfoPath 2003 Toolkit for Visual Studio .NET, it boils down to inheriting from the CachingInfoPathForm base class, changing your submittal options to use form code, deleting the messy generated boilerplate code (you get cleaner properties and events for free in stead) and calling a few methods depending on your needs. The following example shows how to enable offline support for a form that has a submit data adapter named "WebServiceSubmit" and a data object named "Countries".

    public class MyCachingForm : CachingInfoPathForm
    {
        [InfoPathEventHandler(EventType=InfoPathEventType.OnSubmitRequest)]
        public void OnSubmitRequest(DocReturnEvent e)
        {
            // Attempt to submit the request and return a value to indicate if it worked.
            e.ReturnStatus = ProcessSubmitRequest( "WebServiceSubmit" );
        }

        [InfoPathEventHandler(EventType=InfoPathEventType.OnLoad)]
        public void OnLoad(DocReturnEvent e)
        {
            // Attempt to submit any cached requests and query a data object.
            SubmitCachedRequests( "WebServiceSubmit" );
            QueryDataObject( "Countries" );
        }
    }

    If you're wondering where the cached data is stored without needing any extra security requirements: welcome to the magical world of Isolated Storage :-)

    Although I'm labelling it v1.0, I know there will still be some issues with it. I know of one particular problem I haven't been able to solve (but I suspect it's an InfoPath problem actually): if you populate a drop down list with data from a Web Service and cache that, a COM exception is shown when populating it from the cache saying "This DOM cannot be loaded twice". Any help solving this would be greatly appreciated :-)

    Anyway, you're free to use this (as the enclosed license states, usual disclaimers apply) but it would be nice if you'd let me know if you're using it somewhere or if you're trying it out and think it's crap. All feedback is welcome! If there's enough interest in this little project, I might even write an article of some sort to explain the workings and usage a little more in depth than the current documentation file.

    Enough talk, here's the goodies:

    The JelleDruyts.InfoPathHelper 1.0 library and documentation (123,15 KB)
    The JelleDruyts.InfoPathHelper 1.0 library, source code and documentation (218,74 KB)

    Monday, November 22, 2004 10:07:41 PM (Romance Standard Time, UTC+01:00) #    Comments [8]  | 

     

    New Design!#

    Okay so the last time I redesigned this site wasn't that long ago yet but I guess all the graphics and fancy XP-like stuff was a little over the top... If you can actually see your page being rendered before your eyes then there's either something wrong with your site or the server is really slow. In my case I'd assume it was a little bit of both actually ;-)

    So anyway: I decided to create a new design, much simpler and easier on the eyes this time. Or at least, so I hope. So please tell me what you think!

    (If you're reading this through an aggregator of some kind: don't. Just for once, don't. Open that browser thingy once again if you haven't uninstalled it yet and actually surf to my blog. It'll bring back that special ninetees feeling!)

    Monday, November 01, 2004 10:32:01 PM (Romance Standard Time, UTC+01:00) #    Comments [13]  | 

     

    Register an assembly in COM+ from Visual Studio .NET#

    You know when you have an assembly with serviced components that needs to be registered in COM+, you have to open up a .NET Command Prompt, navigate to the proper directory and call Regsvcs.exe with the name of the dll you want to register?

    I just thought of a really simple trick that handles this with two clicks from within Visual Studio .NET (everybody else has probably already figured this out but somehow forgotten to tell me).

    Just add an external tool - I cunningly called it "Register Output Assembly In COM+" - with the command pointing to Regsvcs.exe (e.g. "C:\WINNT\Microsoft.NET\Framework\v1.1.4322\RegSvcs.exe") and with "$(TargetPath)" as its arguments.

    Now when you run the external tool, it will just register the output assembly of your current project in the COM+ catalog. I also made it use the output window so I see the results right there in Visual Studio .NET as well.

    Blog | Programming | .NET | VS.NET
    Tuesday, October 19, 2004 2:16:33 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Edit & Continue in C#... Why?#

    What's all this fuss about Edit & Continue making it into C#?

    I've never really missed it much, and I believe it makes you think harder before you code if you don't have the 'luxury' of coding-as-you-go. I do believe it makes sense in a few scenarios - e.g. when you have a reaaally big project and you don't want to have to recompile for a simple 'i <= length - 1' mistake (but then again, do you really need your project to be that big?) - but in the majority of cases, I just don't see the need.

    Thoughts? Prove me wrong!

    Blog | Programming | .NET | VS.NET | Whidbey
    Sunday, October 17, 2004 8:56:13 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    TechNet evening session on InfoPath & SharePoint#

    As it happens, I'll be co-hosting a TechNet Evening session on "Customizing Collaborative Solutions with InfoPath 2003 SP1 and Windows SharePoint Services" with Yves Kerwyn on October 27. Woohoo, that should be fun :-) So if you're interested in InfoPath, XML, Web Services and SharePoint, be sure to register for the session and come say hi!

    There should be lots of demos and I fully intend to make sure Murphy's Law is upheld... So there's a big chance you get to point and laugh at me in public - don't miss this unique opportunity!

    Sunday, October 10, 2004 3:51:45 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Belgian Geek Dinner, 2nd Edition#

    I guess Roy Dictus righteously got too impatient for me to organise it (I'm sorry, I'm just too busy these days), so he's calling everyone in for the Second Edition of the Belgian Geek Dinner! So mark the 12th 15th of October in your calendars and tell him you're coming!

    I won't be able to make it before 10 o'clock but I'm counting on you all to make the party last long enough so I can join in a little later :-) So see you there!

    I'm just abusing the occasion here to show off my banner again ;-)

    Update: the geek dinner was moved to the 15th of October, unfortunately I won't be able to make it at all that day. So see you a next time!

    Friday, October 01, 2004 3:50:38 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Enhanced feature request about icon sets in Visual Studio 2005#

    I've seen on Tom Mertens' blog that Robert McLaws is asking to support the number 2 feature suggestion for Visual Studio 2005: an updated icon set that ships with it. I heartily agree. What's more: I'll top the suggestion and expand it a little.

    We don't need just an updated icon set to ship with Visual Studio. If Windows Forms 2.0 is the final stage in Windows Forms development and Microsoft wants people to ship products with a compelling "User Experience" on the road to Longhorn, then they better start providing the community with the proper tools to do so. Icons are in important part of the user experience (platform-wide consistency is an important design principle), so the most important step here is indeed to make the common user interface elements obiquitously available.

    In my mind, that's not just shipping the icon files with the development environment. It's also publishing those icons on a searchable resource-like website where you can just download the icon you need without having to search the entire web and skim out the ridiculously outdated or overly charging icon-library websites. Microsoft must already have some central library of these icons to be shared by the product teams so why not share it with the world?

    The next step is to go all .NET'y and package a large number of common icons in strongly signed .NET resource assemblies (dll's). These could be distributed with the runtime and placed in the Global Assembly Cache (GAC) so they're readily available to all your .NET programs. That gives you the advantage of being able to update all your icons to the latest (fanciest) version with just a configuration change and it follows the same idea as reusable dll's in the first place: why embed all those common elements in each and every program when you can share one version between multiple programs?

    Anyway, an update to the icon set would be a requirement in my mind. The rest would be a nice-to-have. But I really don't want to start searching the web for a decent looking Save button ever again.

    Blog | Programming | .NET | VS.NET | Whidbey | WinForms | Windows | Longhorn
    Monday, September 27, 2004 1:26:11 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Update to my dasBlog hack for showing public referrers and search engine queries#

    A while ago, I hacked dasBlog so I could browse my public referrers. Afterwards, I moved the google and feedster queries to a separate list so they don't clutter the 'real' referrers. Now, I made the code more generic so I could easily add some more search engines like Altavista, Yahoo! Search and MSN Search. So I figured I'd post the code here since I like to think it's pretty cool to have :-) So if you're running dasBlog, just copy the files in the zipfile into your root blog folder and enjoy your new PublicReferrers page!

    PublicReferrersAndSearchQueries.zip (5,98 KB)
    Blog | General | Programming | .NET | ASP.NET
    Sunday, September 26, 2004 11:10:20 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Launching the default mail client in .NET#

    If you want to launch the default mail client to create a new email message, it's really simple and there's plenty of samples available:

    System.Diagnostics.Process.Start( "mailto:user@example.com?subject=TestSubject&body=TestBody" );

    But what if you just want to launch the client and nothing more? You'll have to read the registry (remember that you need the proper RegistryPermission for this) to find the default mail client and launch that executable yourself. Here's a method that will do just that:

    /// <summary>
    /// Launches the default mail client.
    /// </summary>
    public void LaunchDefaultMailClient()
    {
        // Open the "HKLM\SOFTWARE\Clients\Mail" key.
        Microsoft.Win32.RegistryKey mailKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey( @"SOFTWARE\Clients\Mail" );

        // The default mail application is stored in the default value of that key.
        string defaultMailApp = (string)mailKey.GetValue( null );

        if( defaultMailApp != null && defaultMailApp.Length > 0 )
        {
            // Open the subkey of the default mail application and the "shell\open\command" key below that.
            Microsoft.Win32.RegistryKey cmdKey = mailKey.OpenSubKey( defaultMailApp + @"\shell\open\command" );

            // We're now in "HKLM\SOFTWARE\Clients\Mail\<default-mail-app>\shell\open\command".
            // The default value of this key is the command line to start.
            string command = (string)cmdKey.GetValue( null );

            // If there are command arguments, extract them out of the main command string.
            string args = string.Empty;
            if( command.IndexOf( " " ) > 0 )
            {
                args = command.Substring( command.IndexOf( " " ) + 1 );
                command = command.Substring( 0, command.IndexOf( " " ) );
            }

            // Start a new process for the mail application.
            System.Diagnostics.Process.Start( command, args );
        }
    }

    Wednesday, August 25, 2004 6:50:41 PM (Romance Standard Time, UTC+01:00) #    Comments [11]  | 

     

    The ASP.NET 2.0 Profile#

    Tough luck. I had been busy (well, "busy", as I mentioned: the summer has been a bit of an idle time tech-wise) writing an article on the all-new Profile concept in ASP.NET 2.0, but I just noticed that I've been pre-empted by MSDN where the article "Storing User Information with ASP.NET 2.0 Profiles" just went online. It's a great read though, and it covers pretty much everything I had to say about it (and more of course) except the part about Profile migration on which I'll add my 2 cents now.

    Let me just briefly summarize the Profile, which is one of the cornerstones of the new Personalization framework: the Profile is a "personal, type-safe, persistent data store" for each of your website's users. You can use it to remember settings (like language or culture, UI theme, ...), data (favorite color, shopping cart items, ...) or anything else you can think of really. Type-safety is ensured because it is an actual object with typed properties, and the data is automatically persisted into a data store of your choice [1]. Furthermore, you can configure the Profile to work for users that aren't known or logged in yet. When the user decides he likes your site so much that he wants to create an account, you can migrate his anonymous profile very easily in Global.asax by implementing a method like the following one:

    Sub Profile_MigrateAnonymous(ByVal s As Object, ByVal e As ProfileMigrateEventArgs)
        
        ' Fish up the profile belonging to the anonymous user that just logged in.
        Dim anonProfile As HttpProfile = Profile.GetProfile(e.AnonymousId)
        
        ' Move the settings over to the authenticated user's profile.
        Profile.FavoriteColor = anonProfile.FavoriteColor

    End Sub

    So far for the article's example. There's a little issue with this approach though. Imagine a user who already created an account and is now just browsing to your site. He hasn't logged in yet so he gets the default settings for an anonymous user. Then he logs in, thereby migrating and overwriting his own settings (which were persisted from last time) with the default ones. Too bad, you just lost your shopping cart.

    Unfortunately, there's currently no way of checking if the profile being migrated to is new - i.e. if the user already had an account and is now just logging in, or if he just created an account. Only in the latter case will you want to migrate the profile, so that's pretty tricky. Hence the following feature suggestion: provide a ProfileMigrateEventArgs.IsProfileNew flag so we can check if we actually need to migrate the profile.

    Another option would be to check on the default values of the profile [2]. But what if the user changed one of the properties before he logged in? You would wrongfully not migrate his settings. Not good.

    So until there's a better way of doing this, I'd suggest putting a flag on the Profile itself that says it's a new profile and flip it over once you've migrated the anonymous profile, e.g.:

    <profile>
     <properties>
      <add name="FavoriteColor" allowAnonymous="true" defaultValue="Red" />
      <add name="HasMigrated" type="System.Boolean" defaultValue="false" />
     </properties>
    </profile>
    

    Sub Profile_MigrateAnonymous(ByVal s As Object, ByVal e As ProfileMigrateEventArgs)
        
        ' Migrate the profile preferences only if the user is new.
        If Not Profile.HasMigrated Then
        
            ' Fish up the profile belonging to the anonymous user that just logged in.
            Dim anonProfile As HttpProfile = Profile.GetProfile(e.AnonymousId)
        
            ' Move the settings over to the authenticated user's profile.
            Profile.FavoriteColor = anonProfile.FavoriteColor
            
            ' Indicate that migration has been done.
            Profile.HasMigrated = True

        End If
        
    End Sub

    One last remark about the Profile: a VaryByProfileProperty option for output caching would make sense. Imagine you have a multilingual website and you keep the user's Culture setting in the Profile. Then you could easily configure your page to be cached for all users that share the same culture as such:

    <%@ OutputCache Duration="60" VaryByProfileProperty="Culture"  %>

    It's not really shocking but it's just a bit easier than using VaryByCustom and returning the Culture manually...

    [1] The Profile derives from the System.Configuration.SettingsBase, which is the new configuration system available in .NET 2.0. It's also used in Windows Forms so you can have type-safe access to application- and usersettings in this environment as well. Goodness!

    [2] If you're working in VB.NET, you might wonder how you can access the default value of a Profile property. It's not listed in IntelliSense (I'm still puzzled why not because it does show up if you use C#) but there's a Properties collection you can use to access a certain Profile property and get the default value out of it as follows:

    Dim defaultValue As Object = Profile.Properties("FavoriteColor").DefaultValue

    Blog | Programming | .NET | ASP.NET | WeFly247 | Whidbey | Samples
    Tuesday, August 24, 2004 6:57:30 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    MCAD#

    I finally took the time last week to take my third Microsoft exam: "Developing XML Web Services and Server Components with Microsoft Visual C# and the Microsoft .NET Framework" (#70-320). I'm glad to see they're giving you your score afterwards again (I never knew how much I got for the WinApp and WebApp exams) because I came out with a score of 952/1000 :-)

    So that makes me a Microsoft Certified Application Developer; two more exams to go to become a Microsoft Certified Solution Developer. After I achieve that, I fully expect to become a NZCSH (New Zealand Certified Sheep Herder), a required step towards becoming the GCUU (Globally Certified Uomo Universale) - a title last given to Leonardo da Vinci but I've had my eye on it for a while now.

    By the way, there's a new elective security exam you can take to become MCSD.NET: "Implementing Security for Applications with Microsoft Visual (Whatever) .NET" (#70-330 or #70-340), with an accompanying new course "Implementing Security for Applications" (#2840) which again shows the big security push Microsoft is committed to. Goodness!

    Monday, August 23, 2004 8:55:04 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    There And Back Again: My summer trip to the unmanaged world#

    I've been quiet here, which would make sense to you too if you'd take the time to look outside and see that it's summer (open the fridge first to let your eyes get accustomed to the light). It's summertime in Antwerp more than anywhere else, so this is not a good time to be victimised by a vicious CRT's electron attack.

    Anyway, my latest project involved a rather interesting setting, so I thought I'd share a little of what I like to call my "summer trip to the unmanaged world" (fortunately I have a real holiday coming up to compensate the madness).

    The destination

    Imagine a stress test tool that is designed to record some actions and replay them ad infinitum (or - in our case - until the machine running it crashed). We wanted to use it to stress test a binary protocol between a PDA application and a communications server, but this involved keeping all sorts of overhead in a database and dynamically creating the buffers to be sent over the wire. So just replaying a recorded script wasn't going to cut it, hence I was asked to develop a library to parse the binary data coming in from the server, log all kinds of overhead data into a database, and assist in creating the outbound buffers.

    Getting the green card to pass the border

    The stress test tool we're using is expensive and good, but it isn't a development platform to say the least. It doesn't know the CLR, it doesn't speak COM, it can however run some C-style code with a magic LoadLibrary style call to load an external DLL, after which it can call the exported functions in it. That's our entrance.

    Packing the suitcase

    You may or may not have noticed, but I'm a .NET kinda guy. When I was asked to build this library, I gently proposed to do it in .NET because of all the advantages of the managed world. Not only would the library be more secure and easier to maintain (insert more marketing-speek here), but look at it this way: suppose I'm used to packing suitcases for trips, and I know that the clothes go in the big compartiment and the USB stick goes into a little side pocket. The other way around is just silly. (The laptop and booze go into a carry bag.) Don't ask me to stack a container with randomly sized boxes to get the maximum profitability for a single cargo trip. I'd be able to do it, but it would take a long time. So I built the protocol parser and database logging part in C# in 4 days (it would have been less if the protocol spec was complete and correct); I'd still be busy developing it if I'd have to do it in C++. The CLR really is all about developer productivity in the first place, take it from me.

    Building a bigger suitcase

    So I have this small cute suitcase full of MSIL goodies, but where I'm going, they only take old-style cases of fixed size where the handles are on predictable places so they can toss it onto the airport cargo reel quicker and easier or something. Fair enough. So I needed to have something to unpack and reassemble the stuff everytime it needed to pass there and back again (with due apologies to J.R.R. Tolkien). In short, I hacked up a mixed-mode C++ proxy DLL that has more or less the same methods as my library, and which marshals the calls to and from the unmanaged world. And it all worked (eventually). We had some trouble along the way but nothing vital, so we were actually quite pleased we got this baby in the air in a short amount of time. When a consultant from the stress test tool's company came over, he was actually pretty amazed at what we'd accomplished since we pulled more out of their own product than they'd ever seen being done before.

    The suitcase contents

    Depending on the weather, you'll get a more detailed explanation of the tech breakdown (i.e. the good stuff) sooner or later. But let me say this up front: I should not be allowed to develop production C++ code (at least not without a reviewer), and probably neither should you if you're as spoiled as I am and have been programming managed code (albeit .NET or Java) for your entire professional life.

    Harsh? Yes. True? Definitely. Writing secure code in the unmanaged world is hard and painful since you have around 10 years of Win32 mistakes to make up for. And it's quite a humbling experience to see a 4-line proxy method getting the heat in all possible ways for a few hours until it conforms to Win32 best practices and security demands.

    Blog | Programming | .NET | Java
    Friday, August 13, 2004 11:33:57 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    I certainly was - were you?#

    Due to popular demand (i.e. Jan Tielens' demand, I'm lenient ;-) ), if you want to brag about being member of the exclusive club that attended the first .be-geek-dinner:

    Patrick Verbruggen has some pictures of the evening up on his blog as well, by the way. Cool!

    If nobody beats me to it, I'll try to arrange the next one somewhere in the Summer time. My goal is to get some kind of simplified Moore's law in there for the attendance numbers. By that, I just mean that I want to get more and more people to show up ;-) So stay tuned, and start getting people that weren't there excited about the idea - shouldn't be hard, right...?

    Wednesday, June 30, 2004 12:23:09 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Whidbey Beta 1 Available#

    Psssst, don't tell anyone but Visual Studio 2005 Beta 1 is out on MSDN! I can't wait to kick the alpha I'm running now out of the door (sure it's cool and all but it's really messing with my head lately) and install this baby. And if you think that's all, think again...

    There's also a range of Express Products (lightweight development tools for non-professionals) which looks interesting if you can't just cough up the dough for the Visual Studio .NET flagship product and related tools. There's even a SQL Server 2005 Express! And there's a Summer Of Express Coding Contest over on Channel9 to promote these little gems...

    Some other big news: the new MSDN Product Feedback Center is a place where you can get direct access to the Microsoft bug databases. You can search bugs, report bugs, make suggestions, get notifications if bugs you're monitoring change status, ... Wicked!

    Anyway, sorry for bothering you with this - you probably read it all over the blog space today anyway. I just couldn't not post this ;-)

    Blog | Programming | .NET | VS.NET | Whidbey
    Tuesday, June 29, 2004 6:31:33 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    My.Family.Feed()#

    Just a thought for the My namespace in VB.NET Whidbey (since every programmer will be far too busy exploring all the new goodies anyway):

    My.Family.Feed()

    Sunday, June 27, 2004 1:39:24 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    The first .be-geek-dinner was good!#

    Thanks to everyone that showed up yesterday for the first .be-geek-dinner! Great beer, good chats, super meal, a fun little tour of the Sinksenfoor and a nice drink to top it all off.

    Eventually, 8 people were there and I think that's pretty good considering the timing (Tech-Ed starts on Monday) and the fact that there's just not a whole lot of us in our little country ;-) Too bad Patrick Tisseghem couldn't make it in the end, maybe his wife revoked her permission to be a geek after all ;-)

    I like the fact that I can put a face on your names now so it's been great meeting you all! Hope to see you again some next time :-)

    Saturday, June 26, 2004 4:46:38 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Practical details on the .be-geek-dinner#

    Update: I've added Peter Stuer to the list, Yves Hanoulle decided to skip this one but hopefully we'll see him next time. Furthermore, since there doesn't seem to be much interest for it, we won't be taking the tour of the brewery. See you tonight!

    Don't forget: the first ever .be-geek-dinner is this Friday the 25th of June, Het Pakhuis in Antwerp, at 20:00.

    Optionally, we can get a tour of the brewery with a degustation of their beers. That's a fixed price of €83,20 for groups under 15 people. I have to let them know 2 days in advance though (i.e.: tomorrow) so please put your vote in the comments now if you're ok with spending a few euros extra to do this tour.

    So here's the rundown of people that should be showing:

    So if my automatic bullet counter plugin is working correctly, that's 9 people. I've made reservations for 10 just to be sure. If you just figured out what a shame it is that you're not coming and you're sad about all the good times you'll be missing: just let me know and I'll bump the reservation count! Or if you can convince other people to come along, just use the comments here and I'll take care of it.

    See you on Friday!

    Tuesday, June 22, 2004 10:30:30 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Brandnew Look: BlogXP Theme#

    If you're reading this through an aggregator: there should be no difference whatsoever for you, carry on ;-)

    If you've actually fired up that trusted ol' webbrowser (have a drink to wash away the dust), you might have noticed that I finally got around creating a new look for my site/blog. I'd been planning on doing that for a while now, especially because the sidebar was growing too large and I wanted the individual panels to be collapsible. Note that I can collapse some panels by default now, but I just can't seem to decide which ones ;-)

    So I built sort of a "webpart" look-and-feel into dasBlog (without the flashy "moving" webparts though, and unfortunately it also doesn't "remember" your settings - maybe later). I snatched a few images and ideas, drew or enhanced some myself, scripted the collapsing and then spent far too much time playing with the css to make the whole thing look good. It still has a few minor quirks I ought to work out (mostly in the admin area) but all in all, I'd say it looks pretty nice. It looks best in IE (due to the gradient filters at the top) but it also seems to work great in Mozilla's new Firefox 0.9.

    So how do you like it?

    I've named the theme BlogXP, here's a zip file if you want to use it yourself (go wild):

    blogxp.zip (14,21 KB)
    Thursday, June 17, 2004 8:26:50 AM (Romance Standard Time, UTC+01:00) #    Comments [6]  | 

     

    How Hotmail might surpass GMail and Yahoo! Mail#

    Yahoo! Mail has just upgraded its free email service so you can store up to 100MB in stead of the meager 6MB it was before. Furthermore, it also has an advanced search feature that lets you search messages in your mail folders by certain criteria.

    So, that's a very obvious move toward Google's upcoming GMail service, which allows storage up to 1GB and also has this type of full-text searching. (Or so I'm told by the hype machine anyway, because it's in beta and on an invite-only basis for now.) So who said competition wasn't doing any good for customers?

    So that's two search engines coupled with email services making a move. I'm predicting MSN Search and Hotmail will soon follow. And if they want to keep an edge, they'll have to surpass it somehow.

    I have an idea how they might achieve that: why does everybody want POP or IMAP access to mailboxes? (It's still in beta but there's already a POP proxy for GMail [via Scoble's Linkblog]...) Because the browser experience just isn't good enough for demanding email users. (And for long-term archival, but that's something these new large email storage spaces are supposed to handle.)

    So how about a smart client for Hotmail? Think about it, access your email everywhere in a rich GUI without having to explicitly install anything. It's a perfect sell for the smart client, .NET, Hotmail, MSN - Microsoft tout court. Should be easy, right?

    Wednesday, June 16, 2004 8:42:41 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    MoodTip#

    In the series "human-pc interaction driving you to deeper metaphysical contemplation" (see MoodPrompting), Visual Studio .NET just enriched my life with a "MoodTip" - a tooltip designed to make you think twice about your software design. Depending on the mood of the environment, it appears that the Controller property is either Overridable (virtual for all you C# boys & girls) or NotOverridable (sealed).

    I'll be off to the Himalaya to think this through, I'll let you know when I figured it out...

    Blog | Programming | .NET | Quirks | VS.NET
    Tuesday, June 15, 2004 3:35:29 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Trouble installing Longhorn WinHEC#

    I've been trying to get the WinHEC build of Longhorn installed but to no avail... Did anyone get it up and running on a Dell Latitude D600?

    I run the setup (from within Windows XP) to install it onto a freshly formatted partition, and I get up to the part where it's supposed to start copying files onto the partition. At least I suspect so, because I get "An error occurred while copying files".

    Now I've read in the readme (I actually read those on anything other than RTM builds nowadays) that the hard disk driver must be supported - "or else"... (Or else what? I don't know. It probably throws funky "cannot copy" errors in your face, right?) Luckily, you can insert a floppy disk containing drivers at some time during the setup process if your hard disk is not supported.

    Well, 'luckily' is a bit of an overstatement. A floppy disk? Let me sketch that into its right context here: I'm installing Windows Longhorn, a true 21st-century OS with vector graphics, a filesystem backed by a relational database, an extensible item store, a polyglot communication stack, all programmable with the obiquitous .NET runtime, and (how conveniently) a brandnew driver model - and it's asking me for a stone age floppy disk? Even if I had a floppy drive for my laptop (which I don't), I certainly wouldn't have any driver disks to feed it with. It's kind of like "here's that ftp server with the zipped 19GB pr0n collection you asked for, but it only has a 56K modem and there's 178 users before you".

    Anyway. It's alpha. Years from shipping. A lot can change. (Read: change this!)

    So with that option out of the way, I tried installing it onto a new VirtualPC. Unfortunately, you can't mount a directory (the one containing the setup files) as a CD in VirtualPC. So I made an ISO out of the lot and mounted that, but I can't get VirtualPC to boot it (how do you make a bootable cd?)... It would be nice if VirtualPC could map the virtual drive onto the local filesystem (kind of like the nifty Shared Folders, but then backwards) so I could try installing it straight onto that, but there's no way of accessing the virtual drive straight from the host system as far as I know.

    Alas, so far nothing worked. No Longhorn for me. If anybody has any ideas, be sure to let me know!

    Monday, June 14, 2004 10:14:41 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Get your .NET code ready for other platforms#

    Although .NET is mainly targeted at Windows right now, it's obvious from the Mono Project (now in beta 2 phase!) and other initiatives like the SSCLI that running .NET code on other platforms is either already possible, or within reasonable reach. So how do you take that into account when developing in .NET?

    Personally, I knew I was doing the "right thing" all along because it just felt right, but it's nice to see that reflected by 'trusted sources' once in a while: Scott Hanselman takes some gems away from nGallery's Mono compliance and sprinkles them with a little of his own fairy dust. Also, Clemens Vasters just noticed that there's a dasBlog port on mono. These accomplishments are both pretty impressive.

    So here's the aggregated wisdom, with a personal topping to cover it up:

    1. Use constants like System.Environment.NewLine and System.IO.Path.DirectorySeparatorChar - everywhere and always.
    2. Furthermore, use platform helper methods as much as you can. Don't outsmart the system, it's not because you know from your daily Windows usage how to append a filename to a directory like so:

      string fullPath = directoryName + @"\" + fileName;

      that this wisdom will help you on all platforms. (By the way, did you check to see if directoryName didn't already end in a backslash? And add a myriad of other checks here as well.)

      Use

      string fullPath = System.IO.Path.Combine( directoryName, fileName );

      in stead. Guaranteed to be correct (regardless of the platform), more readable and meaningful, and less code (if you'd add the necessary checks to your own code of course)!
    3. Make sure sure all I/O operations (files, directories, ...) are case sensitive.
    4. Make sure you don't compile your assemblies with the /incremental flag.

    I'd like to see that first point supported in the framework just a little better though, since not using Environment.NewLine is just that tiny bit easier. Compare

    Console.WriteLine( "Exception details:\r\nMessage: {0}\r\nStack Trace:\r\n{1}", e.Message, e.StackTrace );

    to

    Console.WriteLine( "Exception details:{0}Message: {1}{0}Stack Trace:{0}{2}", Environment.NewLine, e.Message, e.StackTrace );

    Arguably, the second one does look a bit cleaner, but it's still more cumbersome with the extra parameter. So, an extra escape character indicating a new line (regardless of the platform) would be nice. Who cares about those 'return' and 'newline' codes anyway these days.

    Other than that: stick to the rules and on one beautiful day, you just might find yourself running on Linux. And why not, why not indeed.

    Friday, June 11, 2004 12:04:18 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Shortcomings Of InfoPath 2003#

    InfoPath 2003 is a pretty cool product with lots of potential, but it's obvious that it's still a little young for the real world. Here are some issues I found when developing with InfoPath out-of-the-box:

    • You can't make fields required easily if the datasource is a (readonly) webservice since you can't change that definition and you can't set the validation to "cannot be blank" anymore. Apparently, this is by design: "Notice that the Cannot be blank option is disabled; this property is always disabled in the InfoPath user interface when your data source is an external data source". Why is that?
    • Publishing is a pain; I just wanted to publish to a website on my local machine and after a lot of head-scratching I found that you actually have to deploy to a network share. The first thing you do is set the directory (the virtual directory on your drive) and then the web url from which the form will be accessible.
    • Copying the form to another location - e.g. from develepment to acceptation or production - can't be done easily since the originating location is baked in so you have to redeploy it. Alternatively you could try to script the process to extract the xsn file (which is really just a cab file), update the location and repack it.
    • You can't prefill a form with data from a WebService easily, if you don't want the user to click a button first then you'll have to resort to scripting. This looks like a very common scenario though, so I'd expect this to be a lot easier.
    • Data binding the controls to the fields can be tedious, a drag & drop mode would be easier.
    • There's no support for WSE so I can't use WS-Security; another option would be to use custom SoapHeaders but there's also no support for that.
    • There's no password control, so I can't prompt the user for his credentials without having him expose his password to potential neckbreathers.
    • It would be nice if InfoPath worked a bit more like ClickOnce: click an xsn in your browser and have the the form open immediately in stead of getting the standard Open/Save dialog. Of course, it's still a cab file so it could be harmful to just allow this without the ClickOnce wonders of .NET security - but it would provide a much more transparant user experience.
    • Copy/paste doesn't work between instances of InfoPath, not even for plain text. So it's pretty hard to copy part of an existing form to another one.

    Note that this is based on a standard InfoPath 2003 installation, I haven't tried the new InfoPath SP1 Preview yet (which might solve some issues but certainly not all of them).

    Monday, June 07, 2004 4:00:32 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    The Ultimate Reflector Setup#

    Why:

    • Because you will need to use it.
    • Because it allows you to search and look at assemblies, classes, methods, fields, inheritance relationships, call stacks, references, resources, ... (let's summarize that as everything).
    • Because you can decompile methods to any supported language (IL, C#, VB.NET, ...).
    • Because it works with any .NET runtime.
    • Because it's cool :-)

    Download:

    Install Reflector:

    • Unzip Reflector into some random directory on your hard drive (no further installation necessary).
    • Unzip all add-ins to the Reflector directory, open Reflector and add them all (View - Add-Ins - Add).
    • Test: select mscorlib and choose Tools - File Disassembler, click Generate and run through the generated source code to verify that there are no bugs in the .NET Framework (warning: this might be illegal ;-) )
    • Congratulations, you're now ready for some serious .NET development.

    Install the Visual Studio .NET add-in:

    • Run Reflector.VisualStudio.exe to install the Visual Studio .NET add-in.
    • Open Visual Studio .NET and enable the Reflector add-in (Tools - Add-in Manager - Check the tool and startup boxes).
    • Test: right click a project and choose Reflect, it should show you a dockable Reflector window.
    • Congratulations, you're now in lazy mode so you never have to leave Visual Studio .NET anymore.

    Enjoy:

    • You now have direct access to one of the most useful .NET utilities ever.
    • (It does eat up quite some memory though - 40 MB minimum on my machine, with all add-ins loaded - but it's worth it.)
    Blog | Programming | .NET | VS.NET
    Sunday, June 06, 2004 1:18:02 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    .be-geek-dinner#

    Update: because of TechEd, the date has moved to the 25th of June.

    Look people, we're running way behind here. Seriously. We must be around the smallest country in the blogosphere, still we don't use that huge geographic advantage to have us some good old-fashioned fun.

    A lot of you are doing the linking thingy and some of you are keeping up a separate Belgian blogroll - great! But I'd like to get to know you Belgian bloggers out there a little more personally. Put a face on the fame, you know. So how about we arrange ourself a little get-together and have a geek dinner (a ".be-geek-dinner" if you will) like our American friends do all the time. Only better - of course ;-)

    I guess it's impossible to get this done democratically by everybody voting for dates and places so I'll just toss in a suggestion:

    Friday the 2nd of July Friday the 25th of June, Het Pakhuis in Antwerp, at 20:00.

    Closing financial years and deadlines should be over by then, it's a pretty big place so there's enough room, and best of all: it's also a fine brewery so we're upholding our Belgian culture :-)

    Leave a comment if you're coming; if there's enough people, I'll make reservations and try to get us a tour of the brewery.

    So... Spread the word, steal this post, or hype it up big time and host the button!

    (Yeah I realize this button thing is way over the top, but whatever :-) )

    Friday, June 04, 2004 7:33:04 PM (Romance Standard Time, UTC+01:00) #    Comments [17]  | 

     

    Last-Minute Whidbey Feature Request: Easy Global Interception#

    While we wait for decent AOP frameworks in .NET that can handle this kind of stuff, wouldn't it be great if we could just do some basic interception like the code below? We can't have every single object inherit from MarshalByRefObject to get some interception goodies now can we? At least we'd get rid of boilerplate code added to every single method (who wants to type hard to maintain logging code manually anyway, or have it generated and still clutter your code view).

    Of course this has some major security implications (if you can hook into any method and look at it's parameters you can easily sniff passwords or alter parameters or look at private methods you weren't intended to see). But I guess these could be fixed by allowing only your own methods (i.e. in the same assembly) to be intercepted.

    As for the performance hit, well, in the easiest cases where the MethodInfo parameter isn't used (to 'switch' on certain methods for example) the compiler or JIT could inline this stuff altogether (change obj to this and replace the args with their respective parameter names). Otherwise, there'll be a pretty serious performance penalty but then again, you get some serious benefits and you can enable it dynamically only in case there are problems on your production machines. When disabled, the code won't even be run so it'll perform at a whopping 100%.

    CLR guys? Got some spare time before the first Whidbey beta? Robert? Got some spare time to push this up the agenda? ;-)

    public void Initialize()
    {
        // Only allow this if we have problems or while debugging.
        if( enableInterception )
        {
            // Hook up some event handlers to get interception on method entry and exit.
            System.Reflection.MethodInfo.MethodEntered += new MethodEnteredEventHandler( MethodEntered );
            System.Reflection.MethodInfo.MethodLeft += new MethodLeftEventHandler( MethodLeft );
        }
    }

    /// <summary>
    /// Called when any method is entered.
    /// </summary>
    /// <param name="info">The runtime information about the method being entered.</param>
    /// <param name="obj">The object on which the method is being called, or null if it is a static method.</param>
    /// <param name="args">The arguments passed to the method.</param>
    /// <remarks>
    /// This method is not called for event handlers of the <see cref="System.Reflection.MethodInfo.MethodEntered"/> event
    /// to avoid an infinite loop.
    /// </remarks>
    public void MethodEntered( System.Reflection.MethodInfo info, object obj, object[] args )
    {
        // Insert your logging, security, pre-condition check, ... code here.
        System.Diagnostics.Debug.WriteLine( "Method entered: " + info.Name );
    }

    /// <summary>
    /// Called when any method is left.
    /// </summary>
    /// <param name="info">The runtime information about the method being left.</param>
    /// <param name="obj">The object on which the method is being called, or null if it is a static method.</param>
    /// <param name="outArgs">The byref and out arguments of the method.</param>
    /// <param name="returnValue">The return value of the method, or null if it is a void method.</param>
    /// <param name="exception">The exception that caused the method to be left, or null if the method exited normally.</param>
    /// <remarks>
    /// This method is not called for event handlers of the <see cref="System.Reflection.MethodInfo.MethodLeft"/> event
    /// to avoid an infinite loop.
    /// </remarks>
    public void MethodLeft( System.Reflection.MethodInfo info, object obj, object[] outArgs, object returnValue, object exception )
    {
        // Insert your logging, security, post-condition check, ... code here.
        System.Diagnostics.Debug.WriteLine( "Method left: " + info.Name );
    }

    Thursday, June 03, 2004 11:09:59 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Rotating an image around its center in .NET#

    As you can see on the new screenshots for the WeFly247.NET Passenger website, there's a dynamic flightmap image displaying the route the plane has already traveled and where the plane is at now.

    As I was replacing some hard-coded info (like the image to use for the map, the start- and endpoints of the flight on the map, ...), I figured I was also going to need to rotate the plane dynamically so that it would follow the path (I wouldn't feel too comfortable if I was looking at a flightmap where my plane seemed to fly sideways). Before that, I had just rotated the static image itself, but if the flight path can change, so can the rotation.

    There is an Image.RotateFlip method but that only allows rotating over 90/180/270 degrees. I also looked at some matrix transformations on a Region object using Matrix.Rotate, but that seemed a little too hard to get working. Furthermore, I wanted to rotate the plane around its center (not around the upper left corner as most rotations do) so I sat down and thought deeply about rotation equations (so my math teacher was right, it does come in handy sometimes!), decided I forgot all about it (sorry teach), and looked it up again.

    After rotating a point (x, y) around the origin over an angle a, the new coordinates (x', y') are:

    x' = x cos(a) + y sin(a)
    y' = -x sin(a) + y cos(a)

    Now you can use a Graphics.DrawImage method overload to draw the image in a given parallelogram extrapolated from an array of three points. That would do just fine to rotate the image if I could calculate the three points, which I could using the simple formulas above. So my final implementation to draw a rotated image onto a graphics object became (beware: VB.NET snippet coming up ;-) ):

    ' Draws an image onto the given graphics object. The image is rotated by a specified angle
    ' (in radians) around its center and then drawn at the given center point.
    Private Sub DrawImageRotatedAroundCenter(ByVal g As Graphics, _
        ByVal center As Point, ByVal img As Image, ByVal angle As Double)

        ' Think of the image as a rectangle that needs to be drawn rotated.
        ' Rotate the coordinates of the rectangle's corners.
        Dim upperLeft As Point = RotatePoint(New Point(-img.Width / 2, img.Height / 2), angle)
        Dim upperRight As Point = RotatePoint(New Point(img.Width / 2, img.Height / 2), angle)
        Dim lowerLeft As Point = RotatePoint(New Point(-img.Width / 2, -img.Height / 2), angle)

        ' Create the points array by offsetting the coordinates with the center.
        Dim points() As Point = {upperLeft + center, upperRight + center, lowerLeft + center}
        
        ' Draw the rotated image.
        g.DrawImage(img, points)

    End Sub

    ' Rotates a point around the origin by the specified angle (in radians).
    ' Rotation adheres to the following rules for the new coordinates:
    ' x' = x cos(a) + y sin(a)
    ' y' = -x sin(a) + y cos(a)
    Private Function RotatePoint(ByVal p As Point, ByVal angle As Double) As Point

        Dim x As Integer = p.X * Math.Cos(angle) + p.Y * Math.Sin(angle)
        Dim y As Integer = -p.X * Math.Sin(angle) + p.Y * Math.Cos(angle)

        Return New Point(x, y)

    End Function

    Wednesday, May 26, 2004 9:53:56 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Last month on WeFly247.NET#

    It's been quite a while since the last progress report on WeFly247.NET, but we've been very busy getting a more or less stable build out for the rest of the team to review - luckily that also means a lot of work has been done in the mean time :-)

    Last time, I left off with a "standalone" Passenger web site that looked like it was designed by a developer (well, it was :-) ), but since then I've updated the look and feel to match the great designs from Elevator Digital and connected it all to the business tier. The website is pretty much done right now, it's all talking to the backend over Web Services through smart Agents that handle security and custom authorization Soap headers.

    The home page:

    The "panels" (flight info, destination info, crew info, ...) on the homepage are Web Parts so you can customize the whole lot and look only at what you would want to see during your flight. The Flight Map on the homepage is a dynamic image (easy as pie with the new .asix handler and the DynamicImage control in ASP.NET 2.0) which displays the the route the plane has already traveled and where the plane is at now.

    There's also an InfoPath form that allows you to fill in your Customs & Immigration information; it also pulls and pushes data from and to the backend through Web Services (but since InfoPath doesn't talk Soap headers yet, it's not automatically secured yet).

    The Duty Free Shop:

    So with that part pretty much done, I've started working on the Maintenance application, which is a Smart Client application for the pilots that can be used on a Tablet PC. They use it to go over a checklist before takeoff (there are no gas stations in mid-air so better check if you're fueled up) and to monitor some statistics in-flight (to make sure you won't be fined for speeding). There's some custom controls here with transparency, and the whole app is supported by smart Office documents: the new ActiveDocumentHost control allows you to embed those directly into your WinForms app! If there's a problem with one of the readings (e.g. your altitude is too low), you can look at an Excel sheet showing the data and you have a Research Pane ready to help you diagnose the problem before you submit a problem report in Word.

    The Maintenance Application:

    So we've already covered quite a lot of the scenarios but there's still a lot of work (especially on the Flight Attendant app on Pocket PC) - so I'm off coding again!

    Wednesday, May 26, 2004 9:12:07 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual Studio Team System#

    Whoa, a lot of important stuff seemed to have happened over night. Now they sure wouldn't have anything left to say on a PDC this year, since they just unveiled some pretty impressing technology on TechEd yesterday: the Visual Studio Team System.

    It's been all over the blogs so I'm not going to repeat all the goodness here, I'll just mention some highlights:

    • Integration of FxCop and other code analysis tools
    • They killed NUnit and NCover (which hadn't even really taken off yet), indeed, why stop at killing NAnt ;-)
    • They finally gave that good old piece of junk called SourceSafe a neckshot and replaced it by (make sure you're sitting down for this) a spicy new SQL Server-based, fully transactional, client-server source control system! (Please hold on while I whipe my chin.)
    • A bunch of designers (I suppose they're summarized under the "Whitehorse" umbrella?) to help all roles on the team (architect, dev, tester, ...) put their ideas into spiffy models.
    • ...

    And in other news (via Sam Gentile): WSE 2.0 has shipped!

    Damn, I'm going to need even more hours in a day :-(

    Tuesday, May 25, 2004 8:27:07 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ADO.NET 2.0 Quickies#

    Writing data access code will probably remain useful for quite some time, especially since ObjectSpaces is being rolled into Longhorn's WinFS data store. I sure do understand why, but I'm still a little sad to see it go (although it's not really gone of course).

    Anyway, here's a quick dump of some things I picked up from a recent MSDN TV episode on ADO.NET 2.0. Especially cool is the "provider-agnostic data access code" so you're never coding against an actual provider (SQL Server, Oracle, ODBC, plain text, whatever, ...) but use the generic versions through a factory...

    // Get the configured providers if you want to see them all.
    DataTable providers = DbProviderFactories.GetFactoryClasses();
    // The "InvariantName" column contains the invariant name to be passed to GetFactory.

    // Use a specific provider.
    DbProviderFactory factory = DbProviderFactories.GetFactory( invariantName );
    using( DbConnection c = factory.CreateConnection() )
    {
        // Use generic methods to create commands and other ADO.NET goodies.
        c.ConnectionString = "...";
        DbCommand cmd = c.CreateCommand();
        cmd.CommandText = "...";

        // Something else that's new: load a DataTABLE directly in stead of a DataSET.
        DbDataReader r = cmd.ExecuteReader();
        DataTable table = new DataTable();
        table.Load( r );
    }

    // And for perfomance, in stead of updating each row separately to the DB,
    // batch them all at once to lower the number of connections to the DB.
    // This will call sp_executesql(""); with a sql string that contains the batched statements.
    DataAdapter da; // Initialize this...
    da.UpdateBatchSize = 100;

    Blog | Programming | .NET | ASP.NET | Whidbey | Windows | Longhorn
    Monday, May 24, 2004 2:53:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    No PDC in 2004#

    Scoble reports on Channel9 that there won't be a PDC this year.

    They are planning one for 2005 though so start moving your conference budgets over to that year ;-)

    Saturday, May 22, 2004 11:23:39 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Easy sorting with generics and anonymous methods#

    Imagine a typed dataset of which you want to sort the rows in a special way according to a calculation on the column values. Sounds fairly complicated - but using the new generics and anonymous methods feature in C# Whidbey, it's pretty easy really:

    MonitorItemData.MonitorItemsRow[] rows; // This array is already filled.
    Array.Sort<MonitorItemData.MonitorItemsRow>( rows,
        delegate( MonitorItemData.MonitorItemsRow row1, MonitorItemData.MonitorItemsRow row2 )
        {
            // Calculate the absolute deviations of the individual monitor items.
            // Multiply by 10000 to increase the subtraction precision of the ints.
            int deviation1 = Math.Abs( 10000 * ( row1.ActualValue - row1.StandardValue ) / row1.StandardValue );
            int deviation2 = Math.Abs( 10000 * ( row2.ActualValue - row2.StandardValue ) / row2.StandardValue );

            // Sort descending according to the deviation.
            return deviation2 - deviation1;
        }
    );

    For the Array.Sort method, we're using a generic overload which takes a an array of the generic type T and a Comparison<T> delegate that can compare two instances of the generic type. But where is the Comparison delegate type mentioned? Nowhere - it's called type inference. So by looking at the signature on the anonymous method above, the compiler can make a well-educated "guess" about the type of the delegate and this baby will just compile and run. Cool? I think so!

    Monday, May 10, 2004 9:45:48 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    More on NullReferenceExceptions (or is it less)#

    Whoa, this must be the most verbose explanation of why you get an "Object reference not set to an instance of an object" error I've ever seen. Well, I guess someone had to put it this way sometime.

    Maybe it's just in case people a few hundred years from now will actually want to dust off their museum pieces (you know: tablet pc's, stuff with screens and input devices, the physical stuff) and actually try to compile and run something just for kicks. They will sooo need this FAQ and a debugger then.

    Friday, May 07, 2004 8:11:27 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Random Visual Studio Whidbey Annoyances#

    I guess I'm in bitch-mode anyway so why not: some random things that are driving me nuts in the Visual Studio .NET Whidbey CTP:

    • Those damn datasets that keep generating an extra SomeDataSet1 file and class! It's great that we get typesafe datasets from the MSDataSetGenerator, really, but one class will do just fine thankyouverymuch. It's probably because the dataset is under source control, thereby making all the associated files readonly. This makes it pretty hard for the generator to write to the file and in stead of checking out the original file, it has the great idea of ignoring it altogether and appending a number to it in stead - thereby introducing multiple versions and breaking builds. (Just my guess.) It even messes things up sometimes if you're just looking at the dataset. What's wrong with looking at a file? Never let looking at a file trigger a checkout!
    • Clicking a project item in the solution explorer when you're coming from another project makes the solution explorer scroll so that the current project node is made visible. Contrary to what someone must have thought on the design team: that's not cool. I don't expect any window to change its position or layout by just clicking in it. But what's a lot worse: if you double-click, say, a classfile and it decides to scroll then the double-click fires afterwards and you're really opening a different file. Combine that with the previous remark and you can have the great effect that double-clicking some regular class in solution explorer can cause the window to scroll, thereby making itself think you clicked a dataset, triggering it to be displayed, checked out and having it generate a SomeDataSet1 file. Did I mention this wasn't a cool feature?
    • And last but not least... The eternal pain of references. This is killing me. References have been bugging me before, so please, please, pretty please with sugar on top, fix them once and for all! Although certainly not always possible, the ultimate solution to problems with references has always been using Project References. So that's what I'm doing now, but in ASP.NET they tend to switch from project references to fixed binary references that don't get updated anymore. And you wonder why you get runtime errors... So you need to check those references regularly and reset them to project references. Joy! What's even funnier is that an ASP.NET site doesn't have a project file anymore, which would be fine if there was nothing to remember for an ASP.NET project. But what about these references? I'm sorry to have noticed that, but they're stored in the solution file. Bad, bad, bad... If you have multiple solutions with the same ASP.NET project in them, it would depend on the solution file which kind of references the project would use. If you really need project-settings, revive the project file guys and recall the "no project file needed"-hype. What's one file extra going to do anyway?

    Anyway, ASP.NET 2.0 still pretty much rules apart from that so that makes up for a lot :-)

    (That doesn't mean that this shouldn't all get fixed of course, so if you're reading this and can do anything at all about it that would be greatly appreciated :-) )

    By the way, I realize it's an alpha build so it's not perfect; this post will just help me re-evaluate the beta once it ships :-)

    Blog | Programming | .NET | ASP.NET | VS.NET | WeFly247 | Whidbey
    Tuesday, May 04, 2004 9:32:36 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    The ASP.NET 2.0 ObjectDataSource Revisited - or - Give Me That Extra Inch!#

    Since I cried tears of joy over the new ObjectDataSource in ASP.NET 2.0, two things have happened:

    The first one is only important if you're doing, well, anything at all in .NET really. By that I mean: go get it already dammit! It can now load, visualize, search and decompile assemblies from any .NET runtime, including the Whidbey ones, so this killer tool just became even better! I just used it to check upon my remark to see if the ObjectDataSource would cache the runtime type information it found - but it doesn't look like it does. This would imply a significant overhead on assembly probing and reflection each time the data is fetched (if it isn't read from the cache of course). Anyway, let's call that "room for improvement" or "never mind you twat they know bloody well what they're doing so stop decompiling and jumping to conclusions already".

    Regarding the second one, I thought about naming this post "ASP.NET 2.0 DataBinding - Close But No Cigar" or even "Microsoft still doesn't get DataBinding" - but that's just a little harsh and arrogant ;-) It would probably generate more attention though, but for now, I'd just like to subtitle it "Give Me That Extra Inch".

    So what's the problem? Let me just briefly sketch the right context here... Most modern applications are n-tier, which means your data is stored in a data tier (which is backed by a relational database most of the time), it passes a business tier holding all your core business logic, and then reaches your user interface tier. The data itself is "disconnected", meaning no connection (to a database or other data store) is kept alive and the data has no memory of where it came from. In .NET land, this is represented by the DataSet object with embedded DataTables, primary keys, relations, constraints, ... - thus providing an in-memory representation of a relational database. But I'm sorry for boring you; you know all of this, right?

    So the two main points in modern data-driven enterprise development are: n-tier and datasets.

    And although we (and certainly Microsoft) have known all this for years, there's never been any real support for this in the .NET framework or in the samples: 95% of all examples still go straight from the UI to the database. And that's frustrating for you as an enterprise developer, since they're not realistic or useable in your world.

    Now I thought for a minute that the ObjectDataSource would finally be able to solve this issue - as I've said before, it's a DataSource control that fetches data from any object through declarative configuration. So just hook it up to a remote business component or web service that returns a dataset and you've succesfully decoupled your UI from your other tiers, while maintaining your easy-to-use declarative ASP.NET databinding.

    Well, yes, but only partially. While this works great for a lot of scenarios, it fails when things get just a little bit more complex: it won't work when you have multiple data tables in your dataset, which is a very common practice when you're fetching related data. Since you're working with multiple tiers, you'll want to reduce the number of calls and get chunky in stead of chatty conversations. So you try to group as much related data in a dataset as possible. And then the client can look at this data as individual tables but also navigate the relationships between them.

    And a data source having multiple tables has always been supported in databinding as well, using the data member to indicate which table in your dataset should be used. The new databinding classes also support this through "views" on the data, which you can retrieve by the view name. Unfortunately, here's the drawback: the ObjectDataSource only supports one table from the dataset. It actively checks on the retrieved object to see if it's a dataset and if it is, it just uses the first table to create its one and only view. (How do I know? Reflector for president!) Even the documentation says so: "The ObjectDataSource control supports only one data source view". So that's where it ends I guess. They really did come close this time, but I'm still a little disappointed.

    I need to get a little more chunky than chatty too, so just to summarize: please make the ObjectDataSource support multiple views if there are multiple DataTables. Thanks. That's all I wanted to say really :-)

    Monday, May 03, 2004 3:27:56 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Working around some ASP.NET AdRotator limitations#

    I'm playing working with the updated AdRotator in ASP.NET 2.0, and I had to work around some limitations so I figured I'd best share what I came up with...

    The AdRotator is a control that shows a random advertisement, which is basically just an image and a link to a target page (like a shopping page or an e-commerce site). And that's the problem. That's all it is. An image and a link. What I wanted to do is make an advertisement that also shows a description and a price next to it, but unfortunately there's no templating mechanism for the AdRotator to customize the control. (Spot the hidden feature request.)

    Another problem I had is that I pull the advertisement data from the database (the new AdRotator supports the cool new ASP.NET 2.0 databinding so you're not limited to xml files anymore like before). But there's no NavigateUrl in the database, and I need to give the AdRotator a URL to which it should navigate when you click the image. Unfortunately, I can't make the NavigateUrlField "smart". I have a ProductID in my datasource, so I'd like to have it construct a url based on that, a little something like this:

    <asp:AdRotator (...) NavigateUrlField='<%# Eval("ProductID", "~/Shop.aspx?ProductID={0}") %>' />

    But that doesn't work of course, the NavigateUrlField has to point to a field in the datasource (and I'm not sure if this <%# %> construct would work anyway)...

    I could add a NavigateUrl field on the business tier but frankly, I don't want to. It's not the business tier's business (pun intended) to know there's an AdRotator somewhere down the line that needs this funny Shopping URL. It only knows about products. I could add the column on the client tier but then I'd lose my beautifully simple databinding since I'd need to preprocess the data. Well in fact the databinding would still be easy, I'd just bind it to a local "proxy" preprocessor object in stead of to the actual webservice. But it's less transparant and I still wouldn't have my price and description anywhere near my image...

    In the end, I decided to do it a little differently. I created a simple Advertisements user control with an AdRotator in it, and labels for the price and description. Then I just use the AdRotator's AdCreated event to play around a little bit. The trick is, you can get to the data source fields through the AdProperties dictionary on the AdCreatedEventArgs. I'll just let the code speak for itself:

    <%@ control language="VB" classname="Advertisement"%>
    <script runat="server">
     Private Sub shopAds_AdCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AdCreatedEventArgs)
      e.NavigateUrl = "~/Shop.aspx?ProductID=" & e.AdProperties("ProductID")
      description.Text = e.AdProperties("Description")
      price.Text = e.AdProperties("Price")
     End Sub
    </script>
    <table>
     <tr>
      <td>
       <asp:AdRotator ID="shopAds" Runat="server" DataSourceID="advertisementData" ImageUrlField="ImageUri" OnAdCreated="shopAds_AdCreated" />
      </td>
      <td>
       <asp:Label ID="description" Runat="Server" />
       <br />
       <asp:Label ID="price" Runat="Server" />
      </td>
     </tr>
    </table>
    <asp:ObjectDataSource ID="advertisementData" Runat="server" TypeName="WeFly247.UI.Proxies.PassengerWebService" SelectMethod="GetProducts">
     <SelectParameters>
      <asp:ProfileParameter Name="cultureName" Type="String" PropertyName="Culture" />
     </SelectParameters>
    </asp:ObjectDataSource>
    Blog | Programming | .NET | ASP.NET | WeFly247 | Whidbey | Samples
    Wednesday, April 28, 2004 10:14:37 AM (Romance Standard Time, UTC+01:00) #    Comments [13]  | 

     

    DataBinding in ASP.NET 2.0#

    ASP.NET 2.0 features a whole new DataBinding infrastructure, which allows you to define and consume data sources declaratively. Sounds fancy, and it is. If you look at last week's coding summary, you can see this in action:

    <asp:ObjectDataSource ID="infoData" Runat="server" TypeName="WeFly247.UI.Proxies.PassengerWebService" SelectMethod="GetFlightData" />
    <asp:GridView ID="info" Runat="Server" DataSourceID="infoData" />

    As you can see, there's that new GridView control, and in stead of binding it in code (you know, set the DataSource property and don't forget to call DataBind!) you just set a DataSourceID. This is a reference to some data source object, which can now be just a control as any other (under the "more markup! less code!" motto).

    Now there are a number of data source controls: SqlDataSource (accesses SQL Server), AccessDataSource (accesses Access (whew)), SiteMapDataSource (reads a sitemap file), DataSetDataSource and XmlDataSource (which speak for themselves), and last but certainly not least: the ObjectDataSource.

    Now this is what I call goodness. They've finally come to their senses and realized that not everyone uses a database on the client tier. They've heard themselves say "Web Services" one time too many I guess, and this ObjectDataSource is the solution for everyone who has data living on another tier. What it does is create a new instance of the type you specify, and then call a method on it to get the data. After that, the object is disposed again but your disconnected data remains alive in the data source control.

    Possibly (I'm just guessing - hoping actually - here, I haven't checked since I can't use Reflector to decompile this yet :-) ) they'll cache the object and method information (not the object itself, the documentation states clearly that it's disposed of) somehow to prevent every call from having the overhead of using reflection to find the type and the method to call. Anyway.

    So in our case this little piece of markup just creates a new Web Service proxy for the passenger Web Service, calls the GetFlightData method on it (which effectively sets the whole SOAP story in action, talk about hidden complexity) and uses the returned data for data binding.

    Sounds easy, and easy usually means limited. Fortunately, they've thought well about it, and provided quite a range of extra options. DataSource controls support parameters to be passed in or out, through parameter controls which can fetch their values from other controls, form fields, cookies, the new ASP.NET Profile, the query string, the session, or you can build your own. (A literal parameter value would be nice to pass in a constant though, but it doesn't seem to be there...). For example, this one fetches the user's culture from his Profile, and passes that along to the GetProducts method:

    <asp:ObjectDataSource ID="advertisementData" Runat="server" TypeName="WeFly247.UI.Proxies.PassengerWebService" SelectMethod="GetProducts">
     <SelectParameters>
      <asp:ProfileParameter Name="cultureName" Type="String" PropertyName="Culture" />
     </SelectParameters>
    </asp:ObjectDataSource>

    And in case you were worried about two-way data, DataSource controls also support updating, deleting and inserting through other properties - again with parameters like before. For a lot more information, look at the excellent MSDN article on databinding in ASP.NET Whidbey by Early & Adopter.

    So this is one of the reasons I'm pretty impressed with ASP.NET 2.0. More reasons are bound to follow - stay tuned :-)

    Wednesday, April 28, 2004 9:59:20 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Server Explorer On Steroids#

    As you can see they've added a whole bunch of things in the "Server Explorer" window in Visual Studio .NET (Whidbey): basically all your operating system objects are there (processes, treads, accounts, shares, ...). So I guess if you can get to it through WMI then you can find it here. So what used to be a separate download (the WMI Extensions) is now built-in and accessible right from your IDE!

    Pop quiz: what's missing in this picture? Yep. The SQL Server plugin. Fortunately, you can still do most of the things you need (editing stored procedures, looking at table definitions, running ad hoc queries) after creating a Data Connection but I must say I panicked for a second there :-) But I'm sure it'll be back when the beta ships...

    Now let's hope they fixed that ugly memory protection error I get when trying to browse a table (as I said, most of the things...).

    Tuesday, April 27, 2004 4:28:12 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    I've moved to another website!#

    Yep that's right folks, you can point your browsers, aggregators and pretty nieces to http://jelle.druyts.net from now on!

    I've finally decided to move this blog away from my home-grown server with variable uptimes to a dedicated home with a guaranteed uptime of 99.99%! Well that's probably not entirely true given the doubtful uptime reports I've read, but they do host a lot of people I know and I've been hearing pretty good things about 'em so here goes: three cheers for WebHost4Life!

    I'll be crossposting to my old site for a while until the hits on my own machine have dropped below a certain undefined minimum, but don't wait for the actual unplugging to happen - please adapt your bookmarks, newsreaders, permalinks, blogrolls, mental references, whatever, to http://jelle.druyts.net now and I'll be sure to keep serving you my irregular ponderings in return!

    Oh by the way, if you ever decide to move your dasBlog installation across the GMT boundary (e.g. from GMT+2 like where I live, to GMT-7 where my hosting company is based): don't panic if you seem to have lost all of your comments :-) It's a bug I've encountered and described and as long as it hasn't been fixed, just rename all *.dayextra.xml files to the previous day... Don't ask ;-)

    Update: I guess I won't be crossposting to my old site after all. The build of dasBlog I'm using now (or maybe it's a problem in every version, I don't know) seems to have problems with it - it's giving me errors most of the time... Too bad.

    Monday, April 26, 2004 8:50:52 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Setting a general base class for all your ASP.NET pages#

    Since ASP.NET 2.0 is making such an effort to reduce the need for writing actual code, it's now becoming less likely that you will be using code-behind files. It's still possible of course, but if you don't actually create a page class yourself, ASP.NET will just do that for you. And the good thing is, you can just specify the base class it needs to use - so you can slide in your own custom base class in stead of the standard System.Web.UI.Page. Very convenient in scenarios where you're writing framework-like base pages, or if you want to do something extra in every page of your site. Just use the <pages> section in the web.config file as such:

    <pages pageBaseType="Acme.Framework.Web.UI.SharedBasePage">

    You can do the same for controls by the way. Actually, you can do a whole lot of things in that tag, like adding namespaces that need to be imported by default, pre-registering tag prefixes, well basically just managing your pages in a single place. Cool!

    Blog | Programming | .NET | ASP.NET | Whidbey
    Monday, April 26, 2004 2:42:32 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual MemoryLeak .NET 2005#

    I was wondering why my VirtualPC in which I'm running Visual Studio .NET 2005 CTP (f.k.a. Whidbey) got so incredibly slow. I gave the virtual machine 512MB RAM and I only had 2 Visual Studio's open in it (with fairly simple projects), along with an explorer window and a Word document. I did keep the Visual Studio's open for a pretty long time and apparently that's not such a good idea: Windows was using over a gig of memory. One Visual Studio instance took up around 120MB, the other one maxed at 200MB. Talk about a little memory leak... Let's hope this improves in the beta, or - shudder - is it supposed to eat up that amount of memory?

    Blog | Programming | .NET | VS.NET | Whidbey
    Tuesday, April 20, 2004 3:29:27 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    ASP.NET & XAML - I'm so slow#

    I just made the most obvious observation: ASP.NET 2.0 and XAML are mother and daughter. And their hereditary determined angle-bracket beauty will only be increasing by the laws of natural selection.

    Although that shouldn't come as a surprise at all since it's often been said that "XAML is to WinForms what ASP.NET was to the web". I just hadn't had the physical real-world click just until now. (Of course it's already been shown extensively that XAML isn't really tied to WinForms at all, but it's probably still the most useful scenario for it.)

    So what triggered the click? I just noticed that some ASP.NET controls provide a kind of nested properties like <SomeControl Property-NestedProperty="[value]" />, e.g. <GridView HeaderStyle-Font-Bold="True" />. That's just like the nested properties syntax in XAML (only here they're using a dash in stead of a dot). I don't think the syntax here is as universally supported (I've only seen it with a number of properties, mostly to do with styles) but it's conceptually the same.

    Another thing that struck me is the template mechanism; it's been there since the beginning really, but in fact it's just a way to attach template objects to controls (like grids) without using code. In other words: declarative object coupling.

    So to realize their 70% code reduction claims they're providing declarative nested attributes and object coupling - exactly what XAML is so damn good at… I should've seen this long ago, I'm so slow...

    Monday, April 19, 2004 1:19:22 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    I'm a supermodel now!#

    Hehe, I just got this in my mailbox and it cracked me up... Great work doggi, I didn't know you were a fancy graphics artist as well - since we all know Java and graphics are a bad match ;-)

    Friday, April 16, 2004 9:26:14 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Public Referrers & Google-Trash#

    A while ago, I wrote a simple dasBlog hack to show my referrers (use the links on the site's navigation bar or watch my public referrers directly). Works great, but there's one small issue with it... I'm getting a lot of google-trash on my site lately, and it's snowballing so I'm going to have to try and halt that.

    Somehow, sometime, someone came up to my site with the word 'kcarc' (spelled backwards to try and prevent the problem you're about to discover :-) ) somewhere in his google referral. So that shows up in my public referrers page. No big deal, since I have a robots.txt file that tells google and co to bluntly ignore that page instead of indexing it.

    Unfortunately, either I was too late, or something's wrong with my robots.txt file (it validates though) or google isn't playing nice - because it did get indexed. So looking for that specific word might get you here. And get your search query listed on my referrers page. And getting that indexed as well. And drawing more trashy traffic... And it's been snowballing ever since, because now I'm getting more and more referrals that come looking for "free-version-patches" of their favorite software (which I don't provide, as you might have noticed around here, it's not that kind of technology site).

    So I hacked my hack to exclude all referrers that contain the dreaded c-word... Hope that'll help to keep my referrer logs clean. I wouldn't want to close the page down, they're too much fun to read once in a while really :-)

    Update: I added some more words that needed to be blocked (I feel a new hobby coming up), and I've also removed the google and feedster queries from the main referrer url list (since they're already listed below) which makes the top list a lot cleaner. Cool (if I say so myself)!

    Thursday, April 15, 2004 11:48:58 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Some more info on Microsoft WeFly247.NET#

    Microsoft has been building some sample applications to showcase .NET through the Learn247.NET site. Former projects are Football247.NET and WeRock247.NET that focused particularly on Visual Studio 2003 and Smart Clients respectively. You can order the DVD's for these for free on the site, by the way, so if you're interested in some free goodies... :-) These projects are accompanied by labs presentations, a community website, and a Microsoft tour through EMEA to promote all of this.

    The latest project - WeFly247.NET - (which, as you could have guessed or known by now, I'm on; me being half of the development team) is going to focus on Visual Studio 2005 (a.k.a. "Whidbey"), using an imaginary airline to show a lot of enhancements in the upcoming version of .NET. Rather than sum it all up, I'll just refer to the introductory post to WeFly247.NET on the community forums. The intention is that we use that site to keep "the community" (a.k.a.: you!) up to speed on what we're doing (e.g. weekly builds, blogs, documentation, ...) and get them excited to ask questions and give feedback and build extensions, so if you're interested: be sure to keep an eye on this site!

    I'll keep blogging about my experiences building the apps here, and on the community site as well once we get the blogs up and running over there.

    Thursday, April 15, 2004 11:15:28 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Inheritance modelling in a relational database#

    I'm modelling the database for the WeFly247.NET project, and a small issue came up so I thought I'd share the problem and proposed solution with you... Ideally, because you're way smarter and know of a much better way to do this of course :-)

    It basically has to do with inheritance hierarchies in a relational database, and I found a pretty good introduction to mapping objects to tables on the Object Architects site but I'll just limit myself to the problem at hand here.

    Imagine a SeatServiceRequests table in which all passenger requests on a flight are stored. The list of things you can request is in the SeatServiceItems table; these include a range of drinks, books, papers, pillows, blankets, "special attention" by the flight attendant ;-), ... and belong to certain categories (Drinks, Reading Material, Comfort, ...). In another use case, you can schedule a meal to be delivered to you and choose a drink to go with that. So the MealRequests table needs a link to the SeatServiceItems - but here's the tricky part: only drinks are allowed here (logically). You can't drink a newspaper with your meal (at least not if we can stop you from trying).

    There are options to enforce the constraint that a requested SeatServiceItem must be a drink of course, such as checking the item's category in the business logic, or using a trigger to check the constraint when a new row is added in the MealRequests table. However, that makes it an 'implicit' constraint: it's not modelled in the ERD and not really enforced by the database itself:

    So that doesn't make me very happy...

    The problem, in fact, is that we have sort of an inheritance relationship between a SeatServiceItem and a Drink - a Drink is a SeatServiceItem - but I didn't want to make the database too complex and go about one of the full object-to-table mapping schemes here.

    One thought was to create a "Drinks" view over the SeatServiceItems table so that only items from the "Drinks" category are shown. That way we can link the MealRequests table to the Drinks view. Unfortunately, SQL Server doesn't support relationships between tables and views so we can't use that to model the relationship or to enforce the constraint.

    So I took this up with a colleague who proposed a pretty simple solution for this (thanks, doggi :-) ). Just add an extra "Drinks" table containing the primary keys of the SeatServiceItems that are in fact drinks (so basically this is an emulated view on the SeatServiceItems). Now you can establish all relationships without problems.

    It's not as elegant as I'd want it to be (when working with drinks, you have 2 tables to maintain) but it beats the 'implicit' constraint idea so I guess I'll be using this solution. What do you think? Any issues with this, any other ideas?

    Wednesday, April 14, 2004 3:21:38 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    MVP Global Summit - I want in!#

    I'm hearing a lot of stuff lately about the MVP Global Summit that just took place. Seems like a really cool place to be if you're into the whole Borg / Koolaid thing (and I am, I gotta admit).

    So here's my mildly overdue new year's resolution: I want to be an MVP by next year so I can join in on the party :-)

    Actually, I don't really know what I have to do to become an elected MVP but when all else fails, I'll just crash the MVP party like Rory did :-)

    Friday, April 09, 2004 5:50:47 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Trackback between dasBlog and .TEXT not working?#

    I've seen this before when posting my answer to Brad Abrams' NullReferenceException question, and I just noticed it again while linking to Dave's post about WeFly247: trackbacks don't seem to show up in a .TEXT blog when I set a trackback url from dasBlog.

    I do get a dasBlog event that the referral was received:

    Info ItemReferralReceived:
    Item referral received for local item PermaLink.aspx?guid=66d46880-96e1-43d7-9e38-a992fac63aa4 from target http://dottext.com/Services/default.htm

    But it never actually shows up in the target blog's comments section... Does anybody know if this is a bug or if I'm doing anything wrong??

    Friday, April 09, 2004 1:44:22 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    WeFly247: A piece of the lid just came off#

    I just noticed on Dave Sussman's blog that he's already mentioning the WeFly247 project. I didn't even know we were allowed to talk about it but I guess just linking to someone who does should get me off the hook ;-) Dave is the guy who wrote a lot of books on ASP.NET and other stuff so he sure knows his stuff.

    So as you might have guessed, I'm on the project as well. I flew over to Reading last week for a day to attend the kickoff meeting and as you can see in his post it's going to be a really cool project, and I'm really excited to be working on it! It's also going to be very much in the open so that's both scaring and exciting.

    I'm at the absolute bottom of the food-chain on this project (as the lower half of the "dev team") but I wouldn't want to be anywhere else with all the .NET goodness coming up in 2.0 really :-)

    So stay tuned, we'll be showing you lots and lots of great Whidbey stuff in the coming weeks!

    Friday, April 09, 2004 1:09:10 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Whidbey Impressions#

    I've finally started working on the new Whidbey build. Some marketing-friendly people called it CTP (Community Tech Preview), people who like uniformly-rising integers might call it PD4 (Partner Drop 4) but real coders refer to it as "2.0.40301" of course. I've already had it added it to my list of friendly runtimes (don't worry, I still have enough tattoo-space left for years to come).

    The first big thing I noticed was that Whitehorse still isn't there. The goodness of having a round-trip engineering class designer, a uml modeller, a network layout modeller, ... all within your IDE (emphasis on the 'I' here) will have to wait a little longer I guess. This means I have to go back to Visio to model my use cases and database diagrams for my current project. It's not that Visio is a bad product or anything, it just sucks that's all. It's not integrated, it doesn't look as good, it feels old and clumsy, and I hate the way it does some of its layouting (especially connectors are terrible, and too hard to correct yourself).

    A nice little improvement on the Error Reporting dialog (aka crash-o-spam-a-thon, I always send error reports you know :-) ) caught my attention (actually, it's not a good thing that I noticed this, since I was hoping not to encounter the dialog altogether but well, it's still alpha software of course :-) ): there's now an option that makes the dialog close itself after the error report has been sent. Thanks, it's the little things that annoy you the most you know. They should really put this in the general Windows error reporting dialog as well (I would have figured it was actually the same component but apparently that's not the case).

    I got an interesting exception when trying to view a project's properties (officially I'm pretty puzzled how I'm going to manage without project properties but still): "The type serialized in the .resources file was not the same type that the .resources file said it contained. Expected 'System.Drawing.Size' but read 'System.Drawing.Size'". Normally when I get this kind of warning, I'd expect some kind of versioning problem here. But within Visual Studio? Uh-oh...

    Update: the Project Properties are working again... There seemed to be a problem with the keyfile causing the build to fail which apparently also prevented the project properties page from loading. I removed the keyfile tag in the .csproj file (xml and msbuild rule) and all is well again.

    Now I'm in even more trouble: the new Pocket PC 2003 emulator doesn't seem to do a lot. I doesn't power up and freezes my Visual Studio... Deploying it to the "legacy" emulator does seem to work though, and what's pretty cool is that it gives you these confirmation dialogs to replace "mscoree.dll" so I'm really patching the emulator to the Whidbey build of the .NET CF 2.0. Ah wait a second, fortunately already an update on this: the new emulator does work! You just have to wait a long while when powering it on. It takes a minute before the Windows Mobile splash screen pops up, then you have to wait even longer for the .NET CF 2.0 to be installed, the application itself launches slow, but it works fine and I still think the whole concept rocks :-)

    The Object Browser window doesn't seem to have the new view yet where you can see a class definition as an emtpy code file, too bad because I kind of like the idea: it's more natural as a programmer to view a type contract as an actual code block. You can however get a glimpse of how it will look using the (buggy) Windows Forms Class Viewer tool (WinCV.exe in the SDK 2.0 dir).

    Update: It's not in the Object Browser window but it's there: View - Other Windows - Code Definition View. This gives you a window that shows a code block for whatever class happens to be under your caret. Cool!

    Oh one more thing before I actually get back to work: wear sunglasses when coding. The component tray (you know, the region where your non-visual components like tooltips and extenders are shown in the designer) is in eye-bashing purple. So are the gridlines in the task list. Damn, they must have fired all the people with a working set of eyes over there...

    Blog | Programming | .NET | ASP.NET | VS.NET | WeFly247 | Whidbey | WinForms
    Thursday, April 08, 2004 3:10:35 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Hot news: Longhorn might support blah!#

    Of course, there's no blah without quuz, so it seems they'll be doing all it takes in Longhorn to get that too.

    Ok so I have a little question for Chris: Why the hell would you need quuz to do some blah? Seriously, I've been quuzing for a few months now and it didn't get me anywhere near blah or even close to foo. I'm so confused... Maybe my latest shmaz is barred :-(

    Monday, March 29, 2004 9:29:06 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    An Outlook Annoyance Solved#

    Ok so with the end of my Lotus Notes era-of-hell in sight (since I'll be finishing my current project soon), I'll be returning to my trusted friend Outlook again shortly. I've always experienced a little annoyance with it, but I've just found a simple solution so I just figured I'd share that with you (you never know if one you - my trusted share of thousands of readers - suffers the same problem).

    It's something really simple actually: I just wanted a weekly reminder. That's it. The problem is, I didn't get my reminders to work correctly... I created a Task in Outlook and made it recurring (every week on the same day). Then I set a reminder for it, on a certain time of the first occurrence of the day. When that reminder popped up on the right day, I typically clicked snooze and forgot all about it for a little while ;-) but at sometime when I was done with it, I clicked "Dismiss" to indicate my weekly chore had been fulfilled. But dismissing it also seemed to disable the reminder, so I wasn't notified about the task the week after. Bummer. I could've clicked "Snooze for a week" but then I'd have some serious time-shifting going on: each time I hesitated or snoozed for a little while, the reminder would just come later and later each week.

    I guess I could've gone off and programmed it myself using .NET into Outlook through Visual Studio Toolkit for Office or something, but then again - where would we be without our friend the internet and Microsoft Office Online? Apparently, the solution doen't lie in creating a weekly Task, but instead you should be creating a recurring zero-minute-length calendar appointment. Not as pretty as it should be, but it does the job.

    Blog | General | Programming | .NET | Windows
    Saturday, March 27, 2004 7:04:08 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Good stuff coming up!#

    It's been pretty quiet here, I know, but I'm finishing up my current project so there's just not a lot of interesting stuff going on. The things I've been pretty involved with lately and that I really do want to share with you are just too big to post here so I'm cooking something up for a slightly different distribution channel - stay tuned :-)

    Besides, it seems somebody heard my desperate cry for new Whidbey goodies: my next project will be all about Whidbey and Yukon! So that's gonna kick some serious bootay; I just hope I won't be under NDA or anything because I can't wait to explore this goodness and share the drool with you :-)

    Saturday, March 27, 2004 1:54:28 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    WSE 2.0 introduces new kind of versioning issue in .NET#

    "WSE 2.0 is now Microsoft.Web.Services2 and the config section is called <microsoft.web.services2>." [Via Early Adopter]

    "WSE major versions are not simply incremental feature additions, they are different libraries. As such, they deserve different names." Sure, there are some good technical reasons to append the version number to the namespaces, dll's and config sections, but I don't like the smell of this. Stuff like "msxml.dll, msxml2.dll, msxml3.dll, msxml4.dll" springs to mind. Granted, those are libraries with incremental feature additions - they're not simply "different libraries" as is the case here.

    It's sort of like calling your successor product "NT" (which in Windows-land stands for New Technology - or doesn't it?), "Next-Generation", or "The Revenge" for that matter.

    I'm not calling this change dll hell, but it still feels icky. I wish that .NET would even have nice solutions for these kinds of "higher-level" versioning issues.

    Tuesday, March 16, 2004 8:17:12 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    OneShotFlag#

    If you've ever written some test code before (you do write unit tests, don't you?), you probably ran into the common code construct that you initialize a "success" flag to a certain value so you can toggle its state later on. I just wrote a little utility class to guard you from making mistakes here: a boolean-like flag that can only change its state (value) once.

    Normally, you would initialize a boolean flag to the expected outcome and then only change that flag if one of the tests failed. You can't set the flag directly everytime because that can reset the value when it should not be reset (e.g. if one test succeeds then the failure of the previous tests would be "forgotten").

    Bad code (the result of the first test won't ever matter):

    bool success = true; // We expect this to work.
    success = !HasFirstTestSucceeded();
    success = !HasSecondTestSucceeded();
    // ...
    if( success ) Party();
    

    Better code (semantically correct but cumbersome):

    bool success = true; // We expect this to work.
    if( !HasFirstTestSucceeded() ) success = false; // Only change the value if necessary.
    if( !HasSecondTestSucceeded() ) success = false;
    // ...
    if( success ) Party();

    So you have to write code that only changes the flag if it differs from its initial state. This is just where the OneShotFlag class helps: it only allows the flag to be changed only once. Furthermore, it can implicitly be converted to a boolean so checking its state is just as easy.

    But it becomes more natural like this:

    OneShotFlag success = new OneShotFlag( true ); // We expect this to work.
    success.State = HasFirstTestSucceeded(); // Change the value directly.
    success.State = HasSecondTestSucceeded();
    // ...
    if( success ) Party(); // Note the implicit conversion to a boolean here!
    

    Here's the code for the class:

    public class OneShotFlag
    {
     /// <summary>
     /// The current state of the flag.
     /// </summary>
     private bool m_State;
     /// <summary>
     /// Indicates if the flag has already changed its value once.
     /// </summary>
     private bool m_ShotFired;
     /// <summary>
     /// Creates a new <see cref="OneShotFlag"/>.
     /// </summary>
     /// <param name="initialState">The initial <see cref="State"/> of the flag.</param>
     public OneShotFlag( bool initialState )
     {
      m_ShotFired = false;
      m_State = initialState;
     }
     /// <summary>
     /// Gets or sets the current state of the flag. The state will effectively be changed only once.
     /// </summary>
     public bool State
     {
      get
      {
       return m_State;
      }
      set
      {
       if( m_State != value && !m_ShotFired )
       {
        lock( this )
        {
         m_State = value;
         m_ShotFired = true;
        }
       }
      }
     }
     /// <summary>
     /// Requests to change the state. The state will effectively be changed only once.
     /// </summary>
     public void ToggleState()
     {
      State = !State;
     }
     /// <summary>
     /// Implicitly converts the given OneShotFlag to a boolean value.
     /// </summary>
     /// <param name="flag">The OneShotFlag to convert.</param>
     /// <returns>The current <see cref="State"/> of the OneShotFlag.</returns>
     public static implicit operator bool( OneShotFlag flag )
     {
      return flag.State;
     }
    }
    

    Sunday, March 14, 2004 11:40:01 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Whidbey Delayed - You Bastards!#

    ZDNet brings us the sad news that Microsoft delayed Whidbey and Yukon until the first half of 2005. You utter, utter bastards! I want this stuff now! Actually, I do have alpha bits from the PDC of course, but they're wearing off ;-) And I have a later build but that's pretty unstable. I want some new goodies and even the beta doesn't seem to be coming any time soon... Somebody ease my pain!

    Blog | Programming | .NET | Whidbey | PDC03
    Thursday, March 11, 2004 9:12:38 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    NoInstanceExceptions#

    Heh, doggi sees my call that Java's NullPointerException is a ridiculous name for a pointerless language, and comes up with an AOP solution to replace all NullPointerExceptions with NoInstanceExceptions. Far out. A little far-fetched too, but still, far out :-)

    "Warning: this is just for academic purposes, not intended for production systems...", yeah 'coz you'd really be tempted to use this hack in a live environment ;-)

    Pretty powerful stuff you can do with AspectJ by the way. Can't wait to see an equivalent AOP framework emerge on the .NET side of things, there's just so much good stuff you can do with that kind of power.

    Blog | Programming | .NET | Java
    Tuesday, March 02, 2004 8:48:21 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Longhorn Request: Secure & Transactional FTP#

    With WinFS coming up, this one should be a breeze on the upcoming Windows platform (which will go unnamed for a change to further lessen that diabolic (but oh so just) hype): I would like to see FTP grow up (*). And by growing up I mean become more than a protocol to shake hands and throw bits at each other until you run out of ammo. I want to see it become (at least) transactional and secure.

    "Transactional" because if you're using it to transfer a whole website (still one of the most widely used reasons to use FTP in the first place - apart from that "legal" form of "datasharing" any ignorant judge would call "piracy" of course), you want either the whole thing to succeed or fail. You don't want half of your site reflecting the fact that your company just went bankrupt while the other half is still pretending to be alive and kicking while throwing fast-climbing sales graphs in your face to prove that point. You also don't want to see any changes to your site halfway through the transmission, instead you want the whole batch to be committed in the end - when everything arrived safely and correctly. ACID baby, yeah.

    "Secure" because right now, I just don't trust plain FTP. I don't use it at work to change my site for example. There are all kinds of initiatives to make FTP secure (like run it over SSL or in an SSH session) but I want this to be the default. Security is not an add-on anymore, it's not optional, it should just "be there", implicitly.

    Of course this reaches beyond Windows into all platforms, but I'm just focusing on Windows now because, well, that's my personal bias. (I'd be interested in knowing how hard it would be to build this in penguin-land though.) The Windows built-in ftp.exe command is a great and simple tool I use regularly and it just seems so incredibly easy in WinFS to make it even better. With WinFS becoming a file system backed by a relational engine, it's intrinsic that you can use transactions. So at the start of your FTP command, you just call WinFS.BeginTransaction (so to speak) and do a WinFS.Commit() in the end. And with Indigo and its secure messaging features, why not use 'services' as a higher-level layer upon merely sending bits across the wire? Overhead? Sure! But I'd like to see you perform security, routing, and even transactions on the wire-level yourself without using Indigo. Don't want to? Keep it simple and unsafe? Good luck coming up with more fast-climbing sales graphs...

    (*) Actually, I would like to see the whole web grow up. Starting with the slashdot community ;-) No really, the current state of the web is like a badly designed medieval castle you just keep throwing bricks at to keep it from falling down. Browsers can't keep up with modern-day webapplication requirements like obvious state- and usermanagement (cookies and ASP.NET viewstate being obviously working but ugly hacks), emails still disappear from my inbox all too regularly or never get there at all, RSS still having no other central distribution mechanism than being pull in stead of push, TCP being a hard protocol because it tries to be reliable over an intrinsically unreliable IP-connection, ... So I'm not waiting for IPV6 really, I'd bet my money on TheWeb 2.0. (Yes, I am aware that this last remark isn't remotely realistic thank you very much. But it would be nice to have a band of modern-day architects design a whole new future-proof web though. If only for the kick of it.)

    Tuesday, February 24, 2004 11:49:06 PM (Romance Standard Time, UTC+01:00) #    Comments [6]  | 

     

    NullReferenceExceptions#

    BradA poses an interesting question in his post on NullReferenceExceptions: "What if you have an instance method that does not touch any instance state (for example an instance method that just does a Console.WriteLine (“hello“)). Will you get a NullReferenceException when you call that?".

    Off the top of my hat, I'd say it should be possible from the CLR perspective to call instance methods on null objects. As Brad says: a NullReferenceException occurs when you access instance state, i.e. when you use the implicit this reference that is passed in every instance method. If your method never uses that reference, I'd say you have no problem.

    So I just tried that and it doesn't seem to work though (as I could have expected but still). Imagine a TestClass with a DoTest method that just writes a line to the console. If you look at the IL the C# compiler built for us, it says:

    .locals (TestClass V_0)
    L_0000: ldnull
    L_0001: stloc.0
    L_0002: ldloc.0
    L_0003: callvirt TestClass.DoTest
    

    As you see it just calls a method, the null reference is on the stack ready to be used as the this parameter, but it won't be used by the method call. So I'd say: rock on. But the runtime says: curtains close. Maybe it has something to do with the fact that the C# compiler always emits a virtual call (callvirt) even though I made TestClass sealed. And then it goes off looking for the object header to find the pointers to the baseclass or something, failing because there's no object header since there's no instance? I don't know, I'd have to dig deeper into the internals to find out but I think I'm on the right track here...

    By the way, I've always loved the name "NullReferenceException" in sharp contrast to Java's "NullPointerException". Made me chuckle when I first saw that :-D

    Update: as BradA pointed out in my comments, I seemed to be on the right track indeed. So I took his advice and changed the "callvirt" into a regular "call" and - lo and behold - it works! (So I'm asking "nobody" to do something for me and got away with it. I should try this for my dishes...)

    A quick peek at Don Box's excellent Essential .NET Volume 1 (page 158-159) tells me why: a non-virtual call is just a jump to the exact place in memory where the JIT compiler placed the native code. No magic required. A virtual call however (which the C# compiler always creates, and I think somewhere in that book Don explains why that is - although I can't seem to remember right now...) will use the object's RuntimeTypeHandle to determine the proper method to call. Well, a RuntimeTypeHandle is instance state - which won't work on null objects. And so it fails. Yep, that about feels right...

    Update: I won - woohoo :-) Thanks for the 2 .NET t-shirts and the note, Brad. Although I must say, I was really hoping for a job offer ;-)

    Monday, February 23, 2004 4:50:46 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    The Ultimate Snake 2 Highscore (no, really)#

    I'll start out by admitting it: yes, that's really sad. Like, playing-minesweeper-all-day-long-kinda-sad. But still. Try to beat me. Standard single player Snake 2 game on a Nokia, level 3, without a maze or anything. 2384 points. You heard me, 2384 points! Damn, if it wasn't so pathetic I'd be proud of it. And just to prove it, here's the highscore screen:

    And this is how it looked just before I took that last piece of snake grub and ate myself to death:

    If anybody cares (I guess that would be a so-called rhetorical question): here are the Snake 2 specs, in case you have an itch to program it yourself :-)

    • The playing field is 13 by 33 squares.
    • Normal food (what are those, mice? I don't know, what do snakes eat out in the wild? and did the makers really study them before programming it? anyway...) is 3 points plus the fact that your tail grows by one square.
    • After 5 pieces of this diet-stuff, you get a big one. If you eat some more normal food in the meantime, that doesn't count for the 5 lighter pieces, so it's not a good idea to go and eat the petty food if you're out hunting for real meat.
    • That bigger vermin is where you get the real work done: 20 points plus 2 points per second left on your counter. And don't forget you get an extra square on your tail length too.
    • Then there's a tricky one, somewhere near the end (I think when there's around 10 open squares left), the bigger vermin stops coming. Too bad, that would really kick the scores up :-)

    Maybe somebody could make a smart client Avalon (don't hurry, you have the time) version with full vector-based graphics (yes! wallsize screens running Snake!), User Experience (alert me on my cellphone if somebody beats my highscore), Indigo (realtime multiplayer gaming) and WinFS (to store settings, highscores, and be able to search game statistics in no time). *snap*

    Sorry 'bout that :-)

    Saturday, February 21, 2004 6:09:09 PM (Romance Standard Time, UTC+01:00) #    Comments [4]  | 

     

    SharpToolbox#

    SharpToolbox is a "comprehensive list of development tools for .NET". Looks really great, lots of stuff on there and you can search by category or by attributes ("gimme everything that's free"). And it even has rss feeds! How come I didn't find this sooner...

    (Hmm and it's also hosted on Webhost4life, same as Rory and Tom as I've noticed. If I'd decide to actually host my site somewhere (like, on a real server) I guess I'll go there. Looks like a pretty great service.)

    Friday, February 20, 2004 11:21:30 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    .NET To Java Compiler?!#

    Whow, this looks pretty awesome: Visual MainWin For J2EE from Mainsoft features a compiler that can translate .NET Intermediate Language to Java bytecode. [Via Miguel]

    Think about this. No really. I just don't get it. Surely it's not able to compile any .NET code to Java, right? That would render Mono pretty useless, since Java is free and runs on any platform. And seeing that they're using parts of Mono themselves, I guess that kind of power is out of the question. So since their (overly simplified) goal is to map ASP.NET to J2EE and ADO.NET to EJB, maybe there are limits to what you can compile on their platform. But if they got this far, why not go all the way?

    I'm pretty curious how this works, it's bound to be pretty nifty since it's patent-pending (although being nifty certainly ins't a prerequisite for filing patents). Too bad they won't open the can a little so we can have a peek inside. The whitepaper just doesn't satisfy me enough ;-)

    Blog | Programming | .NET | Java
    Tuesday, February 17, 2004 4:51:50 PM (Romance Standard Time, UTC+01:00) #    Comments [6]  | 

     

    MoodPrompting#

    I got this while installing my latest NVidia driver. I really dig this style of prompts:

    No hassle, it comes to the point immediately, gives you simple options. I went for "Yes", I don't know why. I guess I was just in a "Yes" kind of mood.

    Maybe it was silently asking "Should I make your screen look better?", "Shall I get you out of these wet clothes?" or just a plain old "Do you feel allright?" - I'll never know. But that's not the point. I'm sure the programmer's intention was to make it a kind of self-reflecting metaphysical question driving you to deeper contemplation on the meaning of life. A job well done I might add.

    But I'm still soaking wet.

    Thursday, February 12, 2004 10:58:11 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Killer Font#

    Anyone else think this is funny?

    A critical update for a font? What are these "unacceptable" symbols gonna do? Call your mama names? Give you a wedgy? Shout racist insults at the local grocery shop owner and then steal a banana? Or maybe spell ".NET" as "J-a-v-a"? Oooooh now that would be unacceptable!

    It even seems so dangerous that you may have to restart your computer - just to make sure that evil font has really left no backstabbing trace in your memory whatsoever. I guess it'll have something to do with national security or something. Run for cover!

    Wednesday, February 11, 2004 11:41:25 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    TheServerSide.NET Disappointing?#

    Anyone else think that TheServerSide.NET isn't really living up to its expectations? It got all this momentum when it started, but I've rarely seen anything really interesting going on there. I don't know, maybe it's me but it's just not "doing" it for me. Just like Slashdot really, I've subscribed to that too but most of the time I just scan the subjects diagonally and Mark All As Read... (Well, also because I don't really respect the pretty childish bash-bash-bash community spirit they got going over there.) Unlike Scoble, who posts a lot more but of whom I pretty much read everything he posts. Well that's before he got into this new "Scoble Light"-mode anyway, I don't know if it's going to be better this way but I must say I'm looking forward to coming home from a weekend away and not finding a few hundred unread posts :-)

    Tuesday, February 10, 2004 9:52:07 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    An Outlook Loveletter#

    Take it from me: Lotus Notes is a product that deserves a quick and silent death. I'm currently on a project with a large company, the first I've been where they're using Lotus all over the place. And we hates it, don't we? Yes we do... Let me elaborate for a second or 2(000)...

    <rant>

    You launch the program. You get a password box. Fair enough, Outlook users are spoiled because it uses your Windows account to log on. But the fact you have to log in every time eliminates the possibility for me to launch it only when there's new mail (there's a separate little tray app that can check your email) because it slows down the process of quickly checking your email too much. Ok so the program is always running, and always taking space up my taskbar. I'd minimize it to the tray (sorry, notification area) using PowerMenu, but then the icon gets turned into this default windows app icon (same happens to Opera by the way, so I can't see the difference).

    But anyway, once you're actually logged in, you want to go off and send an email of course. Just to make sure this thing really works. So you're sweeping the screen for a "New Email" button but you must have missed it. Sweep again. Then you start thinking, it's not gonna be that "New Memo" button is it? Click it. Now that looks remarkably like composing a new email. Come on, "New Memo"? I'm not creating a memo, I'm not gonna print it out and put it on the freakin' fridge. What's wrong with "New Email"? Except when they actually do use the word (consistency is not their middle name) they still call it "eMail" (note the casing) - as if it's still an enhancement to regular mail. It's an actual word these days guys, please.

    Now let's talk GUI conventions for a second. You know, that's the user interface stuff that everybody does more or less in the same manner so we get a consistent (whoops I said the c-word again) look and feel across the entire platform. But I've been told that IBM made up its own set of GUI conventions over the years and clings on to them like hungry mice to a hairy piece of cheese. Just a few examples:

    • Notes behaves sort of like a tabbed MDI environment: you can have multiple "windows" open but they're on separate tabs. You can click a tab header to switch to another window, and you can close it by clicking the 'X'. Great, except that the 'X' is on every tab header itself, in stead of one 'X' on the right of the tab header so you can just keep clicking to close multiple windows.
    • Getting help when you hover over something is really nice, and that's what status bars are used for normally. But in Notes, they conveniently use the title bar of the window. Well, 'use' it is an overstatement: they just paint a grey box over it and put the text in there.
    • If you look at the Inbox, you see a list of emails - sorry, memo's. Selecting multiple emails at a time is possible, but not with ctrl (single select) and shift (multiselect) like you're used to: shift is single select, ctrl does nothing. Dragging in the list to select a bunch of mails also doesn't work.
    • The calendar is buggy when you're scrolling with the wheelmouse: in stead of moving to another month (or something else that makes sense), it just moves the calendar up and down. And fails to redraw correctly.
    • The menus are totally screwed, I'll just walk you through an example for the hell of it. Say you want to create a new folder in your inbox, you right click the inbox folder and expect a context menu, right? Wrong! That would be too easy, in stead you have to go to the "Create" menu and choose "Folder", and then the place where you want to create it in. To delete a folder, it gets even more complicated. Select the folder, go to "Actions", choose "Folder Options", and then "Delete Folder". Note that it's a related task but it winds up in a totally different place. What does that "context" in "context menu" stand for again? Another example: if you'd ever want to mark an item as unread, again don't go looking for a context menu (well there is one but it would make too much sense to put the command in there of course). You have to go to the "Edit" menu, select "Unread Marks" and then "Mark Selected Unread". Rock on.

    You want some more? As long as I'm ranting...

    Well it's just not an intuitive program to work with. For example, there's a Trash and an Autotrash button. What's up with that? I don't even want to know the difference, I just want to get some item out of my sight and have it be done with. For all I care autotrash seems to work in my inbox, and trash works in my outbox. Whatever.

    Moving items is possible (whew) but sent items cannot be moved, only copied. Ok so you copy and trash the original, right? Works great until you find that the copies are also deleted - that is sooo not funny. Same goes for calendar entries, meeting requests should get out of my sight when the meeting is scheduled but they sit quietly in my inbox. Until I delete them - but wait! That removes the calendar entry! The joy of missing a meeting... And alarm options, really, what's up with that? By default, a calendar entry doesn't have an alarm set to remind you of it. In stead, you have to open the calendar entry, edit document, click Alarm Options, enable alarm, close dialog box, save document. Holy crap how productive!

    Replying is funny though, really. You don't actually reply; I mean, you could but you'll soon find out that's not what you want to do. The Reply button is still there for when you want to save a few bytes for your 14k modem actually (what, some people have broadband these days...?) because it omits the original message altogether. So you press "Reply With History" (ah so now we're getting lessons in history?). Great - except if you want to reply to everybody. Then you have to press an additional Reply To All button in the new window. What is this, are Lotus programmers paid by user mouseclick or something?

    Finally, to make sure nobody goes off using a competitive product, they've made very sure that you can't export anything out of the program in any useful way. Well there is some kind of crappy text format, but don't even think about importing that into another program. So there's no separate email files (.eml), no Outlook personal folders (.pst), no nothing. Except if you buy expensive third-party tools of course. (They rub each others backs just to make sure their little eco-system holds together I guess.)

    Oh, and 16-color icons really, really give an enormous performance boost on my system. Thanks for not overloading me with all this shmancy-fancy 32-bit color GPU-eating overkill.

    We wants Outlook back, Outlook, my precious!

    </rant>

    Aaaaah that felt good. (Bear in mind that I've been using Outlook for years so you might say I'm slightly biased towards the Microsoft way of using a computer and an email client in particular - but in the end, aren't most of us? Really?)

    Update: I really wonder what they're going to do when Avalon comes along. With all this rich functionality and user experience goodness that gets unleashed - will they make the switch? Or will they stay behind and (random prediction) lose their market share to remain only in the companies where NT4 is still running by the time Longhorn ships? I really wonder...

    Tuesday, February 10, 2004 1:03:01 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Two Things About Website Usability#

    Ok so I'm not a usability expert on adding all kinds of semantic tags to the html for people with reading problems and such. I've read about it, I've seen it in action, and I understand the point and the need, but these things go pretty far and deep. So for blogs (to name an obvious example) I actually expect the engines and their templates to take care of this for me. My previous engine (nucleus) was pretty good at this really. But there are two few things that I explicitly do pay attention to myself just because there's fairly easy to implement or because it just makes the world a better place. (Right. Like any of this really matters. Anyway.)

    One of them has to do with the regular expression I talked about in my blog migration post. Thanks to all of you for playing along with your numerous comments by the way ;-) The point is, when I'm browsing I want to know if a link will take me someplace outside the current site and if it will open a new window. As a precaution, I wind up right-clicking and choosing "Open in New Window" far too much... So I pumped this simple expression into dasBlog to replace all occurences of href="?(?<expr>http[^"\s]+)"? with href="${expr}" class="external" target="_blank". What this does is just take all hyperlink references that point to a place starting with http and add the mentioned class and target attributes to them. Indicating that it's an external link is done with the CSS class "external", which renders an external link differently than an internal one (hover over the nucleus link and the link to my other post above and note the difference). Opening a link in a new window is done by using the _blank target as you may know. Now this works on two simple assumptions:

    • All links within my site should have relative url's (so they don't start with http). Of course I could just as easily extend the regular expression to ignore my own url but this is sort of a mental reminder to keep my site free of absolute urls (in case I decide to change my root url someday)...
    • Every link to an external site should open in a new window. Leaving my site is your choice, not mine, so I leave it up to you to close the window :-)

    By the way, the HTML 4.01 specification defines a rel attribute on a hyperlink that indicates the relationship between the document and the link target. Unfortunately it doesn't define an "external" link type that would specify the link will take you to an external site. There's an article about "New-Window Links in a Standards-Compliant World" that talks some more about this and provides a javascript that implements the behaviour I'd like to see - but I'd really like the browser to take care of this plumbing (like rendering it differently and having options to always open it in a new window for example). However, most browsers, like IE, don't support the attribute yet :-(

    So that one's automated, the other thing I really try to hold myself to, is having meaningful hyperlinks. I really hate the "click here" style link that I'm certain served its purpose well in the early 90's when pretty much nobody had a clue what to do with a webpage or a link, but since then I think the web has become less about typing and more about writing (at least I hope so). A link should not be breaking your prose, it's not supposed to surface to the sentence flow. It's a way of navigating from one place to another, so the link's text should indicate by itself where it's taking you. A less obvious but nevertheless non-discardable advantage these days (where "knowledge" and "searching" are key, listen to any WinFS talk to convince yourself): while spidering your site, search engines like google can add their search semantics (metadata) to the link you're providing using the link text. So you are adding semantic value to other people's sites - where otherwise, the only semantics come from the site's owner. Pretty powerful I'd say.

    Anyway, I'm off in that zone again, snapping back out of it now... If you got this far, at least you'll know when clicking a link on my site will open a new window ;-)

    Friday, February 06, 2004 3:32:22 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Useless Optimization#

    Rory nails it: optimization is not always a good thing. On today's CPU's, performance is only critical when an actual performance issue stands up, bites you in the knees and gives your grandmother a bad haircut. Hardcore low-level optimizations are a remnant of times when counting bytes actually mattered and everybody worthy of the title 'programmer' could do a 16 bit multiplication with overflow checking in their heads in under a minute. Reversing a string these days... please. Perf hits in today's distributed world linger in network connections, databases and I/O, most of the time not anywhere inside your box unless you're really cranking numbers to achieve world peace. (Modest exaggeration.)

    I'm not saying you can't have performance issues in your code - duh - I'm just totally agreeing with Rory that you shouldn't waste your precious time on anticipating them. Furthermore, compilers and runtimes are way smarter these days and can sometimes optimize parts of your code a lot better than you ever could.

    By the way, why would you want to reverse a string anyway? (Well except when you run elgooG of course.)

    Thursday, February 05, 2004 9:46:18 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual Studio .NET Tips & Tricks#

    There's an article on CodeProject with some Tips and Tricks for the Visual Studio .NET IDE. Some pretty nice ones too, actually. Ok somebody in the comments says it's not an actual article since it's just listing some shortcuts you can find in the Help - but who really does that kind of advanced data mining anyway, right ;-)

    I should really be using some features more, like incremental search, multiple copy/pastes and cursor position cycling. So many shortcuts, so little time.

    Blog | Programming | .NET | VS.NET
    Thursday, February 05, 2004 9:20:16 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Cake anyone?#
    Seriously, if Anders says "you get to have your cake and eat it too" one more time, I'm creaming him. Just like I creamed BillG a few years back ;-)
    Blog | General | Programming | .NET | Whidbey | PDC03
    Tuesday, February 03, 2004 12:44:42 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Public Referrers In DasBlog#

    Cool, I just finished up a quick dasBlog hack to make my referrers publicly available. Check it out in the navigation bar! I also parsed the google referrers to show the actual search they entered in stead of just the url which is less readable. Just because I got curious after Rory's google explorations :-) Furthermore, it's now also possible to show more than only today's referrers, you can specify how many days it should look up.

    For those of you with fat glasses and a wireless router that has toilet-reach: here's how I pulled it off without having to touch the dasBlog codebase... If you look at the dasBlog sourcecode, the aspx pages are extremely simple: they don't contain any markup but are rendered by their code-behind classes. These are defined in the dll that makes up the dasBlog runtime (which I don't want to touch or recompile). Luckily, there's another way to get pages with code-behind without precompiling the sourcefiles: that is to use the Src-tag of an aspx page. This points to a sourcefile which will be JIT-compiled the first time the page is requested. Same goes for user controls.

    Now the referrers in dasBlog are shown in the admin area only, and they're defined in a Referrers.aspx page which just checks security and loads the ReferrersBox.ascx control. So in order to bypass security (i.e. make it public) I needed to have a page with a codebehind that didn't check the security. Hence the PublicReferrers.aspx and PublicReferrers.aspx.cs files: the first one is just a copy of the original one - except that its "Src" tag points to the second file which is a C# source file. This is a copy of the original dasBlog file but with the security part stripped out and the loaded control replaced by PublicReferrersBox.ascx. Fairly simple, right?

    The PublicReferrersBox.ascx is also a copy of the original ReferrersBox.cs but with the ActivityBar stripped out - it's only needed in the admin area. It points to its codebehind file PublicReferrersBox.ascx.cs which is a modified version of the original dasBlog file. So this is basically the code that provides the extra google goodness and the fact that it can show more than one day. I'm not going to bore you with the code, just take a look if you're interested.

    So if you want this on your own blog: go and get your hands dirty recreating these steps. Or better yet: just download the necessary files and copy them in your root dasBlog directory :-)

    PublicReferrers.zip (5,21 KB)

    Blog | General | Programming | .NET | ASP.NET
    Saturday, January 31, 2004 1:50:23 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Migration Step 4: Website#

    Last time, I left off with all services including ASP.NET installed - ready to kick dasBlog into action.

    Installing dasBlog was easy (copy, set directory security, create IIS application, edit config file, done) and I'd already tried it out before to make sure it would be up for the task at hand. That is: run a little more than a blog but make it look and behave more like an actual website.

    So the blog would obviously be the frontpage, since that's the biggest chunk of the site and it's updated most (static frontpages are boring). I started off with the preinstalled 'dasBlog' theme which is pretty nice by itself, and modified that a little bit to suit my needs - you know, play around with the colors, locations, disclaimers, css, that kinda stuff. Then I dropped my OPML containing my blogroll into the site (so you know who I read, social networking baby), and I used the 'navigation links' feature to show a list of the posts I think you might want to read most (just to help you filter out the incredible noise ;-) ).

    What I really like about dasBlog is that it has a severely underdocumented feature called nested categories, which can show categories in the nifty tree you can see on the site (aggregators don't get this kind of fun). So the key to making it look like a website with some non-blog pages is (ab)using these categories to make pages out of them. They'll show up in the category tree though, so I just called the tree a sitemap to fix that :-) But basically it just boils down to adding some 'posts' in 'categories' like 'Projects' and 'Stories' (reminds me of a 'laser' but never mind) and such, and then a sibling feature of dasBlog kicks into action: per-category templates! That means you can make templates defining the page layout per category. So I made a more simple template to get rid of all blog-like formatting like date and time, permalinks, trackbacks, and all that other stuff that makes aunt Jane's head hurt, and assigned those templates to the non-blog pages. Easy enough.

    Finally, I used another cool dasBlog feature: Content Filters. These allow you to replace strings or regular expression matches in your posts with something else. For example, they can replace simple things like smileys you type into image tags which actually show a smiley picture. Okay so that's not Nobel Prize stuff but it works pretty well and I decided to use it for a usability feature. I'll save that for a separate post, but I'll leave the regular expression for you as a pop-quiz: href="?(?http[^"\s]+)"? gets replaced into href="${expr}" class="external" target="_blank". Shouldn't be too hard, right? Don't hold back posting your answer into the comments...

    So basically, I'm very happy with dasBlog. I got everything I wanted out of it, and it didn't mean touching the code. I was tempted at some points to dig in and add some features but I wouldn't want to be unable to upgrade if there's a new version so I sucked it up. So great work Clemens and everyone who contributed!

    Blog | General | Programming | .NET | ASP.NET
    Thursday, January 29, 2004 11:59:23 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    MSBuild In 30 Minutes#

    John Lam gives a great introduction on MSBuild in his PDF newsletter "Practical Eye For The .NET Guy". Pretty clear and interesting even if you're familiar with [N]Ant. (Through Chris Sells' blog)

    Wednesday, January 28, 2004 12:49:09 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Volhouden Clemens!#

    Heh, hearing Clemens Vasters give a talk in Dutch - now that alone should be worth becoming an SDGN member for the upcoming CttM :-)

    Sunday, January 25, 2004 1:44:33 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Events Under The Hood#

    I've already explained how events work in Visual Basic .NET, now I just found out about a rather old article on MSDN about events in C#. Nice look at how you can implement the delegate handling yourself in stead of having the compiler do it for you.

    Wednesday, January 21, 2004 11:09:09 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ASCII Blitting#

    Heh, the Whidbey Console class has a MoveBufferArea method so you can move an entire screen region to a new position in the console at once. I sure hope the folks at marketing won't forget to mention the huge performance benefit of ASCII blitting :-)

    Sunday, January 18, 2004 2:03:56 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ClickOnce Manifest#

    All right so ClickOnce seems to work again: publishing works. But when I try to launch a published program, it gives me a deployment error: "general activation failure".

    Lucky me, it mentions "See the log file for more details". Unfortunately, it doesn't provide a hint as to what log file that is, or where I could find it. So far for extra credit on the "useful errormessages" department - but I remind myself that it's alpha software so I'm not complaining :-) So I just reverted to the IIS logs, and it seems that the browser is trying to fetch a .manifest file for which it gets an HTTP/404 error.

    Adding a new MIME type (.manifest, type text/xml) in IIS seems to do the trick, but I do suppose (i.e. hope) that this works by default on a clean box. So I'm just assuming that it's my setup (which is probably pretty messed up by now, cyclic installations of older and newer builds of various programs can't be too good).

    Friday, January 16, 2004 12:13:15 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Dessert anyone?#

    By now the suspense ("Did the Whidbey reinstall work? Were the Whidbey gnomes really digging holes in the new build?") should be killing you. Otherwise: go fish! ;-)

    Well, only partially I'm afraid. The PDC build of Visual Studio .NET Whidbey installed but with some errors (some packages couldn't be loaded but they don't seem too critical). ClickOnce is working again though, and ASP.NET is looking a lot better.

    It's just that Yukon still isn't running :-( Always fun to watch its transactional installer in action though: you spend an hour looking at a progressbar running towards the light, until it tries to start the MSSQLSERVER service, which fails, and then it just goes about rolling back the whole installation without asking. An hour and a half wel spent!

    Oh well, at least the tiramisu worked out pretty well. Maybe I should consider a career change ;-)

    Friday, January 16, 2004 8:43:10 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Whidbey Trouble#

    Ok so there are a lot of nice things to be said about the new Whidbey build. However, I struggled too hard to get a lot of stuff to work that I really need right now: ClickOnce was broken (publishing failed due to an XmlException on a file which I presume to be the generated deployment descriptor but I couldn't tell), ASP.NET acted weird (personalization didn't work for instance, it wouldn't even recognize the personalization tag in web.config), and I tried installing the PDC build of SQL Server Yukon (beta 1) to try if that worked but after installation (which meant installing the PDC build of the .NET Framework as well, although the new build of Visual Studio .NET wanted that out of there when I installed that) the service failed to start altogether. So I guess that spells d-o-o-m.

    Uninstalled everything. Reinstalling PDC build :-(

    Thursday, January 15, 2004 6:55:50 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    More impressions of the new Whidbey build#

    Nice: now you get a task-switch window if you press CTRL-TAB to switch between your windows too (a bit like ALT-TAB in Windows itself) with the available windows and the active tools (like the Solution Explorer).

    It seems they have already fixed the minor F2 usability bug. How thoughtful :-) "Make" is still there though, although they moved it into the "Build" menu. Not good enough, and certainly not as long as CTRL-SHIFT-B doesn't 'Make' by default. Can't seem to assign another shortcut to it either, apparently...

    The removed all WinBar stuff in this build (office style toolbar), I wasn't too fond of the implementation yet but why they ripped it out altogether...? Ah well, it'll be back I guess, they've been marketing it already so there's no way back.

    Ah the DataGridView (formerly known as GridView) is already looking much better. It's like the ASP.NET DataGrid we all got to know and love ;-) Sorting, header resizing, flexible column types, extensive support for formatting and templates, now in a Windows Forms environment. Cool!

    The StatusBar control has been replaced by a StatusStrip, which now finally supports a ProgressBar. It will probably support more things later on too (like buttons and such). Well it's a start but I'd rather see it able to support any type of control. (I wrote a class once that could handle that actually...)

    Hey another code generator (the "Strongly Typed Resource Builder") has entered the building! Resource files are now backed by typed classes so you can go form.Text = AppWideResources.ApplicationTitle if you have a resource key called "ApplicationTitle" in an AppWideResources.resx file. The resource editor has also undergone a remake (thank God, because quite frankly: it sucked): now you can add images, icons, audio, files, ... from the IDE. Pretty nice! Sorry for Lutz Roeder's Resourcer though ;-)

    Damn. The WebBrowser component seems broken in this build: I get a COM error :-( Uhm yeah what else, I really didn't expect it all to be managed code by now ;-)

    And in general, the Windows Forms designer is really messing with my head. It keeps messing up the generated code and it just seems a lot less stable than the PDC build. Well can't have it all I guess...

    Thursday, January 15, 2004 11:00:37 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    New Whidbey Build#

    Whew, I finally got the new Whidbey build installed. Seems that you need to uninstall the previous version of Whidbey first - which I can understand for Visual Studio .NET and the likes; but what about the .NET Framework? It had to go although I thought it had strong versioning... So that all took quite some time already, but I think I used my waiting time well. Ask me tomorrow how my tiramisu turned out ;-)

    Anyway, when installing Visual Studio I had to start over a few times because it turned out there was an error in one of the cab files. From the name of the cab (something with CE in it) I guessed it had something to do with the Compact Edition of the framework so I tossed all that out and I got it running now :-)

    Anyway, some first GUI impressions: it all looks a bit fancier. Office style toolbars, shark-fin tabs, updated icons, realtime-rendered DirectX breakpoints (uhm, ok so they have a 3D look now), ... Ah and they finally got rid of that awkward Toolbox list thingy and replaced it by a much more useable expandable list control with an actual scrollbar this time. Good work.

    Ok, already a quick non-GUI impression: I've had it installed only for a few minutes and it already crashed once. Alpha software is so cool :-) Now - to get me some sleep!

    Thursday, January 15, 2004 1:27:32 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    PropertyGrid#

    Cool, they added the PropertyGrid to the toolbox by default in Visual Studio .NET Whidbey. Actually a very useful control I tend to use quite often so I'm glad they did :-)

    Wednesday, January 14, 2004 11:59:13 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Partial types trick in PDC build#

    As I mentioned before: the WinForms designer and DataSet generators don't create partial types yet in the PDC build of Whidbey. However, you can work around that if you're using Visual Basic .NET: in contrast to C#, VB doesn't require all participating classes to declare themselves as partial. So you can keep the generated file as such, and create another file with a class which is declared partial (Expands Class Form1 for example) and it will work :-)

    Wednesday, January 14, 2004 11:26:13 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Usability issue in Solution Explorer#

    Pressing F2 (the well-known shortcut for Rename all over Windows) pops up the Object Browser in Visual Studio .NET Whidbey. Not bad if you're in code view (where 'rename' doesn't make any sense) but very annoying if you're trying to rename a solution/project/file in the Solution Explorer :-( Some shortcuts should always preserve their meaning. What if CTRL-C would suddenly mean "Close Visual Studio"? Man, would I be annoyed ;-)

    Wednesday, January 14, 2004 11:20:59 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    The unfortunate return of the Make command in Visual Basic .NET#

    Hmm this is kinda crappy actually: you can't build a Visual Basic .NET project anymore but you have to select "Make" in the File menu. There's also no default shortcut for it so the CTRL-SHIFT-B combination (which is the shortcut for Build of course) that has been burnt in my fingers for two years now is just a shortcut for 'beep' in VB.NET. Why revert to a "way of life" that existed in a different platform (errrr 'version' if we should believe MS marketing) when an intermediate 'version' did it another way - and a more consistent way at that?

    So feature disposal request for the release version of Whidbey: please kill Make!

    Wednesday, January 14, 2004 10:55:08 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Whidbey Time!#

    All right, I finally got some dedicated time do some Whidbey this week: I'm at Microsoft Belgium for a few days looking into all the new stuff :-)

    One of the perks of being at the source: you get the latest build, in stead of the PDC build which misses some features. For instance, a new Windows Forms class isn't declared as a partial type yet (with the designer-generated code in a separate file). The same goes for typed DataSets: no partial types there yet in the PDC build. And the narrowing property visibility feature doesn't work yet either (e.g. have a public getter and an internal setter). All fixed in the latest build though :-)

    I'll try to blog a lot about my findings - sort of as an "I'm sorry" for the fact that I didn't cover the PDC very well (hardly at all actually). On the other hand: a lot of people have done a much better job at that than I could have. I still have a thing or two to say about the PDC but it's getting pretty outdated don't you think ;-)

    Wednesday, January 14, 2004 9:24:40 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Photo Album Tech Specs#

    Just a quick rundown of how the photo album is implemented... Basically you have a hierarchical structure leading to a series of pictures: Collection -> AlbumGroup -> Album -> Series -> Pictures. A Picture is pretty obvious right? An Album is a set of pictures about one topic. But there's a level in between called a Series, which logically groups pictures within the same album. For instance, an album could be about one trip, and each series represents pictures taken by someone else. If there's only one series, it's not displayed to make it easier to browse the album. Now you can group these albums together in an Album Group which is basically just another higher level view. I used it to group the albums by year. The same goes for a Collection, but if you don't need this level (which I don't) you can turn it off so it won't be visible.

    In retrospect, it probably would have been better to be able to define a recursive tree with arbitrarily deep nesting but well, I didn't. Maybe in a next 'version' ;-) In general, I hacked the code up pretty quickly so it's all not that pretty I'm afreaid (meaning: I won't open source it :-) )

    Now for the implementation: it's a single ASP.NET custom control which uses an xml file to define the hierarchy explained above. Each level has some properties like title, description, date, keywords (not used yet), the path of how to get there, the thumbnail to be used and the security roles which are allowed to see this item. ("What, I can't see everything?!" - uhm yeah that's right :-p ). Some other advanced features: thumbnails can be created on the fly and cached on disk, and the date and time the picture was taken can be extracted from the EXIF meta data stored in the file. This all happens on startup of course, not on every request. The security aspect uses the current principal to define which roles it has - and that's where it plugs nicely into dasBlog because if you're logged in it sets that current principal automatically with the role defined in its configuration file. This means I can hand out individual logins on my blog to people I trust not to send the secret pictures around ;-)

    Now of course, you're not supposed to enter every single picture in the xml file because well - that would take forever in my case... It can use filters (like *.* or *.jpg) to add all files in a directory. And the other levels (series, album, ...) will also be filled in automatically from the directory structure if you don't specify anything yourself.

    I'll probably expand the photo album some more later on. As I mentioned I want to do something with keywords so you can search for pictures or have an alternative index like the cross reference on Mark Pilgrim's excellent photo album (this is where I stole the idea of course). Implementing it wouldn't be so hard actually, it's more a perseverance test to see if I could keep up adding keywords to each and every picture...

    For now, there are two minor bugs I know of... If there's an album with one series, then the series won't be displayed as I mentioned before. But it will show up in the breadcrumb (navigation) above. I'll probably fix this sometime. The second one is trickier: when you're browsing the photo album, each level displays how many items are below it. For example: there are 3 albums in 2000, or 11 pictures in a certain series. Well that's certainly true but if you go look into it, it may actually be less depending on your security role. So if you see less pictures than expected - then you know you're missing out on something great :-) I could fix this too but it would have a runtime performance penalty.

    Anyway, that's pretty much all there is to it I guess. All comments and suggestions welcome of course!

    Sunday, January 04, 2004 12:42:21 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    My Photo Album went online!#

    I'm proud to announce the new photo album on my website! Lots of pictures already, and lots more to follow I guess :-)

    Blog | General | Programming | .NET | ASP.NET
    Saturday, January 03, 2004 2:13:02 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Barcelona!#

    Well, I'm off to Barcelona to start the new year!

    Expect some pictures here soon, I'm finishing up a pretty cool photo album in ASP.NET that plugs in nicely with dasBlog - well actually it's a web control so it plugs in nicely with anything remotely related to ASP.NET :-) So stay tuned...

    Everybody have a great party; and my best wishes for 0x7D4! (Ok so that was my last small act of nerdliness for this year ;-) )

    Blog | General | Programming | .NET | ASP.NET
    Sunday, December 28, 2003 11:15:29 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Learning To Learn#

    Chris Sells has a very interesting take on learning to learn. Pretty relevant for the business I'm in (teaching/consultancy).

    Quote 1: "Industry training, at least in its popular form, is roughly based on the model of a fire hydrant: sit in front of it, open the valve, and take a big drink. Due to time limitations, trainers present information at a rapid pace, with participants retaining only a fraction of what they see and hear. What knowledge they do gain seldom passes on to co-workers after the training session, and is forgotten almost as rapidly as it is presented, necessitating continual re-training. ... [Students] often leave with the impression of the instructor being something of a genius for being able to present so much information so quickly, and instilling within their own minds a sense of inferiority for not grasping all of it at the delivered pace."

    Quote 2: "What did I do to learn? Simple: I would challenge my existing knowledge of a subject by trying to apply it to real-world conditions and/or thought experiments. If I didn't know enough about a topic to successfully apply it to a realistic problem, I would research and study until I did. If ever I was completely baffled by a problem, I could determine my own conceptual weaknesses by incrementally simplifying the problem until I could solve it. Whatever complexity I eliminated from the problem that enabled me to solve it was where my understanding was weak. Once I knew what I didn't know, I not only knew where to focus my study efforts, but I also felt more motivated to study because I could perceive my own needs."

    Tuesday, December 09, 2003 11:16:43 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Obsolete In Whidbey#

    Oh great, Brad Abrams posts an early (i.e.: highly subject to change) list of methods that will become obsolete in Whidbey. Always good to know what to start stearing clear from. (Hmm no more new Hashtable()? It's probably an overload I guess...)

    Wednesday, November 19, 2003 11:25:20 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Interface Vs. Abstract Class#
    Saturday, November 15, 2003 7:40:11 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Weird NUnit bug#

    This is quite freaky: somewhere in our NUnit tests we declared an int array with two elements to hold some expected results. No problem, tests ran. Now we make it three elements, and NUnit fails to start the test. Using debug mode in the NUnitAddin even gives us an ExecutionEngineException. Poking a little further, it turns out that its even worse: just declaring an int array (and never using it!) of three or more elements makes NUnit turn the other way when you ask it to run the test. Whow. How can that even have any influence? Interception on int[].ctor? ;-) This blows my mind.

    Friday, November 14, 2003 2:00:03 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Cursors#

    If you've ever written a client app with some time consuming code, you probably wanted to show the obligatory hourglass at some point. This is not a hard thing to do in .NET, but you should be careful to do it right.

    One way would be to just do this (in some method of a form or control):

    this.Cursor = Cursors.WaitCursor;
    // time consuming code...
    this.Cursor = Cursors.Default;
    

    However, this can result in an hourglass that never goes away if your time consuming code decided to fail and bail out with an exception.

    This looks better:

    try
    {
       this.Cursor = Cursors.WaitCursor;
       // time consuming code...
    }
    finally
    {
       this.Cursor = Cursors.Default;
    }
    

    But this approach has one minor drawback: you can't stack operations on top of each other. If this code got called from some other time consuming code which had already set the hourglass, you're now falsely telling the user that it has already finished.

    What you should do is remember the cursor as it is before you're changing it, and restoring it at the end. This can get pretty boring copy/paste code if you need to do this a lot so I wrote a little helper class which wraps this up for you. Your code can now be as elegant as:

    using( new CursorHelper( this ) )
    {
       // time consuming code...
    }
    

    Here's the code for the helper class. It just remembers the current state, and restores it when disposed. You can also use an overloaded constructor to use other cursors than the hourglass by the way. It could use some more defensive code (like checking if the control isn't null or already disposed etc.) but it gives you a good idea...

    /// <summary>
    /// A helper object that allows to set and restore the cursor on a control easily.
    /// </summary>
    /// <example>
    /// This will create a WaitCursor on a control and restore it to the previous state after the "using" block:
    /// <code>
    ///   using( new CursorHelper( this ) )
    ///   {
    ///      // time consuming code...
    ///   }
    /// </code>
    /// </example>
    public class CursorHelper : IDisposable
    {
       /// <summary>
       /// The previous cursor of the associated control.
       /// </summary>
       private Cursor m_OldCursor;
       /// <summary>
       /// The control of which to set the cursor.
       /// </summary>
       private Control m_Control;
       /// <summary>
       /// Creates a new CursorHelper for the given control. Sets the current cursor to
       /// a WaitCursor and restores the previous cursor on Dispose or destruction.
       /// </summary>
       /// <param name="ctrl">The control of which to set the cursor.</param>
       public CursorHelper( Control ctrl )
       {
          m_OldCursor = ctrl.Cursor;
          m_Control = ctrl;
          ctrl.Cursor = Cursors.WaitCursor;
       }
       /// <summary>
       /// Creates a new CursorHelper for the given control. Sets the current cursor to
       /// the given cursor and restores the previous cursor on Dispose or destruction.
       /// </summary>
       /// <param name="ctrl">The control of which to set the cursor.</param>
       /// <param name="newCursor">The new cursor to set.</param>
       public CursorHelper( Control ctrl, Cursor newCursor )
       {
          m_OldCursor = ctrl.Cursor;
          m_Control = ctrl;
          ctrl.Cursor = newCursor;
       }
       /// <summary>
       /// Restores the cursor on the associated control.
       /// </summary>
       public void Dispose()
       {
          m_Control.Cursor = m_OldCursor;
          GC.SuppressFinalize( this );
       }
       /// <summary>
       /// Restores the cursor on the associated control.
       /// </summary>
       ~CursorHelper()
       {
          Dispose();
       }
    }
    
    Wednesday, November 12, 2003 3:58:14 PM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Surfacing#

    Decompression is over, now to get rid of my newborn cold (just now that we have a long weekend - and lots of work to do :-( ) and I'm off to get bloggin' again... PDC was great. Stay tuned...

    Sunday, November 09, 2003 5:55:26 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Xml Comments in Include Files#

    Eric Gunnerson points to the C# documentation on include files in xml comments. These allow you to use external files for your xml comments - which can be convenient if you have a separate team managing the documentation. I wonder if this feature is used anywhere outside Microsoft itself actually - I'd sure love to have someone write the documentation for me but unfortunately I don't have that luxury ;-)

    Tuesday, October 21, 2003 9:58:14 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Ingo Rammer's Architecture Briefings#

    If you're doing anything in .NET (especially when you're dealing with interconnecting systems and using Web Services or .NET Remoting), I'd keep an eye on Ingo Rammer's Architecture Briefings if I were you.

    Tuesday, October 21, 2003 9:46:22 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Pager Control for ASP.NET#

    There's a cool Pager Control on MSDN that lets you add paging to databound ASP.NET controls other than the DataGrid. Looks pretty good!

    Tuesday, October 21, 2003 9:43:21 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Converting between C# and VB.NET#

    If you're planning on switching languages, or if you have some code you want to integrate (i.e.: steal/copy/paste) in your project, you already had a tool to convert C# to VB.NET - but now you can also convert VB.NET to C# online. Nice.

    Tuesday, October 21, 2003 7:52:03 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Why do abstract classes need to implement interface members?#

    Why can't I do this in C#:

    public abstract class Base : ISerializable { }
    
    The compiler complains that Base doesn't implement the methods necessary for the ISerializable interface. But who cares, it's an abstract class - it's not supposed to implement all members. It would make sense to me to be able to require that derived classes of my base class implement certain interfaces but without providing abstract methods for all of their members already.

    Now I have to go:

    public abstract class Base : ISerializable
    {
        public abstract void GetObjectData(SerializationInfo info,
    StreamingContext context);
    }
    
    which is pretty lame if you need to do this for a number of methods or interfaces...

    Monday, October 20, 2003 1:05:11 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    MenuItems#

    In Windows Forms, you can only add strings to be displayed in a menu. It would be a lot easier if you could add entire objects to a MenuItem, which could override their ToString()-method to determine the way to represent them in the menu item. For instance, you may want to map menu items to objects so that when the menu item is clicked, you immediately get the correct object in the event handler's sender object. Now, there is no way to associate an object with a menu item.

    As a solution, you could consider subclassing the MenuItem class and adding a Tag property that can contain any Object. It is impossible to override the Text property of the MenuItem class so you must set the Text property to the tag object's ToString() value at construction or when the Tag property changes. Now when you listen for the Click event of the MenuItem, cast the sender to your MenuItem subclass and obtain the associated object through the Tag property.

    Code sample for a generic MenuItem class with a Tag property:

    public class TaggedMenuItem : System.Windows.Forms.MenuItem
    {
        private object _tag;
        public object Tag
        {
            get { return _tag; }
        }
        public TaggedMenuItem( object tag )
        {
            _tag = tag;
            if( tag != null ) this.Text = tag.ToString();
        }
    }
    

    Of course you can do a whole lot more with this kind of subclassing but this can serve as an easy start...

    Monday, October 20, 2003 10:20:04 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    StatusBar Resize Bug#

    There's a bug in the .NET implementation (both 1.0 and 1.1) of the System.Windows.Forms.StatusBar control which allows it to be resized independently of the form (and don't tell me this is "by design" ;-) ).

    If you have a Form with a StatusBar on it that contains a Panel autosized to its Contents, just maximize the form and you'll see what goes wrong. The resize handle (called a SizingGrip) on the StatusBar that is normally used to resize the window is still there and can be used to resize the StatusBar (in stead of the window - since it's maximized now).

    Fortunately, there's a pretty easy workaround for this: just hide the SizingGrip when the form is maximized as shown in the following example:

    protected override void OnResize( EventArgs args )
    {
        // Check if status bar already exists.
        if( m_StatusBar != null )
        {
            // Sizing grips must be manually removed when maximizing!
            m_StatusBar.SizingGrip = ( WindowState == FormWindowState.Normal );
        }
        // Resize with adjusted status bar.
        base.OnResize( args );
    }
    

    I just noticed that this bug doesn't occur on Windows XP (it certainly does on Win2K), it seems to be hiding the sizing grip when the window is maximized. Hmm how can that be when the .NET runtime is supposed to be platform independant?

    Blog | Programming | .NET | Quirks
    Monday, October 20, 2003 10:16:53 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Some cool ASP.NET Whidbey preview stuff#

    Scott Gunnerson talks about his ASP.NET Keynote Demo and shows some screenshots. Points 6 (SQL Cache Invalidation!) and 8 (Master Templates!) must totally rock!

    Monday, October 20, 2003 8:52:51 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Tools! Tools! Tools!#

    Pretty much every blog on the planet linked to this one already but just in case you were interstellar for a while: go check out Scott Hanselman's Ultimate Developer and Power Users Tools List!

    The ones that were new to me but I will definately install on every dev machine I get my hands on:

    • CommandBar for Explorer: hosts a command prompt window right in your windows explorer. Great for all your command line needs such as csc, javac, regsvcs, ildasm, ...
    • QuickCode.NET: a brilliantly simple and extensible keyword expander for Visual Studio .NET. Type in propv int Test [Alt+Q] and shazam: you get a new property Test with a private variable _test and xml documentation. Sweeeet.

    Also cool on the list:

    • Snippet Compiler: just punch in some code on-the-fly and run it. No need to start Visual Studio .NET, great for demo's!
    • Web Services Studio: use web services interactively. Point to the proper WSDL endpoint and you get an immediately callable .NET Proxy for it.
    • GhostIt: make 'ghost' windows with a single click. These are semi-transparent background windows, perfect for instant messengers and the like.
    • NetPing: where a regular ping just doesn't cut it. See operating system, uptime, memory and disk space usage, manage the pc, portscan, start remote desktop, ...

    So these are a welcome addition to the tools I'd been using already:

    • Lutz Roeder's Reflector: the slickest way to look at assembly contents, referenced assemblies, metadata, ... Goodbye ILDASM (except for an actual full decompilation to IL of course). Used to be in conjunction with Anakrino but since Reflector also has a built-in decompiler these days I stopped using that. So no more worries: need to know what happens inside an assembly? Decompile it and have a look! Or - if it's something the likes of the Framework Class Library - browse the Rotor Source Code (which is kind of what the Java folks would call a reference implementation for the CLI I guess).
    • NAnt: a pretty powerful build tool for .NET (ported from Apache Ant).
    • NUnit and the NUnitAddin: a widely used unit-testing framework for .NET (ported from JUnit). The addin lets you run your unit tests straight from Visual Studio .NET. Cool!
    • Process Explorer and other great tools from SysInternals: need to know which process is locking a file, which TCP connections are open, which registry keys are being modified, ...? Look no further!
    • PowerMenu: a little utility that lets you do some nice things with your windows very easily (minimizing to the tray, setting transparency, keeping on top, ...).
    • SharpReader: my preferred news aggregator. Although I've also been using FeedDemon lately but that won't remain free.
    • E = m c²: not to blow my own horn but I use it all the time on all my machines ;-) It's a utility that can check various sources for messages, then filter and redistribute them (read: check mail, rss feeds, events, ... and show popups, send mails, ...).

    The best thing is, these tools are all free and almost none of these require installation. Drag'n'Drop/Unzip/Xcopy (whatever you want to call it) deployment is so my bag :-)

    Monday, October 20, 2003 8:30:31 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    A Few Must-Reads#

    Clemens Vasters talks about what could be the next big paradigm shift in programming (as was moving from procedure-based programming to OOP): Service Oriented Architectures (SOA). Also check out his excellent post on why checking postconditions is more important than checking preconditions.

    Eric Gunnerson shares his favorite resource on Deterministic Finalization.

    Joel gives a very interesting basic overview of encodings and character sets. A few months ago I was puzzled by encodings myself so I looked into it - and Joel's right: it really isn't that hard.

    In another post, Joel explains why he doesn't like programming with exceptions. This is one of those things where I don't know what to think about it, surely he's a smart guy and he will have his reasons to go back to return codes and drop structured exception handling but I don't really see the point and his post doesn't convince me either. Weird.

    On MSDN Magazine, Jason Clark from Wintellect talks in detail about the upcoming feature of G enerics in .NET. In a previous article, he already covered a first look at generics, which is also pretty interesting.

    The Kiss Of The Titans: Don Box dumps his old-time girlfriend ("COM is Love") and moves on to Chris Sells (voyeur on duty is Tim Ewald by the way). Man, if those drinks were poisoned: no more .NET ;-)

    Whow, linkorama ;-)

    Wednesday, October 15, 2003 8:35:22 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    IXmlSerializable#

    Very interesting if you run across a .NET type that can't be serialized (e.g. a Hashtable) or when you want precise control over the xml mapping:

    Custom xml serialization with IXmlSerializable: "IXmlSerializable is an undocumented interface that can be used to provide custom xml serialization support to types that are to be serialized using XmlSerializer."

    Monday, October 13, 2003 8:48:14 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Helper Interface For British Chauvinists#
    public interface ISerialisable : ISerializable {}
    Thursday, October 09, 2003 12:02:04 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    More Annoyances: Slow Webmail#

    As a follow-up to my last post, I went out to look on google for another problem I've been having.

    I'm using my own server to access my email over the web (full control baby), and I'm using Imp as my webmail client. Unfortunately, it's pretty slow to use (each page takes 10 seconds to load) and I tracked the problem down to the imap_open call in PHP which opens the connection to my IMAP server. I found a number of people noticing the call is slow but unfortunately I haven't found a solution to this that works for me :-( Something I found quite often is to specify the port in the connection string but I'm already doing that and it still takes 10 seconds. Also tried to use the IP-address direcly in stead of the host name but still no luck. Damn!

    If anyone has a good idea, be sure to let me know before I actually start sniffing the wire to see what's going on...

    Sunday, September 28, 2003 3:14:06 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    VB.NET Whidbey Demo#

    Fawcette is hosting a nice demo on VB.NET Whidbey taped from VSLive! in New York last July. The demo is taken from the keynote by Eric Rudder, but don't bother to sit it out: the demo is by far the most interesting part.

    The sound starts out pretty crappy but don't worry, it gets a little better ;-)

    Sunday, September 14, 2003 3:46:11 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    PDC 2003#

    Oh yeah, I will be there. Reservation is confirmed, plane and hotel are booked :-)

    PDC 2003

    By the way, if you're looking for a PDC flair that suits your specific situation: these are pretty cool too :-)

    PDC 2003 PDC 2003 PDC 2003

    I'll try to blog when I'm there - if so I'll put myself on the PDC Bloggers site. Only one problem, my server shouldn't be going down when I'm there or else no blogging for me :-s

    Blog | Programming | .NET | PDC03 | Windows
    Thursday, September 11, 2003 1:31:23 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    ASP.NET Version Switcher#
    Here's a nice GUI tool that lets you swi tch the runtime version between 1.0 and 1.1 for an ASP.NET website. Wish I had this when I was manually converting the isapi mappings in IIS!
    Tuesday, September 09, 2003 7:54:40 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Whidbey, Yukon, Longhorn slides#
    Sean and Scott give a great roundup of what's coming in the next versions of .NET, SQL Server and Windows. Currently, most of this information is still under NDA until the PDC but it has been assembled from all bits that have "leaked" onto the internet. Nice :-)
    Sunday, August 31, 2003 12:04:29 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    From Xml Encodings Over Filesystem Tags To Generic Metadata#

    I just remembered a discussion I had a few months ago about the xml encoding attribute, which got me thinking on the subject again. My original question was: "What's the use of the encoding attribute in an xml declaration?"

    Let me explain briefly... If you have an declaration at the top of an xml document, this will basically tell your xml parser that the document it's reading is stored in UTF-8 encoding. The encoding is the way a document (not limited to xml documents, this goes for any text document) is actually stored or streamed, which means it defines how the ones and zeroes should make up a readable text document. There are lots of possible encodings and you can read books the size of a small building about the subject but that doesn't really matter now. UTF-8 and UTF-16 are the defaults for xml documents by the way, so if you omit the attribute, the parser should check the Byte Order Mark (BOM) at the beginning of the file to determine which of the two the file will be encoded in. This implies that every xml parser on the planet (and beyond, if SETI@home finds anything my guess is that it'll be an alien xml document) should at least understand UTF-8 and UTF-16 encoding.

    Anyway, my point was: at the time the parser encounters the encoding attribute in the xml declaration, it's already reading the file so it should already be assuming some kind of encoding. To me this sounds like saying "This sentence is written in English" or shouting to people that they need ears to understand you (well unless they can lipread of course). Or to quote Blackadder III: "It's the most pointless book since 'How to Learn French' was translated into French." What I understood from my discussion on the subject is that this shouldn't be a problem because of two possible reasons:

    1. All the characters in an xml declaration as the one above are very basic (i.e. they're all plain ASCII) and will be the same bytes for all the different encodings out there. I find this hard to believe (what about little and big endian differences?) but it shouldn't be too hard to check though.
    2. Special bytes (such as the BOM) at the beginning of a file already indicate the used encoding. But in this case the encoding attribute doesn't seem to have any value at all anymore.

    Another use could be that you could switch to a different encoding after the declaration, indicating in the encoding attribute how the rest of the document will be encoded. But why would you want to do that? I can understand that you would like to set a different encoding for some element in an xml document but what's the point of having the entire document after the xml declaration in another encoding than the declaration itself?

    Even the xml 1.0 specification admits that autodetecting the encoding is a hopeless situation, also providing some hints as to what a parser should do to determine the right encoding.

    So still, I don't see the real use of the encoding attribute directly embedded into the document. Especially since changing the encoding means changing the document content - which in my mind are two very unrelated things which shouldn't be affecting each other. The encoding says something about the document and therefore it is metadata. Some ("internal") metadata certainly belongs with the document itself (e.g. the date and time a picture was taken gets nicely baked into a jpeg file), while other ("external") metadata is very context-sensitive (e.g. a document's encoding). You can store an xml document on disk as Unicode, but choose to send it ASCII encoded over a wire to reduce bandwith. That doesn't change the semantics of the xml document.

    I guess this is just another example of what Joel Spolsky means by "leaky abstractions". We should hide the way a file is physically stored from upstream layers but still the parser somehow needs to know how to read the bytes...

    To a certain extent, filesystems already allow this type of metadata but it is very limited. For example, you can tag a file as readonly in most filesystems but you cannot define your own tags. Maybe this system should be extended to provide some generic options to associate metadata with files. If the rumors about Windows "Longhorn" are true that the new WinFS filesystem will be using a SQL Server "Yukon" as a backend, it should be a breeze to allow more generic attributes at the file or directory level. On a wire-level, the TCP/IP protocols define headers which provide out-of-band metadata but these headers are also fixed. Layers that build upon this stack, such as HTTP, will define their own headers but the borderline between inline and outline headers seems to become quite unclear.

    Metadata is ubiquitous, but it has its set of problems. Maybe it's time somebody pins down the way we should be handling "external" metadata from now on - taking the context into account. For example, encoding makes sense when storing bytes to disk or transmitting them over the wire but generally not when manipulating the data semantically from within a programming language. A readonly flag has a use for a file on disk but not when it's attached to an email. Windows Access Control Lists (ACL's) defining which user has access to which files and directories make no sense when the file has left the supervision of the domain controller. If I look at Attributes in .NET I see this sort of metadata standardisation has been done beautifully within a programming environment - why not expand this to other computing domains?

    Sunday, August 31, 2003 11:51:35 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ClickableButton#
    Brilliant article by Chris Sells on CodeProject: a Clickable Button Control! Absolutely amazing...
    Saturday, August 23, 2003 10:34:15 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Web Services Demo: Instant Messaging#
    Web Services so rock: I'm teaching a .NET Framework course this week and in the Web Services module I thought I'd give a live demo of the wsdl.exe tool that generates proxy classes for Web Services. Normally live demos are not a good idea but hey sometimes you can really get something cool out of them so I figured I'd give it a go.

    So I point my brower at xmethods.org to look up an interesting Web Service and I stumbled upon one called InstantMessageAlert that claims it can send instant messages to any of the four major IM services: MSN, AIM, ICQ and Yahoo! Messenger.

    So I sign in to Yahoo, take the wsdl, generate a proxy class, call the SendYahoo method and BANG! Nothing happens ;-) Ok so that was a setback. Then I popped up MSN Messenger and called the SendMSN method - et voila: an instant message appears from out of the blue telling me how Web Services rule. And thereby giving me extra credit for my evaluation later on ;-)
    Friday, August 22, 2003 11:00:23 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    DefaultValue on property with enum value (VB.NET)#
    If you have a custom control with a custom property, you can specify a default value for it by using the DefaultValueAttribute. For example: <DefaultValue("")>. This will tell Visual Studio.NET not to serialize the value to the container's code when it is equal to the default value (an empty string in this case). However, if the type of your property is an Enum, you can't just specify the default enum value. You must use an overload to specify the type of the enum and the default value as a string. So use

    <DefaultValue(GetType(MyEnum), "MyEnumValue")>

    in stead of

    <DefaultValue(MyEnum.MyEnumValue)>

    Note that both methods work in C#, but only the first one works in VB.NET.
    Blog | Programming | .NET | Quirks
    Friday, August 08, 2003 8:37:46 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    .NET Security Information on No-Touch Deployment#
    I just noticed a cool .NET feature when launching Chris Sells' .NET FormatDesigner directly from the browser. Apparently when you run an application from the internet in .NET 1.1 it shows a balloon stating the program is running in a partially trusted context. Furthermore, the window caption is changed to mention that the application was downloaded from the internet (well I guess it just shows the trust zone it's running in) and it holds the url where it came from. 

     

    Pretty nice, I guess this will help non-developers at least somewhat when this no-touch deployment thing really starts happening. It's not that much yet but it sure is a lot better than the exception dialog that I got when running the simplest .NET 1.0 form from a browser ;-)
    Thursday, August 07, 2003 8:57:33 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Ingo Rammer discusses why some Windows Forms controls are sealed#
    In this article on sealed controls, Ingo looks into the details of the ImageList and the reason this class (and other controls) are sealed.

    His conclusion:
    "Don't cry foul whenever you encounter sealed in some framework. The designers might have had their reasons for using it, and the implications of NOT using sealed might be bigger than you can imagine at first glance. In the case of the ImageList it would have meant to rewrite ALL windows forms controls in purely managed code instead of just wrapping the underlying Win32 controls."

    Well that about sums it up, now I guess we'll just have to wait until they do rewrite all controls in .NET before we can actually start extending them. Or we can just use alternatives such as Magic or Genghis.

    I liked Ingo's idea to insert a proxy for the ImageList by the way - but I wouldn't expect any less from a remoting addict ;-)
    Wednesday, August 06, 2003 12:53:26 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual Studio References#
    Another source of getting the wrong version of your code (see my post on Various Caches) can be because your references (real ones, not Project References) in Visual Studio are wrong, so make sure they point to the correct location of the dll. Even if you have set them right before, Visual Studio can still make them go wrong sometimes. And even then, I have noticed that the compiler may use another location than the one you are referencing. Needless to say this can be very frustrating and I haven't found a solution for this yet, sadly. The biggest problem with references is the "copy local" flag (in the reference's properties), which makes a copy of the referenced dll in the bin folder of your project. This makes it easy to run your project because you don't need to register your dll's in the GAC or place them in your DEVPATH. Of course, the drawback is that if they aren’t correctly updated to match the current version (that is, if your reference is wrong), you will be working with an older copy.

    If it's in any way possible - and logical within your project - use Project References. I've never seen them go bad.
    Blog | Programming | .NET | Quirks | VS.NET
    Wednesday, August 06, 2003 8:25:32 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Various Caches#
    Your dll's are cached in a few places: you have the GAC's Download Cache if you use downloaded assemblies (that cache can be cleared by running "gacutil /cdl"), the Visual Studio WebCache (can be cleared by closing all instances of Visual Studio and deleting the VSWebCache folder in your windows user profile) and even the Internet Explorer cache (can be cleared by deleting the cache files in the Options screen). If something goes wrong in any of these places (and trust me, it does sometimes), you could be working with a wrong version of your code and you may spend hours looking for a bug you already solved. So when in doubt, delete the caches!
    Blog | Programming | .NET | Quirks
    Wednesday, August 06, 2003 8:20:24 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Tricky Scope (VB.NET)#
    Pop quiz: what do you think would be the output of the following code?
    Dim i As Integer
    For i = 0 To 4
        Dim s As String
        s = s & "X"
        Console.WriteLine(s)
    Next
    
    Quite possibly, you would expect to see 5 lines each containing a single X, since the variable is restricted to its scope: the For block. Since it cannot be used outside the block, you would expect it to be reinitialized each time the block is executed. Unfortunately, what we actually get is
    X
    XX
    XXX
    XXXX
    XXXXX
    
    This is because although the scope of the variable s is limited to the For block (which means it cannot be used outside it), the variable actually starts to exist at the start of the method. It's only initialized to its default value once, upon entry into the method. See Local Variables and Local Declaration Statements in the Visual Basic .NET Language Specification for more information on this.

    This means you have to explicitly initialize the variable to Nothing (or to your own default value):
    Dim i As Integer
    For i = 0 To 4
        Dim s As String = Nothing
        s = s & "X"
        Console.WriteLine(s)
    Next
    
    If you would try something similar in C#, the compiler will stop with an error (not a warning): "Use of unassigned local variable 's'" which makes it impossible to make mistakes about this.
    Blog | Programming | .NET | Quirks
    Friday, August 01, 2003 3:18:24 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Relative Url's#
    A lot of ASP.NET WebControls use a url, such as the NavigateUrl property of the HyperLink control or the ImageUrl for an Image. If you set such a url to a relative path, you might experience some unexpected behaviour in some more advanced scenarios - unfortunately without a clean workaround.

    Internally these url's are rendered to html using the Control.ResolveUrl method. The first thing this does is deciding whether or not it's a relative url. If it's not, then the url is just returned so absolute url's remain untouched. If it is a relative url, then it is prefixed with the control's TemplateSourceDirectory - and there's the tricky part. The Control.TemplateSourceDirectory property is "the virtual directory of the page or user control that contains the server control". This means that a relative url is always relative to the containing control's directory, not to where you're setting it from. If you remember my post on ASP.NET Path Discovery, this will sound familiar. More on that later.

    Now imagine a scenario where you have a breadcrumb-style header control (Header.ascx) with some navigational hyperlinks on it that need to be added depending on the page hosting the control. This means the hyperlink controls are contained in Header.ascx, and their url's will always be relative to this control's directory. If you use the header control from various places in various directories then you need to step away from the idea that setting these url's will be relative to the page you're working in. In stead you should always think on how to get from the header control to the current page's directory which is a pretty heavy burden. And as I mentioned in my post on ASP.NET Path Discovery there is no clean out-of-the-box way to discover this 'current path'.

    As for a workaround: there is no property on a hyperlink or any other control that allows you to modify this behaviour and take the url you gave it as such without modifying the path. The only thing you can do is to manually set the href attribute in stead of using the NavigateUrl property as such: myHyperLink.Attributes["href"] = myUrl. I probably don't need to mention that this isn't my idea of clean code but sometimes you just need to get your work done too ;-)
    Blog | Programming | .NET | ASP.NET | Quirks
    Friday, August 01, 2003 12:37:28 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    .NET Developer Roadmap 2003 - 2005#
    I've been looking at the MSDN Developer Roadmap 2003 - 2005 and it all looks pretty interesting to me.

    Between Whidbey (2004) and Orcas (2005) it looks like Whidbey scores highest on the coolness factor, thanks to the Yukon (next-gen SQL Server) integration which will allow stored procedures to be written in managed code. Orcas seems to be targeted mostly on integrating with Longhorn (next-gen Windows) so it will all depend on how ground-breaking that will be I guess.

    Whidbey's VB.NET came as a bit of a surprise to me. It seems to incorporate many features I thought would be reserved for C# or the like - a form of generics, operator overloading, unsigned data types, ... I'm also glad to see they finally implemented inline xml code documentation - the thing I miss most in the current versions. Also cool: the return of "Edit and Continue" while breaking in a debug session. I wonder how that actually works (sounds like high impact on the CLR to me)...

    The thing that looks the most useful in Visual C++ Whidbey is POGO (Profile Guided Optimizations) whichs "allows the compiler to instrument an application and collect information on how the application is used". This information enables Visual C++ to further optimize code based on real-world usage patterns.

    Concerning my favorite language, C#, I'll be looking forward to experimenting with generics. It seems like one of those things which you use regularly but you know they have quite a lot of hidden power - if only you can find the right use for them. Furthermore, I always liked anonymous methods in Java so I'm looking forward to having that implemented too.

    There are also quite a few enhancements in the .NET Framework worth mentioning, especially some controls I've missed (which meant dealing with interop and API calls): a managed Web browser control, a sound component (let's hope it supports mp3), layout managers, and even a BackgroundWorker component to handle asynchronous calls. Great improvements in ASP.NET include a GridView control (like a DataGrid but with automatic paging, sorting and editing) and what looks like an extensive templating mechanism using Master Pages, themes and skins. More cool-sounding buzzwords are ObjectSpaces (in ADO.NET) and of course WSE (Web Services Enhancements) integration. Let's hope Don Box has been doing something useful the last year :-)

    Finally, I'm pretty curious about the Visual Studio Tools for Office suite which will allow all those VBA-programmers out there to dive into the managed world. I'm glad to see they won't be left in the dark, but of course that makes sense when you think about how much applications are actually written in VBA.
    Wednesday, July 30, 2003 10:45:35 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    Macro Madness#
    There's an article on MSDN that shows some nice things you can do with macros in Visual Studio .NET.

    I've never been too fond of writing macros but maybe I'll give two of the examples a go: I liked the one where you select a region of text and put a #region around it in one step, and I was also quite charmed by selecting some private members and automatically generating the public properties for it. These little things might make a difference...
    Blog | Programming | .NET | VS.NET
    Thursday, June 12, 2003 9:25:55 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    Dr. GUI says: Don't Lock Type Objects!#
    I can't stand the guy's third-person perspective writing but he's got a point here:

    http://msdn.microsoft.com/library/defaul t.asp?url=/library/en-us/dnaskdr/html/askgui06032003.asp

    Damn, and I just did that somewhere.
    Thursday, June 12, 2003 9:25:19 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ASP.NET Abstract Base Classes & Visual Studio#
    Not a good mix ;-) Apparently when you try to open a web form in the designer, it will try to create an instance of the base class of your form's code-behind class. Typically, that will be the System.Web.UI.Page class. I can see why that's a good idea in WinForms where you actually see an instance of a Windows Form (System.Windows.Forms.Form) in the designer - but I don't really understand why the web forms designer would do such a thing. Web forms should render html to the browser, and since User Controls aren't even rendered in the webpage you're designing I don't think any other actual rendering is done by the designer, apart from your own html code for the page of course. So why create an instance of the page's base class?

    The reason I came across this is that we have some custom base classes for ASP.NET pages and our design required one of those to be abstract since an overriding class should implement a certain method. Now the designer crashes on us if we derive from that since it tries to instantiate an abstract base class - no more forms designer for that page. Maybe there is a good reason for the designer to instantiate a Page object, but at least it could look further up the class hierarchy for a more suitable (i.e. non-abstract) base class if it really needs some instance to do its work.
    Blog | Programming | .NET | ASP.NET | Quirks | VS.NET
    Thursday, June 12, 2003 9:03:05 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    It's oh so quiet...#
    Hmmm, not a lot of posts recently. Have I learnt nothing at all then the last couple of days? Sure I have!

    The coolest thing I've done this week (at work anyway :-) ) is writing a sort of ASP.NET databinder for xml documents. Shortly put, it's a component that binds a property of a control to a node within an xml document using an xpath query. For example, it can bind a textbox txtCustomerEmail to the xml node "/customer/info/contact/@email" - which means it will fill the textbox with the proper attribute value from the attached xml document, and after a submit it can update the xml document with the modified email address the user entered in the textbox.

    As an extra treat, if the node specified by the xpath query doesn't exist yet, an attempt will be made to create it - along with any underlying nodes that don't exist yet. So if you try to set the email address to 'billg@microsoft.com' (yes, he's a customer of ours ;-) ) but the xml only contains for example, it will create the info and contact nodes, and the email attribute within it to result in the following xml: . Cool? I think so!

    I have to admit that it's still fairly limited (it can only handle basic xpath queries to be able to create nodes), and it would be great to implement this as an extender provider but since those still don't work in ASP.NET, I guess that's out of the question.

    I've also been quite busy on E = m c² again, watch this space for some cool updates real soon!
    Saturday, May 31, 2003 12:46:03 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Make the page title runat the server in ASP.NET#
    In ASP.NET you have a (i.e.: exactly one) html form in which all your controls reside. If you want serverside access to any control, you just give the appropriate tag an ID and the runat="server" attribute. If the designer didn't already do this for you, declare an object of the proper type in your codebehind page with the exact same name as the ID you gave your tag and you're done - you now have full control over your uhm... control. You can do this with any tag - if there is no serverside equivalent for what you're trying to do, just use the HtmlGenericControl.

    But what happens if you go outside the bounds of your form? A typical problem I've seen posted a few times is: "How can I set the title of my webpage in code?". This implies accessing the <title> tag in your html, which is in the <head> section (i.e. before the actual <body> and <form>). It turns out you can just as well make any tag there run at the server. So you might go:

    <title id="lblTitle" runat="server">WebPage1<title>

    In your codebehind you declare your object:

    Protected WithEvents lblTitle As HtmlGenericControl

    And voila, set the text as follows: lblTitle.InnerText = "This ran at the server!".

    Now we can take this one step further. Why not create something that encapsulates a portion of the <head> tag? Just add a User Control to your project, e.g. "HtmlHead.ascx" and make it emit some header tags such as title, meta tags, link tags to import css files, ... anything you can think of really. If you want programmatic control over any of these tags, give them an ID and make them runat=server so you have them at your disposal in your codebehind class. Now you can declare nice userfriendly properties and methods on your User Control such as PageTitle, AddCssFile, etc.

    In any webpage, just can now register the user control and add it to your <head> tag as follows:

    <%@ Register TagPrefix="uc1" TagName="HtmlHead" Src ="HtmlHead.ascx" %>

    Put an instance of this control in the <head>:

    <head>
       <uc1:htmlhead id="uctHtmlHead" runat="server"></uc1:htmlhead>
       (...other tags, such as the ones generated by Visual Studio go here...)
    </head>

    Don't forget to declare the object in your codebehind:

    Protected WithEvents uctHtmlHead As HtmlHead

    And that's it - you're done: full control over your html head!

    uctHtmlHead.PageTitle = "ASP.NET rocks :-)"
    Monday, May 19, 2003 7:53:41 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Full META Jacket#
    I just noticed that by compiling all E = m c² plugins into one assembly, the total filesize is now 52 KB where it was 80 KB when each plugin had its separate assembly. I guess there's a lot of meta information wrapped around the code - I didn't expect the assembly manifest to take that much space...
    Blog | Programming | .NET | Emc
    Monday, May 19, 2003 7:52:31 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Optional Parameters (VB.NET)#
    In Visual Basic.NET you have the possibility of making method parameters Optional. This means you can omit these parameters when calling the method. However this behavior is not present at runtime and will be simulated when compiled! The compiler just reads the optional parameter’s default value from the called class (which is kept in MSIL metadata) and passes that along when calling the method. This means that if you change the optional value for the function and recompile the called class, nothing will change (except for the MSIL metadata of course). The calling methods will need to be recompiled however, or they will still be using the wrong "optional" parameter!
    Public Class frmMain
        Inherits System.Windows.Forms.Form
    
        Public Sub MissSomeParms(Optional ByVal someInt As Integer = &HABCD, _
            Optional ByVal someString As String = "Hello")
    
        End Function
    
        Private Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
            Handles MyBase.Load
    
            MissSomeParms(, "goodbye")
        End Sub
    End Class
    The MissSomeParms method looks as follows in MSIL:
    .method public instance void  MissSomeParms([opt] int32 someInt,
                                                [opt] string someString) cil
    managed
    {
      .param [1] = int32(0x0000ABCD)
      .param [2] = "Hello"
      // Code size       1 (0x1)
      .maxstack  8
      IL_0000:  ret
    } // end of method frmMain::MissSomeParms
    

    The frmMain_Load method breaks down to:

    .method private instance void  frmMain_Load(object sender,
                                                class
    [mscorlib]System.EventArgs e) cil managed
    {
      // Code size       17 (0x11)
      .maxstack  8
      IL_0000:  ldarg.0
      IL_0001:  ldc.i4     0xabcd
      IL_0006:  ldstr      "goodbye"
      IL_000b:  callvirt   instance void frmMain::MissSomeParms(int32,
                                                                string)
      IL_0010:  ret
    } // end of method frmMain::frmMain_Load
    

    You can see that the first default parameter value (0xabcd) has been created in the caller and passed as a regular parameter. The default values are not being recovered by the called function, although they are available in metadata (.param). Obviously, this must be a capability of the compiler, which reads the .param values once and uses them to call the method.

    Conclusion: Keep away from Optional Parameters.

    Blog | Programming | .NET | Quirks
    Saturday, May 17, 2003 3:40:06 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    Visual Studio breaking down#
    I'm getting extremely pissed at Visual Studio.NET lately. It just keeps bugging me and it keeps getting worse and worse. Furthermore my deadline is getting closer so it's too dangerous to do a full reinstall. So I guess I'll be stuck with the following annoyances for a while:

    Debugger

    Or "bugger" in short. It looks like the debugger just 'degrades over time'. And now it just seems unstoppable... For instance, I'm trying to set a breakpoint somewhere in a class and Visual Studio opens up another class (with the same name but in another namespace) and tries to set a breakpoint in there. I'm setting a breakpoint in some other class but at runtime it says it cannot break on it because it has no symbols loaded for that class. It does have all the symbols for all other classes in the same project (assembly), but somehow it misses the necessary information for this class. And just now I had to do a hard reset of my pc because the debugger crashed on an Internet Explorer window. No rescue possible, even though it's a Windows 2000 box which can usually recover pretty well from a crash. *sigh*

    Compilation Error

    Quite often (but randomly) when I run my ASP.NET project, I get a compilation error saying that some random referenced library could not be loaded:

    BC31011: Unable to load referenced library 'c:\winnt\microsoft.net\framework\v1.1.4322\temporary asp.net files\webroot\dfead88a\73139fa7\assembly\dl2\3a9f7923\5959d15f_b1fec201\ library.dll': Access denied.

    I don't get this; this is a shadowcopy folder managed by ASP.NET and it can't use it properly? I'm hoping it's because this is still .NET 1.1 RC2 but I'm losing so much time killing the aspnet_wp.exe process over and over to get this running. *sigh*

    I just restored all .NET content for this web to use ASP.NET 1.0 (which means associating each .NET extension in IIS within the project with the older isapi filter) and so far I haven't gotten the error yet... Keeping my fingers crossed.

    Find In Files

    Just something I noticed today: I'm searching in files, all project files, all searchable files, whatever, but it seems to keep forgetting to search the root folder of my project. It finds everything in the subfolders but it just completely ignores the root. *sigh*
    Blog | Programming | .NET | VS.NET
    Wednesday, May 14, 2003 1:02:38 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

     

    ASP.NET path discovery#
    Sometimes the guys at Microsoft just strike me with amazement. This happens a lot but I'll limit myself to sharing this one with you - for now ;-)

    I'm working on an ASP.NET project, I have a folder structure like /root/subdir where all my aspx pages reside. Now I learned from last time that it's better to keep my user controls (ascx) in a separate folder (/root/subdir/controls in this case) to keep them from cluttering my actual web's folder. Now inside one of those user controls I'm adding an that should point to "/root/subdir/images/help.png" - sounds easy but I don't want to use absolute url's since I want to be able to move the site into another root or rename the subdir or whatever.

    My first guess was just to use "images/help.png" but apparently this becomes relative to the executing control's path, resulting in "/root/subdir/controls/images/help.png". No good. I could go for "../images/help.png" but I don't like this kind of path walking too much since it depends on my project's directory structure, not on my actual web path. Fortunately this doesn't wind up in the resulting html: it will become "images/help.png" and not "controls/../images/help.png" so that's ok.

    I could use Request.ApplicationPath but that only gives me "/root" so I'd still have to append 'subdir' to it, and as I said I want to keep away from hardcoding paths in my application.

    The logical thing to try next is to use the Request object since that contains the path of the executing aspx page - just what I need because it should be relative to this path. Request.Path contains the page name so I can't use that directly: it's something like /root/subdir/page.aspx and I just want the first part, the directory name. I was naive enough to hope that System.IO.Path.GetDirectoryName(Request.Path) would discover the path separator or allow me to override it easily but nope, I get "\root\subdir/images/help.png". That works in Internet Explorer but it's ugly and non-standard so there's no way I'm using that.

    So I'm sniffing around in the QuickWatch window looking at the Request object and what do I see? Request.BaseDir gives me "/root/subdir" - exactly what I need! I punch it in my code but to my big surprise I find that the BaseDir property is declared private! Why? There must be some reason - at least I hope so - because I also noticed that Request.Url.BasePath points to the exact same thing, but it's also private. Again: why?! These things really blow my mind.

    I guess I'll wind up doing "../images/help.png", I'm not in the mood to strip off the page and querystring from Request.Path myself. Certainly not since it's already been done by Microsoft but they decided not to share it.
    Wednesday, May 14, 2003 9:51:42 AM (Romance Standard Time, UTC+01:00) #    Comments [3]  | 

     

    Behind the scenes of events in VB.NET#

    Take the following event declaration (defining the parameter list immediately) in VB.NET:

    Public Event SomethingHappened(ByVal sender As Object, ByVal e As SomethingHappenedEventArgs)

    The VB.NET compiler is 'kind enough' to generate an appropriate delegate type for us behind the scenes - in this case:

    Public Delegate Sub SomethingHappenedEventHandler(ByVal sender As Object, ByVal e As SomethingHappenedEventArgs)

    Note that you can also define the delegate type yourself and declare the event more in a C# type of way:

    Public Delegate Sub SomethingHappenedEventHandler(ByVal sender As Object, ByVal e As SomethingHappenedEventArgs)
    Public Event SomethingHappened As SomethingHappenedEventHandler

    This approach is better because otherwise, a new delegate type will always be created by the compiler - even if you declare different events with an identical signature which could and should use the same delegate type. By defining the delegates yourself you have more control over what is created.

    Ok so now I'm trying to determine if there are any subscribers to my event. In C# I could get away with a simple "if( SomethingHappened != null )" but in VB.NET SomethingHappened is just the event declaration, not an actual object, so I can't use "If Not SomethingHappened Is Nothing". The generated (or declared) delegate is also just a type, not an object, so I can't just go "If SomethingHappenedEventHandler.GetInvocationList().Length > 0". I spent some time surfing the web looking for an answer, and after a while I found out I should've looked at the documentation straight away. As it happens, the compiler also generates a private member field called SomethingHappenedEvent which holds the actual delegate instance used to maintain the event's invocation list. So the solution was to use "If SomethingHappenedEvent.GetInvocationList().Length > 0".

    So when you declare an event as above, you actually get this:

    Public Delegate Sub SomethingHappenedEventHandler(ByVal sender As Object, ByVal e As SomethingHappenedEventArgs)
    Private SomethingHappenedEvent As SomethingHappenedEventHandler

    According to the documentation, the compiler also generates some methods to add and remove event handlers but I didn't find them. Note that all these compiler-generated types and fields aren't visible in IntelliSense so they're not obvious to discover.

    I must say I'm a lot more comfortable in C# where there are no auto-generated features of type "let's-make-the-basics-easy-but-cause-confusion-for-people-that-actually-know-what-delegates-are". Delegates and Events are the core of event-driven programming in .NET and should be well understood when programming them. This is no playground for compilers, this is a must-know for developers. Another boo to VB.NET!

    If you want to know about how events actually work, go read the Bedtime Story, really!

    Friday, May 09, 2003 7:07:19 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

     

    The Visual Basic .NET Runtime Library#
    I came across this article on MSDN that started off with the following on the Visual Basic Runtime Library:



    Before I get into the operators themselves, I'd like to discuss a somewhat related point. There is a running debate about whether Microsoft Visual Basic® .NET developers should use the Visual Basic runtime library or if they should abandon it in favor of direct use of the Microsoft .NET Framework classes.

    I have been programming in one form of Basic or another since the late 1970s. During all that time, the language has had a runtime library. I spent many years working with Digital's VAX Basic, which had a very powerful runtime library, nearly comparable to the one provided with Visual Basic.

    Most other languages have runtime libraries as well. This includes notable languages such as C, C++, and Java. No C programmer would ever consider the .NET Framework to support the C language unless it also included the C runtime library.

    Why should Visual Basic be any different? I honestly don't know. I fully exploit preexisting functionality whenever possible, and in particular I fully exploit the time-tested and familiar runtime library for any language I use. This includes the Visual Basic runtime library.

    I've recently seen posts on newsgroups where Visual Basic .NET developers were trying to figure out how to implement IsDate and IsNumeric by hand using the .NET Framework classes. While admirable from an academic standpoint, such an effort is redundant because those functions already exist in a well-implemented and tested form. This is especially true when the replacement code is 12-15 lines in length, compared to one function call from the runtime library.

    To me, and I think to most long-time Visual Basic developers, the runtime library for the language is virtually part of the language. The ability to reuse a powerful preexisting and pre-tested software library is compelling, and I strongly recommend that Visual Basic developers resist the pressure from non-Visual Basic developers to abandon this library and make full use of it in their development.



    Here's my two cents about this:

    Yes, we should definately be using a runtime library, it's called the .NET Framework Class Library. Nobody disputes the fact that we need a runtime library or at least that it's a major advantage. But there is a significant difference with former languages that I believe the author missed: any .NET language is not a runtime that is executed by itself anymore, but a tool to compile your text files to the one and only true .NET language called IL (Intermediate Language). And this "real" language has one true supplied library which is the .NET Framework Class Library - which you can recognize by the System namespace.

    Extensions to this library aren't a bad thing, but should be compared to third party libraries used formerly with non-.NET languages. They are definately not an intrinsic part of the language as the article seems to suggest, and should be used with somewhat more caution. The Visual Basic .NET runtime library (recognizable by the Microsoft.VisualBasic namespace) as I see it is a third party extension to .NET (albeit "coincidentally" from Microsoft itself). There's nothing wrong with that as such, but it should only be used whenever necessary.

    However, I strongly oppose to it being included into every project by default, without the possibilty to get rid of it totally. For example, you'll always be able to use the CType, CInt, ... "global functions" which are defined in this library - thereby discouraging you to use their cleaner and faster counterparts (DirectCast, Convert.ToInt32, Object.ToString, ...). So the biggest problem I have with the Visual Basic runtime library is that it makes it too easy to write bad code (global functions, hidden conversions, mistaking language constructs with library classes, ...) and more specifically that its main purpose is to make it seem that Visual Basic .NET is merely an upgrade to VB6 - an idea I strongly oppose to, but that's another story altogether.

    If this library would have been built clean from the ground up with the purpose to serve the programmer with great tools and classes, no problem, but I believe that's already been done - it's called the .NET Framework Class Library.
    Saturday, April 26, 2003 7:49:42 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

     

    All content © 2010, Jelle Druyts
    On this page
    Flickr Schedulr on BBC's Webscape
    Just released: Flickr Schedulr v2.0!
    Just Released: Mayando v1.2!
    What Has Jelle Been Up To (a.k.a. The Last Post)
    Just Released: ProxyMonitor 1.2!
    Updated Again: Setting up Source Server for TFS Builds (v1.2)
    Extracting OLE embedded images from emails in Outlook
    Just Released: Mollom for .NET v1.0!
    Updated: Setting up Source Server for TFS Builds
    My "Deep Dive Into The Guidance Automation Toolkit" presentation now online!
    DSL Tools session at TechDays in Belgium
    Just Released: BuildCop v1.0!
    Just Released: Configuration Section Designer
    Just Released: E = m c² v2.1!
    Setting up Source Server for TFS Builds
    Flickr Schedulr v1.2
    Flickr Schedulr v1.1
    Popfly Explorer: Nifty & Smooth!
    Announcing: Flickr Schedulr
    Proxy Monitor 1.1
    Introducing SmartPass: free password manager for Smartphones
    Just released: E = m c² version 2.0!
    MVP & MSFT!
    Interview on Guidance Automation Toolkit
    Developer & IT Pro Days wrap-up
    Speaking at Microsoft Developer & IT Pro Days 2007 in Belgium!
    Proxy Monitor 1.0
    GAS07: Renaming the solution, signing projects and showing documentation
    Slides for my Visug session on Guidance Automation Toolkit
    Visug Session: Guidance Automation Toolkit
    Type Name Suffixes
    GAS06: Generating Classes
    GAS05: Tuning the C# projects
    GAS04: Adding Project References
    GAS03: Generating A C# Project
    GAS02: Creating A Guidance Package
    GAS01: Introduction To The Guidance Automation June 2006 CTP
    IIS virtual directory names and mapped file extensions
    Unable to load oramts.dll? Try the release version
    The Visual Studio 2005 Toolbox, UserControls and Solution Folders
    A fix for "Could not access network location" when installing a Visual Studio Addin
    Creating a setup package with multiple optional custom actions
    Feline Beta
    More on the Architect role and title
    The Technical Solutions Architect Role
    Closing locked files that break the Team Build
    Visual Studio 2005 Web Application Projects Released!
    InfoPathHelper v1.1: add offline support to InfoPath!
    MSDN Connection & Book of the month discount
    Faking a current HttpContext
    Free chapter from Framework Design Guidelines book
    Enterprise Library Caching without configuration
    Developer & IT Pro Days 2006
    My article is headlining on MSDN
    Article: The Command Pattern In Windows Presentation Foundation
    Speaking at Microsoft Developer & IT Pro Days 2006 in Belgium!
    Xml Documenter Macro
    Article: Customizing generated Web Service proxies in Visual Studio 2005
    Setting a default value for a color property
    My definition of TDD
    A Generic Singleton Implementation
    Downloading Visual Studio 2005 RTM
    Are you ready for the launch?
    Copy To Output Directory
    Generics & Invariance: An Interesting Problem
    Two Cool Tools
    Junior Mistakes
    HtmlEditor 1.0
    Tools Library 1.2
    Notepad Bugreport
    Assert.AreEqual not working properly for Arrays
    ASP.NET 2.0 References
    Deleting source-controlled items
    Releasing E = m c² 1.3
    Static Classes And The HasShutdownStarted Property
    PDC05 Pre-Con: The Art Of Building A Reusable Class Library
    PDC05: The Third Keynote
    PDC05: The Second Keynote
    PDC05 Keynote
    PDC05: Ready To Rumble!
    Introducing Steven Wilssens
    Article: Applied Visual Studio 2005 Enhancements In WeFly247
    WeFly247 now available in Australia
    TechEd Day 2
    TechEd Day 1 - Building Connected Systems Today
    TechEd WeFly247 Swag
    Blog'n My Way To The PDC05
    When ASP.NET 2.0 throws 404's at you...
    Generics at the Indigo service boundary
    WeFly247 Overview Webcast
    Geeks Have More Fun
    WeFly247 Developer Interview
    WeFly247 Screenshots
    Visual Studio .NET 2005 Beta Experience
    Checking for .NET 1.1 Service Pack 1 in an MSI
    DataSets Are Not Evil
    Indigo March CTP "Hello World" Server & Client
    More on .NET Remoting and customErrors
    Batch building NDoc projects
    Branding Guidelines For .NET
    TechEd Europe 2005
    Enterprise Library Lightning Talk
    ASP.NET Comments
    Upgraded to dasBlog 1.7!
    The Corporate Blogging Question
    XML Comments Quick Reference
    Binding the child rows of a DataRow
    Tiny but great DataSet change in Whidbey
    Pushing modified DataSets through Web Services
    Fixing the IExtenderProvider in ASP.NET
    Avoiding WebSite_1 when pulling an ASP.NET Site from SourceSafe
    3rd Belgian Geek Dinner: 21 January 2005 @ Brussels
    PDC 2005!
    Poor man's generics in .NET 1.x
    SmartPart for SharePoint v1.0 released
    Guidelines - a hidden feature for the Visual Studio Editor
    InfoPathHelper: add offline support to InfoPath!
    New Design!
    Register an assembly in COM+ from Visual Studio .NET
    Edit & Continue in C#... Why?
    TechNet evening session on InfoPath & SharePoint
    Belgian Geek Dinner, 2nd Edition
    Enhanced feature request about icon sets in Visual Studio 2005
    Update to my dasBlog hack for showing public referrers and search engine queries
    Launching the default mail client in .NET
    The ASP.NET 2.0 Profile
    MCAD
    There And Back Again: My summer trip to the unmanaged world
    I certainly was - were you?
    Whidbey Beta 1 Available
    My.Family.Feed()
    The first .be-geek-dinner was good!
    Practical details on the .be-geek-dinner
    Brandnew Look: BlogXP Theme
    How Hotmail might surpass GMail and Yahoo! Mail
    MoodTip
    Trouble installing Longhorn WinHEC
    Get your .NET code ready for other platforms
    Shortcomings Of InfoPath 2003
    The Ultimate Reflector Setup
    .be-geek-dinner
    Last-Minute Whidbey Feature Request: Easy Global Interception
    Rotating an image around its center in .NET
    Last month on WeFly247.NET
    Visual Studio Team System
    ADO.NET 2.0 Quickies
    No PDC in 2004
    Easy sorting with generics and anonymous methods
    More on NullReferenceExceptions (or is it less)
    Random Visual Studio Whidbey Annoyances
    The ASP.NET 2.0 ObjectDataSource Revisited - or - Give Me That Extra Inch!
    Working around some ASP.NET AdRotator limitations
    DataBinding in ASP.NET 2.0
    Server Explorer On Steroids
    I've moved to another website!
    Setting a general base class for all your ASP.NET pages
    Visual MemoryLeak .NET 2005
    ASP.NET & XAML - I'm so slow
    I'm a supermodel now!
    Public Referrers & Google-Trash
    Some more info on Microsoft WeFly247.NET
    Inheritance modelling in a relational database
    MVP Global Summit - I want in!
    Trackback between dasBlog and .TEXT not working?
    WeFly247: A piece of the lid just came off
    Whidbey Impressions
    Hot news: Longhorn might support blah!
    An Outlook Annoyance Solved
    Good stuff coming up!
    WSE 2.0 introduces new kind of versioning issue in .NET
    OneShotFlag
    Whidbey Delayed - You Bastards!
    NoInstanceExceptions
    Longhorn Request: Secure & Transactional FTP
    NullReferenceExceptions
    The Ultimate Snake 2 Highscore (no, really)
    SharpToolbox
    .NET To Java Compiler?!
    MoodPrompting
    Killer Font
    TheServerSide.NET Disappointing?
    An Outlook Loveletter
    Two Things About Website Usability
    Useless Optimization
    Visual Studio .NET Tips & Tricks
    Cake anyone?
    Public Referrers In DasBlog
    Migration Step 4: Website
    MSBuild In 30 Minutes
    Volhouden Clemens!
    Events Under The Hood
    ASCII Blitting
    ClickOnce Manifest
    Dessert anyone?
    Whidbey Trouble
    More impressions of the new Whidbey build
    New Whidbey Build
    PropertyGrid
    Partial types trick in PDC build
    Usability issue in Solution Explorer
    The unfortunate return of the Make command in Visual Basic .NET
    Whidbey Time!
    Photo Album Tech Specs
    My Photo Album went online!
    Barcelona!
    Learning To Learn
    Obsolete In Whidbey
    Interface Vs. Abstract Class
    Weird NUnit bug
    Cursors
    Surfacing
    Xml Comments in Include Files
    Ingo Rammer's Architecture Briefings
    Pager Control for ASP.NET
    Converting between C# and VB.NET
    Why do abstract classes need to implement interface members?
    MenuItems
    StatusBar Resize Bug
    Some cool ASP.NET Whidbey preview stuff
    Tools! Tools! Tools!
    A Few Must-Reads
    IXmlSerializable
    Helper Interface For British Chauvinists
    More Annoyances: Slow Webmail
    VB.NET Whidbey Demo
    PDC 2003
    ASP.NET Version Switcher
    Whidbey, Yukon, Longhorn slides
    From Xml Encodings Over Filesystem Tags To Generic Metadata
    ClickableButton
    Web Services Demo: Instant Messaging
    DefaultValue on property with enum value (VB.NET)
    .NET Security Information on No-Touch Deployment
    Ingo Rammer discusses why some Windows Forms controls are sealed
    Visual Studio References
    Various Caches
    Tricky Scope (VB.NET)
    Relative Url's
    .NET Developer Roadmap 2003 - 2005
    Macro Madness
    Dr. GUI says: Don't Lock Type Objects!
    ASP.NET Abstract Base Classes & Visual Studio
    It's oh so quiet...
    Make the page title runat the server in ASP.NET
    Full META Jacket
    Optional Parameters (VB.NET)
    Visual Studio breaking down
    ASP.NET path discovery
    Behind the scenes of events in VB.NET
    The Visual Basic .NET Runtime Library

    Recent Photos
    www.flickr.com
    This is a Flickr badge showing public photos from Jelle Druyts. Make your own badge here.
    Advertising
    Top Picks
    Statistics
    Total Posts: 350
    This Year: 4
    This Month: 2
    This Week: 2
    Comments: 526
    Archives
    Sitemap
    Disclaimer
    This is my personal website, not my boss', not my mother's, and certainly not the pope's. My personal opinions may be irrelevant, inaccurate, boring or even plain wrong, I'm sorry if that makes you feel uncomfortable. But then again, you don't have to read them, I just hope you'll find something interesting here now and then. I'll certainly do my best. But if you don't like it, go read the pope's blog. I'm sure it's fascinating.

    Powered by:
    newtelligence dasBlog 2.0.7226.0

    Sign In