Careful with SPContext.Current…

…as it will be NULL within a Timer Job or Workflow. I have some shared Data Access classes that use SPContext.Current.Web all over the place and now that I want to use them from within a Timer Job, I have to refactor them to take a SPWeb as a Parameter…

Why doesn’t Windows offer a working help system anymore?

If you are developing Windows Desktop applications, you may want to offer context-sensitive help, triggered either by pressing F1 or by clicking on the question mark icon in the title bar and on an element. Back in the old days (starting in 1990 and de-facto ending in 2006), there was WinHelp.

Now, WinHelp wasn’t exactly beautiful and in recent years (after 1996 that is), the “Maximize Database Size” dialog was downright stupid, but WinHelp had all the features a Help system needs: Articles are organized in Chapters and can contain Images, Links and basic formatting. And it allowed your application to open a specific page, providing contextual help.

But most importantly, WinHelp just WORKS. Really, you press F1 and maybe you have to “Maximize Database Size” once, but then it opens. I never ever had a problem with WinHelp.

But Microsoft decided it wasn’t modern anymore. That we needed something new. Granted, WinHelp clearly showed it’s age, and creation of Help Files was somewhat complicated. So they introduced Compiled HTML Help, or CHM. It is a modern Help system, allowing you much more freedom with your layout and styling. It’s a really good format, with one tiny little problem: CHM doesn’t actually work:

Turns out that CHM is displayed through the MSHTML Control (which is essentially an embedded Internet Explorer) and thus it has some security limitations. The most important one is that CHM files on non-trusted (e.g., network) locations simply don’t work.

Now, you may say that this can be resolved. The file can be unblocked, or the path can be set to trusted. An Application Installer could do that. I reply: Doesn’t matter. It’s a Help system. It has to work without configuration. Press F1, get help. If I’m in a situation I need help using my application and my help system tells me that it wants some treats first, it’s a failure. Besides, not every Application has an installer because not every application needs one. A large amount of applications are just DLLs (like the one the above screenshot is from) or ZIPped application files.

So CHM is a complete and utter failure, and Microsoft at least acknowledged that by killing off Microsoft Help 2 and starting a new approach with MAML. However, MAML is not a Help System, it’s a language that can be used as source to be converted into an output format like HTML, RTF or whatever. In other words, Microsoft has created DocBook again without actually solving the problem of displaying help.

The real successor to CHM seems to be the HelpPane introduced in Windows Vista and included in Windows 2008 and 7 as well. Those help files have the extension h1s and a nice little icon, so Windows knows what they are. There is our new Help system, right? Well, try to double click one of those h1s files…

Hmmm… So Microsoft didn’t just register a file type handler for h1s files. Well, can’t be that hard to do, can it?

AP Help – Guided Help – Technical FAQ

Can I launch Guided Help through other means besides the Help Pane?
Yes, but you must create and publish the Guided Help topic through Help. Once you have a Guided Help topic compiled into an H1S file and installed (at this stage only possible for Microsoft and OEM’s), you can launch it directly through a command line if you wish.
The syntax is:

%systemroot%\system32\acw.exe –Extensions GuidedHelp.dll –taskID mshelp://windows/?id=id-of-your-help-topic –ExecutionMode DoIt | ShowMe

For a fast impression copy following text to your run dialog:

%SystemRoot%\System32\ACW.exe -Extensions GuidedHelp.dll -taskID mshelp://windows/?id=3726934c-1315-4c29-bd4d-e42c10225e5a -ExecutionMode ShowMe

Excuse me, but ARE YOU FRIGGIN’ KIDDING ME? Oh, yes you are, let me just quote Microsoft:

Microsoft is committed to providing Help and Support technology in the Windows® platform and will continue to investigate new solutions for software developers.

Sorry, but if “comitted” means “Killing off perfectly working solutions and replacing them with a plethora of broken solutions every two years” then you are absolutely right, because that’s what you are doing. WinHelp survived 16 Years and if you would still ship it with Vista and 7 then it would still be alive. So you as an application developer, what can you do? WinHelp isn’t part of Vista and Windows 7 anymore and you’re not allowed to distribute it with your application. CHM/H1S doesn’t work. What are your alternatives?

Some applications use PDF. They offer rich layout and a Table of Content, however there is no standard reader. Sure, there is Adobe Reader, but you can’t easily control it (e.g., open a PDF on a given page) – if the user has a version that is too old or too new for your application, you may run into issues. And if the user doesn’t have Adobe Reader (or any other PDF reader) installed, you have to explain why someone would download an additional program just because you’re not competent enough to include help. So PDF is not an option.

What about HTML Files? Everyone has a browser, even the short lived Windows 7 E Editions included MSHTML allowing you to at least display HTML within an application. The major downside of HTML is that you can’t control which browser displays it, so you have to stay conservative and make sure old Internet Explorer or Firefox browsers display it (say goodbye to transparent PNGs…). JavaScript maybe tricky (also due to widely spread Extensions like NoScript). And instead of one help file, you have a whole folder. Adding contextual help to your application is somewhat possible, but overall you simply lose the ability to control and test how the help looks and works.

This is possibly the moment where you expect me to say “But after researching all these non-working options, here is the one that works!”. Sorry, can’t do that. I don’t know a single Help system that works on Windows Vista/7/2008. I asked on StackOverflow a long time ago and the consensus was the same.

It’s really sad that a task that seems so simple and straight forward is too hard for Microsoft. Seriously, all that you need to do is to take a simple container format, some basic formatting options, the ability to link and embed images and an API to call Help from your application. If you want, include video support with a standard codec (keeping in mind Windows N/KN Editions)

Simple, easy, straight-forward, hassle-free or in other words: Exactly how a Help System should work. Exactly how WinHelp worked since 1990 before it was brutally murdered. Rest in Piece WinHelp, we miss you dearly.

Dealing with Multiple Time Zones in SharePoint 2010

Organizations that deploy SharePoint farms often have employees in different countries, or at least in different Time Zones. While people in the US (which spans 4 time zones) are pretty comfortable with translating between time zones all the time, the same cannot be said for everyone. Trying to translate between Pacific Time and Middle European Time is just painful, especially since the daylight savings time starts and end at different dates.

With SharePoint 2010 you get the tools to convert the time according to the users time zone. There are two types of Regional Settings: Each Site (SPWeb) has RegionalSettings that specify the Time Zone (and Locale, Calendar etc.) for that site. This is useful if you have sites that are predominately used by people in one time zone. The second type of Regional Settings are the one the user (SPUser) can set (My Settings – My Regional Settings). Those are the same settings as the ones on SPWeb, but each user can specify their own setting.

When storing Dates in code, you have two options:

  • Store the time in local time of the Web and use DatesInUtc = true on a SPQuery to get it back as Utc
  • Store the time in Utc and to not use DatesInUtc on SPQuery

What does that mean? As said, each SPWeb has it’s own Regional Settings. Let’s assume you have a date of 2010-06-14 15:00:00.

If the TimeZone of the SPWeb is Pacific Time (GMT-8) and you query the List using SPQuery, you get back this date. If you however set DatesInUtc = true on the SPQuery, you get back 2010-06-14 22:00:00. SharePoint doesn’t know if 15:00:00 was already UTC, so using DatesInUtc may translate a date twice.

The caveat here is that when storing dates, you would normalize them either to UTC or to the Local Time of the Web. What would you do if some employee from Texas (which runs on Central Time, GMT-6) enters 2010-06-14 15:00:00? You would need to store it either as GMT-8 (so the time becomes 13:00:00) or as UTC (22:00:00).

Needless to say, I prefer to store all dates as UTC if the list isn’t visible to the user directly. Then when querying the list through Code, I can just convert the time to whatever the user’s timezone is:

var user = SPContext.Current.Web.CurrentUser;
// Always perform a Null-Check on SPUser.RegionalSettings
if (user.RegionalSettings != null)
    return user.RegionalSettings.TimeZone.UTCToLocalTime(listDateUtc);
    // User didn't set a time zone, so use the one from the Web
    return SPContext.Current.Web.RegionalSettings.TimeZone.UTCToLocalTime(listDateUtc);

Overall, the option for people to set their own timezones independently from the SPWeb is a fantastic and long needed addition. On the other hand, it does make dealing with times a bit more complex.

If the list is visible to the user, you may need to normalize the times differently (for example, use user.RegionalSettings.TimeZone.LocalTimeToUTC to convert a user time to UTC and then SPWeb.RegionalSettings.TimeZone.UTCToLocalTime to convert the time to the Web-Time).

If you do build custom pages that make use of the Microsoft.SharePoint.WebControls.DateTimeControl then you can just use UseTimeZoneAdjustment=”true” on it to have it automatically convert to UTC and back (SelectedDate will be UTC when accessed through code, but the User’s/Web’s time when rendered).