Introduction
Let's start with personal history of mine.
Once upon a time, I wrote a small application, strictly for personal use, that will notify me whenever time gets close to someone's birthday. Nice way to let your friends know that you think of them. They don't need to know that your computer is keeping an eye on it.
When I first installed Windows Vista, I noticed the Sidebar (yes, I know, it is hard not to notice it) and I noticed how lots of things were based on XML - most noticeably Contacts. I saw lots of gadgets related to them (Address book style and similar), but there was nothing that would pull up an upcoming birthday and/or anniversary and remind me of that.
That is what this gadget will be about.
Downloads
You will notice that there are two downloads here. One is the source of the whole thing and other is just the gadget.
JavaScript
There are few limitations to Windows Vista Sidebar gadget. One that I find most disturbing for a .NET developer is lack of support for using it's objects. It is annoying to hear how Microsoft is all .NET about when new operating system component knows only COM. I needed some way to call my own .NET code.
There are a few ways, most notably using .NET interop to allow COM usage, but the problem with that is that you need to register it somehow. That just doesn't fit with install-free nature of gadgets. And manual entering of registering information in the registry is also something I don't like.
Only other way is to use the possibility of reading data from standard output and placing it in some tag.
var shell = new ActiveXObject("WScript.Shell");
var exec = shell.Exec("file.exe /arg1 /arg2");
document.getElementById('Output').innerHTML = exec.StdOut.ReadAll();
The downside of that choice was fact that I must use some HTML coding in my program to achieve this.
Since configuration also uses HTML/JavaScript programming style, one downside of this decision was that we need to send every configurable parameter down the path of command line argument.
C#
It was quite obvious to me that I need to create a Console project. However, it proved the wrong choice as when you call a Console application, the Console Window was always shown which is annoying. The right path was to build a normal Windows Application. You still get Console output just without the black box. Just what we need.
Parsing of command line arguments was a necessity and I took liberty to reuse one of my classes for that purpose so the only thing to notice here is that it works.
Once we got everything loaded, parsing could start. It just goes recursively into each subdirectory and puts each file in the array. I know that this wastes a small amount of memory since not all Contact files will have been date defined, but it makes it easier to debug and definitely easier to follow.
Birthdays and anniversaries are retrieved next. They all go through a small state machine that does parsing of XML via XmlTextReader
. A class is created out of that data and within it calculates the next birthday date.
try {
thisYearDate = new System.DateTime(System.DateTime.Today.Year,
this._date.Month, this._date.Day);
} catch (System.ArgumentOutOfRangeException) {
thisYearDate = new System.DateTime(System.DateTime.Today.Year,
this._date.Month, this._date.Day - 1);
}
if (thisYearDate >= System.DateTime.Today) {
this._nextDate = thisYearDate;
} else {
try {
this._nextDate = new System.DateTime(System.DateTime.Today.Year + 1,
this._date.Month, this._date.Day);
} catch (System.ArgumentOutOfRangeException) {
this._nextDate = new System.DateTime(System.DateTime.Today.Year + 1,
this._date.Month, this._date.Day - 1);
}
}
Some exception handling is needed for those who were born on 28th of February.
All elements of that array are sorted using that date.
int IComparable<OneDay>.CompareTo(OneDay other) {
return System.DateTime.Compare(this.NextDate, other.NextDate);
}
It is good to have generics.
Everything to do after that is to output the first few dates in HTML format (remember that we are directly filling 'Output' tag). That is all about HTML tags. The only difference you will notice is that it goes to XmlTagWriter
which provides an easier way of filling XML data. Since it internally uses the well known XmlTextWriter
, I don't think that there will be any problems.
Gadget
After everything is done, packing must follow. Since I used all files as content, Visual Studio was "forced" to output everything into the same directory. Just delete .pdb files and ZIP everything else. Zipped file must be renamed to .gadget extension and there you have it.
Possible improvements
Memory usage
Memory usage could be improved by not loading all birthdays at once and then sorting them later. One just needs to make one small list and compare every date with threshold for this list (calculated by all previous dates in it). Why I didn't do this? Well, it is easier to debug this way - you see exactly what the order of upcoming birthdays is, and, at least for me, there are only a limited number of birthdays that I enter in contacts. Memory consumption should be pretty low this way too.
Not calling exe
Parsing of XML files could be done from JavaScript itself. That way, one could avoid calling a separate executable. Why I didn't do it that way? It would take me much more effort to do it and not much would change for the end user. Moreover, this way I get all the benefits of modern programming language procedures and there is nothing I can't do.
Faster updates
It would be nice to have some FileWatcher
to update the birthday list whenever a change is detected. But this timeout based refresh is quite OK. Once you enter all birthdays, they tend to be static in nature. There is no need to burden our system with real-time watching.
History
2007-04-06
- Fixed bug with date parsing
- Repacked source in order to remove Read-only attribute