Wednesday, September 29, 2004


egilh project #9: Windows 95

What: I started translating but passed through managing external translation vendors before I ended up doing what I liked best; technical specialist. The way we translated Windows 95 was drastically different from the previous translation work; instead of editing include and resource files, we modified the resource sections in the final English version (i.e. directly in the .dll or .exe). Translating the resource files gave fast turn-around as the change was visible immediately. It was also a mess some times as some times the word "Desktop" should be translated, other times it’s the internal name in Windows for the desktop and everything boke if you translated it.

The tools for translating were, as usual, pretty bad so I spent some time writing tools that automated the process (more posts later on these later).

When: mid 90s :-)

Lessons learned:

  • Installing and using Windows 95 "blindfolded". Seeing the characters doesn't really make much of a difference when you try to troubleshoot a problem in Greek, Russian and Turkish….

  • Patience. I did a lot, but a lot, of builds of Windows 95. It came in tons of flavors; 5 1/4 floppies, 3 1/2 floppies (small and large build), CDs, ... Each build took the best part of the day as the new .CAB building tool optimized the organization of the files etc. In the few weeks after the US team RTMed I worked nights (and days) to build the most critical languages (French/Spanish)

  • Work smarter not harder:  You can do a lot of stuff if you mange your "dead time" well. The next bunch of projects are internal projects I worked on while installing/building etc

  • Working 2 years  on a "never ending" project is not for me. I hope it is better working in the Redmond dev team or I feel really sorry for the Longhorn developers

  • Brad Silverberg sounds a lot like "Brat Silverberg" (a -very- good friend almost made the mistake to his face)

  • Microsoft really knows how to throw a release party. I had the pleasure of attending both the internal one and the public one with Jay Leno.

  • The Windows 95 T-shirts are incredibly strong. I still have, and use, a few of them while working on the house/garden.

  • Writing software that can be translated/localized. The string "%s printing on %s" should have been translated to "Printing %s on %s" in some languages but it was impossible in Windows 95 (which explains the funny wording in some localized versions). This is one of the things they got right in .NET. Format strings now use positionsl, so "%1 printing  %2" can -finally- be translated to "Printing %2 on %1" without changing the software

Tuesday, September 28, 2004


Using the POOM from .NET CF 1.0

I organize my work (and a fair bit of my life) with the Outlook task list. In Outlook I have a customized view that shows all tasks due today, grouped by priority. This feature does not exist on the Pocket PC so I implemented a simple task manager with embedded VB which I used a lot on my old iPAQ. I didn't want to install the VB runtime on my new iPAQ 4150 so I decided to rewrite it in .NET CF.

I didn't get far before I hit the first hurdle though: accessing Pocket Outlook with .NET CF.

The problem is that Pocket Outlook is only available via the IPOutlookApp COM interface and .NET CF does not support calling COM components. The latest news I have from Microsoft is that this will be fixed in the next version. I quickly found the commercial PocketOutlook In The Hand component from InTheHand for accessing POOM. Both The Windows Mobile Team Blog and Omar Shahine refer to the InTheHand component as the way to go. But, in the end, I decided to continue on my DIY quest for several reasons;

  • I wanted to learn how to access COM components from .NET CF
  • I didn't feel like paying $40 for a component I will only use on a personal project

The only way to call a COM component with .NET CF is to write a standard DLL that wraps the COM methods. A lot of work, as POOM has a lot of objects, methods and properties. The great news is that you can find all the free tools and source code you need on the Microsoft web site(s):

The POOM C++ wrapper support all the objects (at least the ones I have checked). It also comes with a .NET CF client that shows how to access the Pocket Outlook contacts.  The .NET CF client is fairly limited as it only imports some of the C++ managed DLL methods. It is very simple to extend as you only have to add new DllImport's. I added the methods (get and set) for accessing the DueDate and it compiled like a charm but bombed on the Pocket PC when I tried to set the DueDate. I found the problem after having a closer look at the C++ code. The (put) DueDate method signature was:

HRESULT ITask_put_DueDate(ITask* thisPtr,

           DATE st)


  return thisPtr->put_DueDate(st);



The problem is the st argument. It passes an OLE Automation DATE which is 8 bytes.  .NET CF doesn't support arguments larger than 4 bytes (32 bit) so the source code has to be changed to pass a pointer (4 bytes) to a date:

HRESULT ITask_put_DueDate(ITask* thisPtr,

           DATE* st)


  return thisPtr->put_DueDate(*st);



The C# code has to be fixed as well. The correct DllImports are:

[ DllImport("PocketOutlook.dll", EntryPoint="ITask_get_DueDate") ]

private static extern int do_get_DueDate(IntPtr self,

                                          ref double dueDate);


[ DllImport("PocketOutlook.dll", EntryPoint="ITask_put_DueDate") ]

private static extern int do_put_DueDate(IntPtr self,

                                          ref double dueDate);


Almost there, one more thing do do: add a C# property wrapper for the DllImports

public System.DateTime DueDate




            double doubleDate = 0.0;

            PocketOutlook.CheckHRESULT(do_get_DueDate(this.RawItemPtr, ref doubleDate));                   

            return PocketOutlook.OleDateToDateTime(doubleDate);





            double doubleDate = PocketOutlook.DateTimeToOleDate(value);

            PocketOutlook.CheckHRESULT(do_put_DueDate(this.RawItemPtr, ref doubleDate));




The code uses the .NET 1.0 CF Date conversion source code I posted earlier to convert the DATE arguments to System.DateTime

Fix the bugs in the Microsoft code you have a free .NET CF POOM client. Cool Or What?

Monday, September 27, 2004


GPS programming

Writing Your Own GPS Applications has a good intro to GPS programming. It covers the basic GPS commands and explains the terms you need to know when working with GPS. Examples in VB.NET with plenty of comments.

Not sure if I manage to resist the temptation of buying a Bluetooth Tom Tom much longer. Maybe I can integrate it with the SMS Bluetooth manager I wrote earlier this year? Or write a client on my laptop that automagically configures the IP depending on my location?


Friday, September 24, 2004


VB/OLE DATE conversion in .NET CF 1.0

For a  .NET CF 1.0 program I'm working I have to use a VB/OLE Automation DATE from C#. The .NET methods FromOADate and ToOADate are not available on .NET CF 1.0 so I ported the VB.NET implementation of FromOADate and ToOADate to C#



public static System.DateTime OleDateToDateTime(double date)


      const Int32 hex1 = 86400000;              //0x5265c00

      const Int64 hex2 = 59926435200000;        //0x3680b5e1fc00

      const Int64 hex3 = 315537897600000;       //0x11efae44cb400

      const Int32 hex4 = 10000;                       //0x2710

      const double real1 = 86400000;     

      const double real2 = 2958466;

      const double real3 = -657435;


      if (date >= real2 || date <= real3)


            throw new ArgumentException("Arg_OleAutDateInvalid");



      double toAdd = date >= 0 ? 0.5 : -0.5;


      long loc0 = (long) (date * real1 + toAdd);

      if (loc0 < 0)


            loc0 -= (2 * getReminder(loc0, hex1));


      loc0 += hex2;


      if (loc0 < 0 || loc0 >= hex3)


            throw new ArgumentException("Arg_OleAutDateScale");



      return new DateTime(loc0 * hex4);        



public static double DateTimeToOleDate(System.DateTime date)


      const Int32 hex1 = 86400000;              // '0x5265c00

      const Int64 hex2 = 599264352000000000;    // '0x85103c0cb83c000

      const Int64 hex3 = 31241376000000000;     // '0x6efdddaec64000

      const Int32 hex4 = 10000;                       // '0x2710

      const Int64 hex5 = 864000000000;          // '0xc92a69c000

      const double real1 = 86400000;     


      long ticks = date.Ticks;


      if (0 == ticks)


            return 0.0;



      if (ticks < hex5)


            ticks += hex2;



      if (ticks < hex3)            


            throw new ArgumentException("Arg_OleAutDateInvalid");



      long loc1;

      long loc0 = (long) ((ticks - hex2) / hex4);


      if (loc0 < 0)


            loc1 = getReminder(loc0, hex1);

            if (0 != loc1)


                  loc0 -= (hex1 + loc1) * (2);




      return ((double) loc0) / real1;




private static long getReminder(long value1, long value2)


      long res;

      res = value1 - value2 * (value1 / value2); 

      return res;      


Saturday, September 18, 2004


egilh project #8: DAO/RDO

What: DAO/RDO . Well, almost :-(
I got the job and went back to Dublin to celebrate with a few pints with the lads only to discover that the lawyers couldn't get me a work permit. 6 months more work experience and the job would have been mine...

When: mid 90s

Lessons learned:

  • I hate lawyers
  • First time on a 10 hour flight (BA business class via London Heathrow, 3 movies and great food/junk)
  • First time in the USA
  • First time driving a car with automatic gear
  • First jet lag
  • First, but not last, time on Microsoft campus
  • First time I had 7 job interviews for 1 job. (The future boss and 6 future colleagues)

I had great fun during the interviews. Some issues were pretty basic like create a C++ object representing a person but there were more fun ones;

  • You have X coins. One of them is heavier than the other ones. How many times do you have to weight them to find out which coin is the heaviest?
  • In the reception of the building I had my interview there were two stones  showing the current day (01..31) of the month. What is the minimal number of sides needed on each of the stones?
  • Philosophical discussion over atol(); how does it return errors and how would I return errors if I had designed the API
  • Discussing fast cars over a Mexican lunch

Friday, September 17, 2004


egilh project #7: Excel 95

What: Started the translation work on Excel 95 but I quickly moved on to Windows 95 as I got the chance to work on technical issues instead of translations. One of the few things I accomplished was to improve the slow Excel macros. Version 1 first sorted the list and then used a binary lookup to make it many, many, times faster as the lists were large. The second version skipped macros altogether and used a binary tree in C to translate flat text files.

When: mid 90s

Lessons learned:

  • Marketing people don't understand programmers. They insisted on translating Visual Basic for Applications. if then/else is a 'universal' language as far as I'm concerned. At least they had the common sense not to repeat the mistake with the more serious languages.

  • The Dutch make lasagna with bananas (!) and the Irish serve French Fries with theirs

  • You can learn a lot from Dr. Dobbs

Thursday, September 16, 2004


An even wetter Wednesday

The soft rain continued so we decided to go to Firenze. Long queues in front of the most famous museums like 'Galleria di Uffizi'. Or at least they looked long to me. I was told the can get many times longer during high season. Got a great view from piazza Michelangiolo, but all things considered I wasn't impressed with Firenze. It's a big town and the country side in Toscana has more to offer if you ask me. Guess I had too high expectations going there.

Nice places attract tourists which in turn attract illegal parking attendants (don't pay anybody, use the automatic ticket machines) and pick pockets (one backpack opened but nothing important stolen as the valuables were well hidden). I just can't help it: thieves bug me in a major way. The pick pockets reminded me of something I saw in an English TV series decades ago: a guy had fastened fish hooks inside his jacket pocket. Put the hand in the wrong pocket and you're stuck. Wonder if it is legal to go 'fishing' like this in Italy? Guess it's safer not to though: catch the wrong fish and you could wake up with the head of a horse in bed...

(Update: formatting changes)


A wet Tuesday

When Pio II became pope, he reconstructed his home town Pienza to be like the ideal town according to the teories of Leon Battista Alberti. The town was nice but some of the shops really stank of their variety of 'pecorino' (sheep cheese). I can't stand 'normal' pecorino cheese, far less their variation. They pack the cheese in leaves from valnut trees and dig them down. So far so good. But then they make a major mistake and dig them up when they have started stinking like half digested milk. Why bother? It sounds very much like surströmning to me. So, needless to say, I avoided the gastromania shops and bought a nice Chianti Riserva instead.

Left some more money in another wine shop after trying the different local wines. Ended up with some Vino Nobile di Montepulciano. Not something to brag about to wine experts but way better than the stuff in the local super market.

Thunder storms in the region, so it was 'soft rain' most of the evening. Anybody who has lived in Ireland knows what I mean (just subtract the wind) Found a nice osteria close to the characteristic main square, il Campo. Wild boar stew and tagliatelle with wild boar sounds boring but I love it so I stuff myself whenever I'm in Toscana and live on the memories for months :-)

(Update: formatting changes)

Wednesday, September 15, 2004


San Gimignano

OK, I know it's stupid to buy stuff in a turist place like San Gimignano but I couldn't help myself when I tried some of the wild boar ham and salami yesterday. This time I left my money at 'La Buca'

Update:  Don't forget to taste the ice cream if you visit the main square. The gelateria has won both national and international ice cream competitions and you can really taste the difference.


3 Madonne

Arrived at a nice 'agriturismo' close to Siena on Monday afternoon. 3 Madonne ( opened last June and the house with the apartments has just been restored. A bit in the middle of nowhere but in a great position for going on day trips to Siena, San Gimignano, Firenze, Montepulciano etc. The Toscana region site also has some info:

Went for a late evening walk in the old part of Colle di val d'Elsa. The newer, lower part, is nothing special but the old part on the hill is lovely. Loads of shops with crystal and a few displays with people making crystal and carving designs in the crystal. Makes sense as the town produces 90% of the crystal of Italy and accounts for 14% of the world production

(Update: formatting changes)

Monday, September 13, 2004


egilh project #6: Excel 5

What: Microsoft Excel 5. Translated parts of the software and the screenshots. Made daily builds on a 486 with OS/2.

When: early 90s

Lessons learned:

  • Builders doesn't trust technical translators. The build kit I got, deleted all the intermediate files. As a result it took the best part of a day to make a build even if you just changed a string. Not the way I like working. I'm pretty much a feature driven development guy: add a feature or fix a bug, make a quick build and make sure it works instead of adding tons of stuff and hoping for the best. I changed the make files and brought the build time to minutes instead of hours.
  • You can do anything with Excel! Through the years I have seen it used for everything. The majority of the translation work was done using macros in Excel as well. We had huge lookup lists of existing translations and (slow) macros that did search and replace.
  • In Microsoft you can retire at 35! The boss of my boss retired to study art.

(Update: formatted the text better)

Saturday, September 11, 2004


One blog to go, please

I'm hitting the road for a week or so. I wasn't planning on posting anything until I got back but then I discovered Pocket Blog Writer

Pocket Blog Writer has a very simple GUI but that's a feature, not a bug, on a Pocket PC. The important thing is that it works like a charm! A couple of important features like support for multiple blogs and editing posted items are under development.

I'll post a few more items to the 100 programs/products I have worked on if I have GPRS coverage.

Thursday, September 9, 2004


Egilh project #5: Dos 6.*

What: Translated the Dos 6.* retail boxes, screenshots and new software parts to Norwegain

When: Early 90s

Lessons learned:

  1. Don't go outside without an umbrella in Dublin
  2. Forget lesson #1: Don't go outside with an umbrella as the first real winds will tear it inside out. Wear a rain jacket instead
  3. Forget lesson #2: Do hide in a pub if it starts to rain


SQL Server Performance newsletter

I subscribed to the SQL Server Performance newsletter earlier this year and I usually learn something for each issue.

Did you for example know that:

  • Calling a stored procedure sp_something is slower than usp_something?

  • A very fast (but not 110% accurate way) of doing SELECT COUNT(*) from <table_name> is

                 SELECT rows
                 FROM sysindexes
                 WHERE id = OBJECT_ID('<table_name>') AND indid < 2


Tuesday, September 7, 2004


egilh project #4: hospital infections and re-operation analysis

A data entry and analysis DB implemented with Access 1.1. The operator could enter information about operations (open wound, day hospital etc), track infections and operations that had to be repeated (because of infections or other causes)

When: early 90s

Lessons learned:

  • Don't get sick in August when the regular doctors go on vacation. The number of re-operations and infections raise drastically and drop just as fast when they come back

  • Access is great for making data entry applications and prototypes. But, you can go crazy if you want your application to work in a slightly different way

  • Don't use too much water when washing the stairs (I had an apartment in a house with serveral nurses while working on the application and I had to take my turn washing the stairs)


Monday, September 6, 2004


egilh project #3: MS DOS device driver for mounting remote disks

When: early 90s

What: MS DOS disk device driver
I had loads of fun with this project and learned even more. I sometimes used LapLink bit to move files between different machines but I never liked that I had to use a separate software for working on my local PC and a remote machine. I liked my file manager just fine thank you and I wanted to use it for managing files on the "remote" computer as well. LapLink worked via a serial cable so it was painfully slow as well. So in the end I manage to find someone who could sponsor my new project; a MS DOS device driver in assembler that mounted the HD on a remote machine in such a way that it looked like a local HD.

A serial cable would have been too slow for what I wanted to do, so I started testing (pin by pin) the different pins I could use for reading/writing via the parallel port on a standard PC. I ended up with a cabling system that allowed me to transfer 4 useful bits of information in parallel per transmission. A lot faster than the standard serial cable.

It was a -great- feeling when I mounted the disk of another PC via the parallel cable and launched Norton disk utilities which scanned the remote disk as if it was a local disk and did not report a single error.

Lessons learned:

  • Don't trust documentation blindly. The book I had documented the interrupt as accepting a pointer to a disk structure. I was saved by a (competing) book in another bookshop which correctly documented the pointer, to pointer to disk structure.

  • Don't trust other people blindly either. The idea was the I should develop the system with another guy. We met every few days and I always got the answer "great, almost there" when I asked about the progress. I got  pissed off when I found out a couple of days before our release that the guy hadn't written a single line of code on the host side but was still trying to figure out the requirements. I went in a weekend coding frenzy, delivered the project on Monday morning and slept all of Monday afternoon + Tuesday. (I ended up coding the “host“ side as a C program instead of a host side device driver which would have been a lot neater)

  • I hate hardware. I initially got my system up and running using 4 bit info per transmit between the client/server but I had to lower it to 3 bits as my Olivetti laptop didn't like my original cabling design :-(

  • Debugging a device driver written in assembler is “different“ from working with the Turbo Pascal debugger. I ended up with sending status codes to video RAM so I could see the different states of my device driver in the upper right hand corner of the screen

Sunday, September 5, 2004


egilh project #2: Unix System V Chat

When: late 80s

What: IRC like multi user chat. The “GUI” was pretty similar as well. A lot more secure though as you could only send messages.

Lessons learned:

  • Curses :-)
  • X Windows moves the input focus with the mouse so don't put the coffee cup too close to the mouse
  • Scket handling under *nix

Friday, September 3, 2004


100 programs/projects I have worked on

KC Lemson, Shrini Kulkarni and Ron Richardsonare are blogging 100 things about themselves. Neat idea so here goes; 100 programs/projects I have worked on. To much stuff for one post so I'll split them in smaller groups. Larry Osterman is in a nostalgic mood as well, so I guess it is contagious.

egilh  project #1: Simple CRM

When:  mid 80s

What: Simple CRM solution implement in Turbo Pascal.

Lessons learned:

  • You can learn anything from reading the  Borland online help and their examples.
  • Pointers are VERY powerfull
  • You run out of memory if you allocate memory…
      unless you release the memory when yo don't need it anymore

The first version of the program worked as a charm on my PC (8086 with 640kb memory:-). It also worked a couple of times on the clients PC but it had to be rebooted after a while because it ran out of memory as  I allocated, but never released, the memory…

I learned my lesson of memory allocation then and haven’t forgotten it yet

Wednesday, September 1, 2004


Using the ASP.NET cache from …. anywhere

Once upon a time….

I needed a smart way to cache data in memory. It should have a fixed expire date but also be smart enough to delete items that were not used in a long time. Frist I implemented a very simple system using a HashTable with a garbage collection thread.

A simplified version:

private static System.Collections.Hashtable _memoryCollection = new System.Collections.Hashtable();

public void setItem(CacheItem cacheItem)

   _memoryCollection[cacheItem.cacheID] = cacheItem;

public CacheItem getItem(string cacheID)

   CacheItem cacheItem = (CacheItem) _memoryCollection[cacheID];

if (null != cacheItem)
// Note; no interlocked. Who cares if we loose a hit or two
return cacheItem;

A garbage collector can be scheduled using::

System.Threading.Thread thread = new Thread(new ThreadStart(garbageCollector));

Which does something like this:

System.DateTime now = System.DateTime.Now;
System.Collections.ArrayList itemsToDelete =
new System.Collections.ArrayList();

foreach (DictionaryEntry item in _memoryCollection)
  CacheItem cacheItem = (CacheItem) item.Value;

  if (cacheItem.expireDate < now || 0 == cacheItem.usageCount)
    itemsToDelete.Add (item.Key);
// Use this if you really care: Interlocked.Exchange(cacheItem.usageCount, 0); 
cacheItem.usageCount = 0;

// Delete the items
foreach (string key in itemsToDelete)


Then I saw the light and used the ASP.NET built in cache. The really cool thing is that it works from anywhere;, a standalone .exe, a .net component registered in com+ (i.e. inherits from EnterpriseServices). And it gives you garbage collection, sliding and fixed expire etc for free

The clue is not to hit the stones together but to add a reference to System.Web.dll and use the System..Web.HttpRuntime.Cache to get a reference to the cache.


private static System.Web.Caching.Cache _memoryCache;

// Somewhere:
_memoryCache = System.Web.HttpRuntime.Cache;

public void setItem(CacheItem cacheItem)

   _memoryCache.Insert(cacheItem.cacheID, cacheItem.cacheValue, null, cacheItem.expireDate, System.Web.Caching.Cache.NoSlidingExpiration, cacheItem.priority, null);

public CacheItem.getItem(string cacheID)

   return  (CacheItem) _memoryCache.Get(cacheID);

YMMV but I hope the code above gives the idea.