Winter Challenge

Today our team of 4 competed in the Winter Challenge in Franklin, 45 minutes south of Hobart under the name Four Soft Specialists. The team name is a play on the name of company Software for Specialists which I do some contract work for. Tim was our runner, Paul took on the mountain bike leg, I rode the road bike section, but David hurt his shoulder a few weeks ago and had to find a backup paddler for the kayak leg. More on that soon.

The day dawned foggy with the promise of sun. It was perfectly still as we met at Tim’s place — all except our paddler, who would meet us in Franklin.

After an uneventful but very foggy drive, we rolled into Franklin.  David, after talking to our paddler from last year (as David injured himself last year as well — he’s good at this gig, hey?), finally found a paddler, Sam, who was looking for a team. Perfect match!

Direction Sign heading out of transition.  Do you know which way to go?

Tim was off first with the mass start run leg, an 11km slog over a young mountain, through fields with cowpats and mud! Soon after setting off he settled into a group of runners that felt about right. Then two runners ahead took a wrong turn and one turned up a few moments later looking rather cross — and promptly ran straight into a small tree, knocking it down. Tim kept a bit of a wary eye on this runner after that incident!

Paul keeps a lookout for Tim’s return

Tim finished in about 1:11 1:13:20 — a time he was pretty happy with, and so it was Paul’s turn to head off on the very muddy mountain bike leg. Paul easily beat his time from last year, finishing in 1:15:47, and in describing his effort said he only had four notable stacks. He came back very muddy, but by no means the muddiest of the mountain bike riders! 

Tim running up to the finish line

I find the time in transition waiting for my team mate to return quite hard — one seems to be waiting forever as the seconds tick over glacially, and then suddenly it’s time to bolt for the timing tent. But my time came and off I went.

Last year the Winter Challenge was my first ever cycle race. This year I had a little bit of experience under my belt and a new bike, and so I was hopeful I’d be able to beat my previous time. I also had a heart rate monitor on and planned to work at keeping my HR at 165 as best I could.

I found in the morning that my right leg was a still a little bit sore from a minor strain earlier in the week, but I just hoped it wouldn’t flare up during the race.  Fortunately it seemed ok, just niggling slightly in the last couple of kilometres, and it didn’t really feel like it was slowing me down.

After setting out from Franklin at a decent clip, I found my tempo where my HR seemed to be pretty stable at between 165 and 170 bpm, and started chasing down the riders ahead of me.  It’s always a great feeling to pass another rider in a time trial.  I do feel a little mean when I pass because I know it’s not really very fun being passed by another rider.  If passed by a pro, then one can excuse one’s poor performance — but I am certainly not a pro!  Still, I was tickled that I passed about 25 riders on the course and didn’t get passed by anyone.  Isn’t it pathetic what pleases me?

By this time the sun had come out and it was a glorious day!  Just the lightest breeze and about 15 degrees Celsius.  In the distance I could see another rider, about 300m ahead of me.  As I started to work to catch him, a great big semi trailer roared past me, and immediately had to slow as there was no room to safely pass the other rider.  This in turn forced me to slow down considerably behind the truck — is drafting a truck still bad when one has no choice?  It seemed like I was stuck behind that truck for a long time but it was probably only 20 or 30 seconds before it pulled past the other rider and the road was clear for me again.

Unfortunately, another few kilometres down the road, about 10 cars that just had passed me were stuck in a line behind a timid driver that didn’t seem to want to pass another rider.  This time as I slowed down I saw there was plenty of room on the left, so carefully I rode past the whole file of cars and in the end this didn’t slow me up terribly badly.  I must admit I didn’t like doing this very much but I took lots of care and it all worked out fine.  Dunno what the drivers thought…

The road time trial elevation profile

Now I was approaching the two climbs on the course, and so I figured it was time to suck down a gel.  They help psychologically at least although sticky fingers and sticky jersey pockets are a bit gross!  I really couldn’t find a comfortable speed on the climbs, which was a bit unexpected.  I found them both hard going, and just couldn’t find a tempo which suited me, ending up changing up and down much more than I should have.  I took a drink from my bottle and then wished I had brought water instead of Gatorade.  Still, I made it to the top, and  got a chance to practice the ‘keep the power on over the crest’ technique.

The return leg is just a matter of keeping the tempo up and keeping focused.  The wind was still light, so it didn’t impact me very badly.  About half way back I had another drink and went over a bump just as I tried to put the bottle back, and dropped it.  Annoying!  Some might suggest I did it on purpose but honestly sir it was an accident.  Really truly.

There were fewer ‘rabbits’ for me to catch on the return leg (or else maybe I was going slower), and as the last 2km came up I tried to push that little bit harder but just wasn’t able to push my speed up much at all.  I rolled over the line with my wife and daughters cheering me on — they’d just managed to make it down to the line after church.  My average HR was 166: right where I wanted it, and my cadence was 100.80, again right on the button.  My GPS time was about 1:02:00, but official times are not yet online and my official time was 1:02:52 — so we did waste a bit of time in transition it seems.  Hoping to see them soon!  My time last year was 1:06:06, so I am very happy with the result this year.

I tagged our paddler Sam and off he went.  I didn’t see him go because I had to focus on not falling over on my way back to our gear in the transition area.  I guess that means I rode reasonably hard then!  We then had only a few minutes to wait before Power Sam came back over the line to finish for the day.  It was a treat to watch his super-efficient paddling style and listen to him being called out as a very fast finisher by the announcers.  All these superlatives?  It turns out that our Sam Norton is one of Australia’s fastest paddlers (please do note how I am claiming him for some rub-off kudos).  Sam’s finishing time was 0:46:20, a full 3 minutes ahead of the next fastest competitor in our class!  Again, I’m writing this before the organisers have had a chance to post the official results online but expectations are high that he made the top kayak time.  I’ll update the post with times when the results come online.

My bike in transition.  Nope, no aero bars.  Maybe next year!

Unfortunately with only 46 minutes for Sam’s leg, it was just not possible for Sam to make up the 45 minutes or more that we had lost overall already!  We finished overall in 12th place.

Roy and Ben are a couple of fellas I ride with at lunch times.  They were both marshalls on the day — and both said ‘hi’ to me while I stared at them like a complete idiot.  I find it hard to recognise other riders when they aren’t wearing their helmets and bike gear!  I really have to give kudos to them and to the whole Endorfun team — it was an awesome event and heaps of fun.  Really well organised, really friendly, and really well supported by local businesses.

We dropped into the ever lovely Petty Sessions Cafe for lunch while waiting for the presentations.  So great to be able to sit outside in beautiful spring-like weather after a couple of weeks of rain and general cold weather.

And after presentations — in which I won nothing, not even a spot prize (!) — we went home. 

Reproducing the TrustedInstaller.exe leak

Earlier today I blogged about the extraordinary amount of time it was taking to install updates on a Windows 7 virtual machine.  I believed that the issue came down to TrustedInstaller.exe taking what seemed to be an ever increasing amount of RAM and opening an inordinate number of handles.

After writing this blog post, I wondered who at Microsoft might be interested in the issue.  I ended up tweeting a link to Mark Russinovich in the hope that it would whet his interest (the blog did have pictures of Process Explorer, one of Mark’s great Sysinternals utilities ☺):

And verily, Mark did reply:

As Mark was kind enough to respond I knew I had to go and reproduce the issue!  So I built a new VM of Windows 7 Ultimate x64 with stock standard settings, but only 1GB of RAM (oops):

Stock Windows 7 Ultimate x64 install on a VM

I configured Performance Monitor to trace Working Set and Handle counters on the TrustedInstaller.exe process, and saved the settings into a Data Collector Set so that I could capture the whole update process.
 
Then I started the Windows Update:

Windows Update: I selected the additional important update, but none of the optional updates

Three hours later (I did say whoops about the RAM, didn’t I?) Windows finished installing updates, and I was able to generate a report from the Data Collector Set that showed the following graph:

Graph of Working set and Handle Count + summary of Working Set detail

and:

Handle Count detail

At the end of the process we see 34,500 open handles, and a peak of 600MB memory usage.  As one would expect, the RAM usage went up and down quite a bit as updates were installed.  More disturbingly, the Handle Count really did not decrease significantly through the process.  So the real message of the graph is the trend for both the green Handle Count, and the red Working Set, which are both clearly heading the wrong way: up.

There were fewer updates to install than with the other VM, which was also installing Microsoft Office Updates; I’m guessing this had an impact on the overall numbers.

Minor note: I didn’t fix the time zone for the VM.  I wasn’t really doing this at 3AM!

Fun on the mountain

Joey was away over the weekend, so the girls and I drove up Mt Wellington on Sunday afternoon. It was zero Celsius and completely fogged in. They thought it was great!

On the Saturday, Bethany did an epic bike ride with me: 2 bridges in Hobart, with lunch at the Botanic Gardens, and sorbet in Moonah.  Hannah got a ride on the back of my bike.  I underestimated the length and difficulty of the planned route, and Bethany needed a lift from her grandmother for the last 8km back to the car.  Still 15km is an impressive effort for a 7 year old!

Does Microsoft’s TrustedInstaller.exe have a leak?

When building a virtual machine today, I was curious as to why the Windows Update install process was taking such a long time.  The initial run had over 100 different updates so I expected it to take a while, but it was even slower than expected. So I fired up Process Explorer and discovered that TrustedInstaller.exe had allocated over 1GB of RAM to do its updates.  As I watched, this number kept ticking up until all the updates were installed, with a maximum memory usage of roughly 2GB.  The handle count tended to increase over time as well, hitting a maximum of 50500 handles as I watched.

This VM was setup with 2GB RAM so this was causing lots of swap activity, which I guess is what was slowing down the update process so much.

I grabbed a screenshot at Update 94 when I decided that something was not right with this:

And another shot with the final update, Update 103.  50,484 handles is a whole lotta handles.

As each update finished, both the handle count and the memory usage would drop, but never back to the level before the update.

This really does suggest that TrustedInstaller.exe is leaking memory.  So is this intentional?  Whether by design, or by accident, it’s horrible.  Microsoft?

Update: Mark asked me why one would leak by design.  I suggested that one possibility might be that TrustedInstaller locks files or objects to prevent other processes from making changes to them before Windows is restarted, as the update process does not complete until Windows restarts.

The Browser is the Platform.. Welcome back to 1995.

This morning I jumped onto my netbook, and noticed it was running rather sluggishly.  This netbook has only 1.5GB of RAM but normally does fine for web browsing and email.  But this morning it was woeful, taking 45 seconds to open a new mail message.  I fired up Process Explorer to find out what was going on.

Oops, looks like I left Firefox and Internet Explorer running over the weekend.  Big mistake.  Come Monday morning, Firefox had allocated over 1150MB of RAM, and Internet Explorer was not much better on 928MB.  All on a machine with only 1536MB of RAM.  It’s swap time!

What about on my primary development machine.  Firefox is running.  3 pages open, all fairly static.  Should be okay to leave over the weekend, right?

Nope.  Firefox needs a mere gigabyte of RAM to cope with this complex demand.  Firefox suffers the most from the RAM addition, but none of the major browsers are safe.  Chrome, Safari, Internet Explorer 9 are all addicted to RAM.

Some web pages tip the browsers over the edge more quickly than others.  Facebook, Google+ and Google Docs are the worst offenders.  Twitter is usually much better: in that first example, the Twitter instance of IE is on 100MB, and Facebook on 928MB.  Firefox was running Google Docs.  Oops.

The browser developers have in the past blamed web page developers.  I guess that’s a bit like blaming buggy applications for platform instability in Windows 95.

If the browser is the platform, then it looks like rebooting the platform is back in style.  Excuse me for a moment after I post this blog: I need to reboot my browser.

Problems with Internet Explorer 8, print templates and standards compliance

The print engine that we use for one product I work with is based on Internet Explorer’s custom print templates functionality.  This actually works really well, and gives us lots of flexibility and generally is pretty straightforward to use.  Unfortunately, we have run into a bit of a problem recently when trying to print documents in IE8 standards compliance mode: multiple page documents print blank pages, are missing content or sometimes even fail to start printing.

I have been able to reproduce this issue using Microsoft’s own printtemplates.exe example (link in Introduction of article), with only a couple of minor changes to trigger the problem.

Symptoms
The issue arises when all of the following specific conditions are met:

  1. The document to be printed is in IE8 standards compliance mode.  This is mostly easily forced with the X-UA-Compatible META element:
    <META HTTP-EQUIV='X-UA-Compatible' CONTENT='IE=8' />
    
  2. The document to be printed is greater than 1 page long.
  3. The print template uses LAYOUTRECT elements of differing sizes.  This is a common requirement for letters which may have an address section or expanded letterhead at the top of the first page, but will have a much smaller letterhead on subsequent pages.

The Baseline
We’ll use the printtemplates.exe sample provided by Microsoft.  The sample Template3.htm in the example shows how to dynamically create LAYOUTRECT elements.  Here’s what happens before we make any changes to the example:

Print Template sample application, working with defaults
Print Preview, defaults, page 1
Print Preview, defaults, page 2

As shown in the screen shots, the print preview displays as expected.

Reproducing the Problem

With Internet Explorer 8, the problem can be reproduced as follows:

  1. Start printtemplates.exe, and select Template3.htm.
  2. Click the Page Source button.  In the HTML file that is opened, add the META element as the first child of the HEAD element.  This forces the page into IE8 standards compliant mode.  Save the file as sample.htm:
    <HTML>
      <HEAD>
        <META HTTP-EQUIV='X-UA-Compatible' CONTENT='IE=8' />
        <TITLE>Print Template Samples</TITLE>
      </HEAD>
    
  3. Back in printtemplates, click the Template Source button.  In the HTML file that is opened, make the highlighted change to the OnRectComplete function.  This small change means pages other than the first page have a smaller LAYOUTRECT than the initial page.  Save this file as template.htm.
    function OnRectComplete()
    {
        if (event.contentOverflow == true)
        {
            document.all("layoutrect" + (iNextPageToCreate - 1)).onlayoutcomplete = null;
    
            newHTML  = "";
            newHTML += "STYLE='height:5.5in'/>";
            newHTML += "";
    
            pagecontainer.insertAdjacentHTML("beforeEnd", newHTML);
            iNextPageToCreate++;
        }
    }
  4. Enter the full paths of sample.htm and template.htm into the respective fields in the print template sample, then press ENTER in the page field to load the modified sample.htm.

When you press Print Preview now, the print preview window will show blank pages instead of the expected content, and will have only 2 pages instead of the 5 or more that were shown previously:

The print template application with modified page and template ready to roll
Failing print preview, page 1
Failing print preview, page 2

If either of the changes are removed from the sample.htm and template.htm, the problem does not occur.  In Internet Explorer 9, the problem also occurs but appears to be resolved when using IE=9 in the META tag.  However, as Internet Explorer 9 is not available for Windows XP, this is not a viable solution for us.

Workarounds
We’ve found a few workarounds.  None of these are very viable for us but I list them for completeness:

  1. Don’t use LAYOUTRECT elements of differing sizes.  This is a non-starter for us.
  2. Don’t use IE8 standards compliant mode.  This would mean rewriting a number of reports and some complicated CSS to workaround bugs in IE7’s standards compliant mode, but may be a way forward.  Or use quirks mode with all the joy that brings.
  3. Use IE9.

Adding overlay notification icons to Google+’s taskbar icon in IE9

In the same way as I did for Twitter in an earlier post, I have now created a bookmarklet that adds notification overlay icons to Google+’s Taskbar icon, when pinned in IE9.  Currently Google+ only has a small favicon, and I have not found a way to tweak this once the page has loaded, so it’s not quite as pretty as Twitter’s icon, but it’s better than nothing!

Enjoy.

Right click Google+ Overlay Icon (IE9 only!) and click Add to Favorites to create the bookmarklet.

Formatted code for the bookmarklet:

(function() {
    var fLastCount = -1;

    window.setInterval(function()
    {
      try
      {
       var res = document.getElementById('gbi1');
       if(res) {
          var nNewCount = parseInt(res.innerHTML,10);
          if(nNewCount == NaN) nNewCount = 0;
          if(nNewCount != fLastCount) {
            var ico = nNewCount > 5 ? 'error' : 'num_'+nNewCount;
            window.external.msSiteModeSetIconOverlay('http://ie.microsoft.com/testdrive/Browser/tweetfeed/Images/'+ico+'.ico',
              nNewCount+' notifications');
            fLastCount = nNewCount;
          }
        }
        else if(fLastCount != 0) {
          window.external.msSiteModeClearIconOverlay();
          fLastCount = 0;
        }
      } catch(e) { }
    }, 1000);

    })();

Netbook Surgery

Joey’s computer had a little accident recently and is due to go to hospital shortly for some serious transplant work once a donor can be located.

However before sending Joey’s netbook to the doctor we wanted to take a backup of the drive, as Acer will wipe and reinstall the drive in the repair process. Joey had a recent backup but not everything from the last few days. Oh yes, her netbook doesn’t start up.

So I figured I’d swap the hard drive out of her netbook into mine, and back it up onto a USB hard drive. It was 2 minutes work to pull the drive out of her netbook:

2 exterior screws, 1 interior screw and 4 screws on the hard drive enclosure. Quick and easy.

Mine proved a little more complicated. To get to the hard drive, I had to remove 21 screws, unclip the casing, remove the keyboard, the mainboard, the wi-fi card, a secondary circuit board, undo the screen hinge mount, and unplug 6 different cables:

It worked though: I was able to take a backup of the drive and wipe it, before reassembling both computers. Yay!

Now to see how repairable Joey’s netbook is… This bit is Acer’s job!

Demystifying printing with the Microsoft WebBrowser control and ShowHTMLDialogEx

I’m writing up these notes in order to document what has been a long and painful process, involving much spelunking through MSHTML.DLL and IEFRAME.DLL to try and understand what Internet Explorer (or more accurately, the WebBrowser control) is doing and how to correctly use the semi-documented interfaces to provide full control over a print job.

The original requirement for this mini-project was to provide tray, collation, and duplex control for a HTML print job using IHTMLDocument2.execCommand(IDM_PRINT), with a custom print template.  These functions had been supported through a 3rd party ActiveX component, but this component proved to be incompatible with Internet Explorer 9 (causing a blue screen would you believe!), and the company providing the component was defunct, so it fell to me to re-engineer the solution.

After considerable research, I found some sparse documentation on MSDN suggesting that one could pass a HTMLDLG_PRINT_TEMPLATE flag to ShowHTMLDialogExand thereby duplicate and extend the functionality of the print template.  In particular, the __IE_CMD_Printer_Devmode property that could somehow be passed into this function would give us the ability to control anything we liked in terms of the printer settings.

Too easy.  Much too easy.  The first stumbling block was trying to discover the type of the pvarArgIn parameter to ShowHTMLDialogEx. A variant array seemed sensible but did not work.  It turns out that this needs to be an IHTMLEventObj, which can be created with IHTMLDocument4.CreateEventObject.  You can then use IHTMLEventObj2.setAttribute to set the various attributes for the object.

Then there were questions about what IMoniker magic was needed for the pMk parameter.  And more questions about the most appropriate set of flags.  Diving into the debugger to examine what Microsoft did answered both of these questions — it was a simple CreateURLMonikerEx call, no need to bind the moniker or other magic, and the flags that Microsoft used were HTMLDLG_ALLOW_UNKNOWN_THREAD or HTMLDLG_NOUI or HTMLDLG_MODELESS or HTMLDLG_PRINT_TEMPLATE for a print job, or HTMLDLG_ALLOW_UNKNOWN_THREAD or HTMLDLG_MODAL or HTMLDLG_MODELESS or HTMLDLG_PRINT_TEMPLATE for a print preview job.  Yes, that is both HTMLDLG_MODAL and HTMLDLG_MODELESS!

Next, what variant type should the __IE_BrowseDocument attribute be?  VT_DISPATCH or VT_UNKNOWN?  The answer is VT_UNKNOWN — things just won’t work if you pass a VT_DISPATCH.  I also came unstuck on the __IE_PrinterCmd_DevMode and __IE_PrinterCmd_DevNames attributes.  These need to be a VT_I4 containing an unlocked HGLOBAL that references a DEVMODEW structure.  I’ll leave the setup of the DEVMODEW structure to you: there are a lot of examples of that online.

However, even after overcoming these hurdles (with copious debugging to understand what MSHTML.DLL and IEFRAME.DLL were doing), there were other issues.  First, the print template was unable to access the dialogArguments.__IE_BrowseDocument property, with an Access Denied error thrown.  Also, HTC behaviors would fail to load as the WebBrowser component believed that they were being referenced in an insecure, cross-domain manner.  And finally, JavaScript in the page being printed was failing to run — and this JavaScript was required to render some of the details of the page.

I knew that Microsoft actually pass a reference to a temporary file for printing in the __IE_ContentDocumentURL attribute.  So I saved the file to a temporary file, which also required adding a BASE element to the header so that relative URLs in the document would resolve.  But the problems had not gone away.

All three of these problems in reality stemmed from the same root cause.  The security IDs for the various elements — the print template, the document being printed, and the HTC components — were not matching.  So I embarked on an attempt to find out why.  At first I wondered if we needed to bind the moniker to a bind context or storage.  That was a no-go.  Then I looked at the IInternetSecurityManager interface, which a developer can implement to provide custom security IDs, zones and more.  Sounds logical, right?  Only problem is that the ShowHTMLDialogEx function provides its own IInternetSecurityManager implementation, which you cannot override (and its GetSecurityID just returns INET_E_DEFAULTACTION for the relevant URLs).  Yikes.

I was starting to run out of options.  As far as I could tell, we were duplicating Microsoft’s functionality essentially identically, and I could not see any calls which changed the security for the document so that it would match security contexts.

Finally I noticed an undocumented attribute had been added to the HTML element in the temporary copy of the page: __IE_DisplayURL.  And as soon as I added that to my file, referencing the original URL of the document, everything worked!

Now, this is all fun (and sounds straightforward in hindsight), but without some code it’s probably not terribly helpful.  So here’s some code (in Delphi, translate to your favourite language as required).  It all looks pretty straightforward now(!), but nearly every line involved blood, sweat and tears!  This is really not a complete example and hence does not compile but just covers the bits necessary to complement the better documented aspects of custom printing with MSHTML.  Please note that this example uses the TEmbeddedWB component for Delphi, and that temporary file cleanup has been excluded.

procedure THTMLPrintController.StartPrint(FPrint: Boolean);
var
  FDeviceW, FDriverW, FPortW: WideString;
  FDevModeHandle, FDevNamesHandle: HGLOBAL;
  pEventObj2: IHTMLEventObj2;
    procedure SetTempFileName;
    begin
      FTempFileName := GetTempFileName('', '.htm');
    end;
    { SaveToFile: Saves the current web document to a temporary file, adding the required BASE and HTML properties }
    procedure SaveToFile;
    var
      FElementCollection: IHTMLElementCollection;
      FHTMLElement: IHTMLElement;
      FBaseElement: IHTMLBaseElement;
      FString: WideString;
    begin
      FElementCollection := webBrowser.Doc3.getElementsByTagName('base');
      if FElementCollection.length = 0 then
      begin
        FBaseElement := webBrowser.Doc2.createElement('base') as IHTMLBaseElement;
        FBaseElement.href := webBrowser.LocationURL;
        (webBrowser.Doc3.getElementsByTagName('head').item(0,0) as IHTMLElement2).insertAdjacentElement('afterBegin', FBaseElement as IHTMLElement);
      end
      else
      begin
        FBaseElement := FElementCollection.item(0,0) as IHTMLBaseElement;
        if FBaseElement.href = '' then FBaseElement.href := webBrowser.LocationURL;
      end;
      FElementCollection := webBrowser.Doc3.getElementsByTagName('html');
      if FElementCollection.length > 0 then
      begin
        FHTMLElement := FElementCollection.item(0,0) as IHTMLElement;
        FHTMLElement.setAttribute( '__IE_DisplayURL', webBrowser.LocationURL, 0);
      end;
      with TFileStream.Create(FTempFileName, fmCreate) do
      try
        if webBrowser.Doc5.compatMode = 'CSS1Compat' then
        begin
          FString := '';
          Write(PWideChar(FString)^, Length(FString)*2);
        end;
        FString := webBrowser.Doc3.documentElement.outerHTML;
        Write(PWideChar(FString)^, Length(FString)*2);
      finally
        Free;
      end;
    end;
    { Configured the printer, assuming we've already been passed an ANSI handle }
    procedure ConfigurePrinter;
    var
      FDevice, FDriver, FPort: array[0..255] of char;
      FDevModeHandle_Ansi: HGLOBAL;
      FPrinterHandle: THandle;
      FDevMode: PDeviceModeW;
      FDevNames: PDevNames;
      FSucceeded: Boolean;
      sz: Integer;
      Offset: PChar;
    begin
      Printer.GetPrinter(FDevice, FDriver, FPort, FDevModeHandle_Ansi);
      if FDevModeHandle_Ansi = 0 then
        RaiseLastOSError;
      FDeviceW := FDevice;
      FDriverW := FDriver;
      FPortW := FPort;
      { Setup the DEVMODE structure }
      FSucceeded := False;
      if not OpenPrinterW(PWideChar(FDeviceW), FPrinterHandle, nil) then
        RaiseLastOSError;
      try
        sz := DocumentPropertiesW(0, FPrinterHandle, PWideChar(FDeviceW), nil, nil, 0);
        if sz < 0 then RaiseLastOSError;
        FDevModeHandle := GlobalAlloc(GHND, sz);
        if FDevModeHandle = 0 then RaiseLastOSError;
        try
          FDevMode := GlobalLock(FDevModeHandle);
          if FDevMode = nil then
            RaiseLastOSError;
          try
            if DocumentPropertiesW(0, FPrinterHandle, PWidechar(FDeviceW), FDevMode, nil, DM_OUT_BUFFER) < 0 then
              RaiseLastOSError;
            FDevMode.dmFields := FDevMode.dmFields or DM_DEFAULTSOURCE or DM_DUPLEX or DM_COLLATE;
            FDevMode.dmDefaultSource := FTrayNumber;
            if FDuplex
              then FDevMode.dmDuplex := DMDUP_VERTICAL
              else FDevMode.dmDuplex := DMDUP_SIMPLEX;
            if FCollate
              then FDevMode.dmCollate := DMCOLLATE_TRUE
              else FDevMode.dmCollate := DMCOLLATE_FALSE;
            if DocumentPropertiesW(0, FPrinterHandle, PWideChar(FDeviceW), FDevMode, FDevMode, DM_OUT_BUFFER or DM_IN_BUFFER) < 0 then
              RaiseLastOSError;
            FSucceeded := True;
          finally
            GlobalUnlock(FDevModeHandle);
          end;
        finally
          if not FSucceeded then GlobalFree(FDevModeHandle);
        end;
      finally
        ClosePrinter(FPrinterHandle);
      end;
      Assert(FSucceeded);
      { Setup up the DEVNAMES structure }
      FSucceeded := False;
      FDevNamesHandle := GlobalAlloc(GHND, SizeOf(TDevNames) +
       (Length(FDeviceW) + Length(FDriverW) + Length(FPortW) + 3) * 2);
      if FDevNamesHandle = 0 then RaiseLastOSError;
      try
        FDevNames := PDevNames(GlobalLock(FDevNamesHandle));
        if FDevNames = nil then RaiseLastOSError;
        try
          Offset := PChar(FDevNames) + SizeOf(TDevnames);
          with FDevNames^ do
          begin
            wDriverOffset := (Longint(Offset) - Longint(FDevNames)) div 2;
            Move(PWideChar(FDriverW)^, Offset^, Length(FDriverW) * 2 + 2);
            Inc(Offset, Length(FDriverW) * 2 + 2);
            wDeviceOffset := (Longint(Offset) - Longint(FDevNames)) div 2;
            Move(PWideChar(FDeviceW)^, Offset^, Length(FDeviceW) * 2 + 2);
            Inc(Offset, Length(FDeviceW) * 2 + 2);
            wOutputOffset := (Longint(Offset) - Longint(FDevNames)) div 2;
            Move(PWideChar(FPortW)^, Offset^, Length(FPortW) * 2 + 2);
          end;
          FSucceeded := True;
        finally
          GlobalUnlock(FDevNamesHandle);
        end;
      finally
        if not FSucceeded then GlobalFree(FDevNamesHandle);
      end;
      Assert(FSucceeded);
    end;
  { Creates the IHTMLEventObj2 and populates the attributes for printing }
  procedure CreateEventObject;
  var
    v: OleVariant;
    FShortFileName: WideString;
    FShortFileNameBuf: array[0..260] of widechar;
  begin
    v := EmptyParam;
    pEventObj2 := webBrowser.Doc4.CreateEventObject(v) as IHTMLEventObj2;
    pEventObj2.setAttribute('__IE_BaseLineScale', 2, 0);
    GetShortPathNameW(PWideChar(FTempFileName), FShortFileNameBuf, 260); FShortFileName := FShortFileNameBuf;

    v := webBrowser.Document as IUnknown;
    pEventObj2.setAttribute('__IE_BrowseDocument', v, 0);
    pEventObj2.setAttribute('__IE_ContentDocumentUrl', FShortFileName, 0);
    pEventObj2.setAttribute('__IE_ContentSelectionUrl', '', 0);  // Empty as we never print selections
    pEventObj2.setAttribute('__IE_FooterString', '', 0);
    pEventObj2.setAttribute('__IE_HeaderString', '', 0);
    pEventObj2.setAttribute('__IE_ActiveFrame', 0, 0);
    pEventObj2.setAttribute('__IE_OutlookHeader', '', 0);
    pEventObj2.setAttribute('__IE_PrinterCMD_Device', FDeviceW, 0);
    pEventObj2.setAttribute('__IE_PrinterCMD_Port', FPortW, 0);
    pEventObj2.setAttribute('__IE_PrinterCMD_Printer', FDriverW, 0);
    pEventObj2.setAttribute('__IE_PrinterCmd_DevMode', FDevModeHandle, 0);
    pEventObj2.setAttribute('__IE_PrinterCmd_DevNames', FDevNamesHandle, 0);
    if FPrint
      then pEventObj2.setAttribute('__IE_PrintType', 'NoPrompt', 0)
      else pEventObj2.setAttribute('__IE_PrintType', 'Preview', 0);
    pEventObj2.setAttribute('__IE_TemplateUrl', GetPrintTemplateURL, 0);
    pEventObj2.setAttribute('__IE_uPrintFlags', 0, 0);
    v := VarArrayOf([FShortFileName]);
    pEventObj2.setAttribute('__IE_TemporaryFiles', v, 0);
    pEventObj2.setAttribute('__IE_ParentHWND', 0, 0);
    pEventObj2.setAttribute('__IE_HeaderString', webBrowser.Doc2.title, 0);
    pEventObj2.setAttribute('__IE_DisplayURL', webBrowser.LocationURL, 0);
  end;
  procedure InstantiateDialog;
  var
    FWindowParams, FMonikerURL: WideString;
    FMoniker: IMoniker;
    FDialogFlags: DWord;
    varArgIn, varArgOut: OleVariant;
    res: HRESULT;
  begin
    varArgIn := pEventObj2 as IUnknown;
    varArgOut := Null;
    FMonikerURL := GetPrintTemplateURL;
    OleCheck(CreateURLMonikerEx(nil, PWideChar(FMonikerURL), FMoniker, URL_MK_UNIFORM));
    if FPrint then
    begin
      FWindowParams := '';
      FDialogFlags := HTMLDLG_ALLOW_UNKNOWN_THREAD or HTMLDLG_NOUI or HTMLDLG_MODELESS or HTMLDLG_PRINT_TEMPLATE;
    end
    else
    begin
      FWindowParams := 'resizable=yes;';
      FDialogFlags := HTMLDLG_ALLOW_UNKNOWN_THREAD or HTMLDLG_MODAL or HTMLDLG_MODELESS or HTMLDLG_PRINT_TEMPLATE;
    end;
    res := ShowHTMLDialogEx(0, FMoniker, FDialogFlags, varArgIn, PWideChar(FWindowParams), varArgOut);
    if res <> S_OK then raise EOSError.Create(SysErrorMessage(res));
  end;
begin
  SetTempFileName;
  SaveToFile;
  ConfigurePrinter;
  CreateEventObject;
  InstantiateDialog;
end;

Update 14 July: This code is not our production code: I’ve stripped out bits and pieces and tried to keep the bits that are somewhat relevant. Don’t worry too much about the ConfigurePrinter details — the takeaway is the HGLOBAL. I must also apologise for the atrocity that is the SaveToFile function. That’s what you get when working with legacy versions of software. Internet Explorer also won’t reliably work with non-ASCII content there unless you toss a BOM into the start of the stream.

Wet and windy

Joey was off at a birth, and so the girls and I were stuck at home on a wild and windy Saturday morning in the middle of winter. It was raining and blowing a gale, but actually not too cold… At least 7 or 8 degrees.

So, perfect weather for a family bike ride, right? I pulled out the trusty “family bike” and the bike trailer, which we hadn’t used for some time. The girls were thrilled! I took the opportunity to swap off the flat pedals and put on some SPDs from my defunct road bike. Pulled on all my winter gear, rugged up the girls, put the lights on, and off we rode.

Got 1km from home and realised that we’d left the girls’ helmets behind! Groan. Funny how one can ride such a long way but turning around and going home is so hard. Still, it didn’t take long so no real harm done 🙂

We rode carefully down the long descent into town, the girls nice and dry, me not so much. First stop, bike shop to pick up a spare part for my roadie. Then the coffee shop.

Jam Packed is a great place to stop and have a coffee — especially if you are on a bike. The atrium has plenty of space to park a bike or 20 and being a big dry open space the smelly, wet bike clothes hopefully don’t bother other patrons as much as in some other coffee shops!

Coffee and mango juice done, it’s time to head off to the library, where we stayed and read books until it closed (unfortunately rather early, 2pm).

The picture shows us parking the bike at the library. We couldn’t help ourselves and borrowed a few books, which we wrapped up well in plastic bags in the boot of the trailer.

Then it was off to Salamanca Fruit Market for lunch stuff. Hannah and I went for sushi, and Beth had a roll. Now for the trek home.

It was still blowing a gale, but the rain had eased for a bit. Riding up Macquarie St, we were heading straight into a block headwind, a real gale! I was struggling along as we hit the real hill past the Cascade Brewery. I read later that wind gusts were up to 80 km/h!

I used the toe-on-the-chain technique to drop the chain onto the smallest chainring. I really must fix that front derailleur sometime soon. We slogged away until we reach the bridge, where I was quite happy to pause for an emergency loo stop for the girls!

The remainder of the ride up the hill with 60kg of trailer doing its best to drag me back down again was definitely the slowest I’ve ever climbed Strickland Ave, by a long way! Fortunately we were sheltered somewhat from the wind by the curves of the hill.

We even got a tailwind along Chimney Pot Hill Rd but as we turned into Ridgeway we were hit by the full force of the gale and we came nearly to a complete halt! I was grinding along in bottom gear, on the flat mind you, out of the saddle, crouched over my bike like a Tour de France contender, face contorted as I forced my way against the fury of the wind — and rain — and into our driveway. The girls jumped out of the trailer and bolted for the shelter of the house while I struggled to open the door to put the bike and trailer away. I let go of the trailer for a second to brace the door against the wind and had to chase it half way down the driveway. Finally I dragged it into the basement, let the door slam in the wind and heaved a sigh of relief! Home!

The next morning my legs feel like I’ve just ridden a serious race…  And Strava has awarded me a 5th place in the climb “Hydro to Strickland” — because I’ve never ridden that route before, and because there were only 4 other riders who have ridden that route!