Category Archives: Computing

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.

Fixing Windows font scaling without restarting

Windows 7 and Windows Server 2008 include the ability for each user to set their font scale. This is fantastic, except for a legacy complication: the old bitmap fonts MS Sans Serif, MS Serif and Courier have specific versions for each font scale, but these are never changed after Windows is installed. In previous versions of Windows, the fonts were replaced with the correct versions for the selected font scale, which is why a system restart was required

This means that these bitmap fonts can be out of sync with the currently selected font scale. This is typically only a problem for legacy applications, but it is ugly in those cases!

More background is available at the MSDN blog http://blogs.msdn.com/b/developingfordynamicsgp/archive/2009/11/25/windows-7-bitmap-fonts-and-microsoft-dynamics-gp.aspx and the follow-up post http://blogs.msdn.com/b/developingfordynamicsgp/archive/2009/12/02/more-on-windows-7-bitmap-fonts-and-dpi-settings.aspx

In our situation, it was even worse: the client was running a Remote Desktop Services environment, where restarting the server was really out of the question.

So I wrote a little fix-it app that dynamically adjusts all the font scaling registry settings and installs the correct fonts for the selected font scale.  You may need to log off and log on again, but in most cases, no restart is required.  It is setup for 100% and 125% only, and I provide this app here only as a useful tool.  No support or warranties, etc, etc.  Use at your own risk!

Download Fontsizefix.zip.

Update 1 Jul: As I discussed this blog with Peter Constable, I realised that I didn’t really describe what the tool did.  So: fontsizefix updates the various metrics in HKCU\Control Panel\Desktop, and a couple of LogPixels registry settings in HKLM\SYSTEM\CurrentControlSet\Hardware Profiles\CurrentSoftware\Fonts and HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI\LogPixels, updates the fonts key in the registry to point to the correct versions of MS Sans Serif, MS Serif and Courier, and then RemoveFontResource and AddFontResource in order to get the correct version of the font loaded.  I’m sure it’s not 100% but it got us over a hurdle with the terminal services environment.  For purposes of support, it was easiest to make a tool that did the whole lot rather than document a bunch of registry tweaks which are easy to trip over on, and then we figured we might as well make it available to other users as well…

Notes on a virus

 This morning a coworker logged in and was greeted with a dialog box he did not expect:

There is no disk in the drive. Please insert a disk into drive \Device\Harddisk2\DR6.

This was immediately suspicious.  We quickly fired up Process Explorer to identify what could be causing the problem, and found a cmd.exe process that had been spawned by winlogon.exe, with the following command line:

“C:\Windows\System32\cmd.exe” /c echo [autorun]>>F:\autorun.ini
Alarm bells ringing!  There’d be no reason a legitimate app would be trying to do this…  F: is a card reader on my coworker’s machine, which he has never used, and hence is empty, which is why this message arose in the first place.  He is running the most recent version of F-Prot, with up to date signatures, but that did not pick up this malware.
Now we knew there was something malicious afoot on his computer, we decided to try and capture as much information about the issue as possible.  Unfortunately, we missed the first few steps that the virus was taking, and have no information on how it got onto to the computer in the first place, but here’s the rest of what we found.
The first thing we spotted was that winlogon.exe had a version resource indicating it was created by Microsoft, but it also had no valid certificate, and the details were not consistent:
Curiously enough, we also spotted that it was a .NET application.  I haven’t seen a .NET virus before…  Looking at the process details, we could see it was running from %AppData%, which is certainly not kosher:
We took a look at the folder, and saw a couple of other files, spread.exe and MSNMessengerAPI.dll.  Looks like it may spread through Windows Messenger?
In Process Explorer, sure enough, spread.exe was running.  Then something which was concerning: it appeared to be writing to what I believe is Firefox’s certificate database, accessing cert8.db and key3.db:
We also spotted a third process was spotted, svchosts.exe, which was stored in another folder: %LOCALAPPDATA%\Microsoft.  It may not be related but seems to be similar in construction.  This process was notable for a handle leak; at the time we spotted it, it had over 4700 handles open, mostly thread and duplicate file handles.  Now it was time to kill the processes.  I was expecting, given 3 processes, some complex watchdogging to prevent the processes being killed by the end user, but no, we were able to kill one after the other without any interference.
Now that there seemed to be no suspicious processes running, we took a look at the registry.  It fairly naively installed itself into HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run.

The HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce key also contained a simple reference to winlogon.exe:
And curiously enough, HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows key had a reference to winlogon.exe as well:

At this stage, we zipped up the various virus files and deleted them, restarted, and so far, no residue has been detected.  It appears that the virus did not manage to elevate itself and attain administrator access, which was reassuring.
Finally we ran the following SysInternals command to give us a rough idea of what winlogon.exe references:
strings -n 16 winlogon.exe
This command picked up function import references to sqllite3 and cryptography functions, which strengthened my fears about Firefox’s certificate store.  Very few interesting strings were spotted, which suggests that they may have been encrypted.  We opted to uninstall and reinstall Firefox, creating a new profile and all, but I remain concerned about whether or not Internet Explorer’s certificate store has been compromised.
We have not diagnosed how this virus spreads, but have submitted it to F-Prot for evaluation.  I’ll be interested to see the outcome.

VMWare to Hyper-V: it’s never as easy as promised

Today I took on the task of converting one of our last VMWare Server-based VMs to Hyper-V.  This was an XP VM that is used for accounting, and with lots of software and configuration the idea of reinstalling Windows did not appeal.

So I used vmdk2vhd to convert the disk to Hyper-V format, making sure that I followed the initial preparation steps as noted in various blogs.

No joy, ended up with the dreaded “A disk read error occurred. Press ctrl+alt+del to restart” error.  with some research found sites that suggested using FIXBOOT, FIXMBR and CHKDSK in the XP Recovery Console, but I didn’t expect much success with this route and I didn’t get any.

As a side note, I could not remember the local Administrator password for the box, and ended up using Petter Nordahl-Hagen’s excellent offline password editor.

Finally, found a blog that explains how to resolve the problem using TestDisk by CGSecurity.  Ran that, went through the various steps, and finally ended up with a VM that would at least boot to Safe Mode.  After a few minutes of churn, Windows installed various drivers and I could boot to normal Windows and complete Hyper-V Integration Services install.

Or could I?  Nope.  Windows wanted to be reactivated before I could login — but I no longer had network drivers.  As I was not willing to spend the time on the phone with a Microsoft operator when at home late at night, I booted back into Safe Mode, and ran the Integration Services install from there.

No!  Another error: “Setup cannot upgrade the HAL in this virtual machine”.  Yet another blog post (and comment thereunder) to the rescue.  I ended up having to copy HALACPI.DLL from %windows%\servicepackfiles\i386 into %system%.  Rinse, reboot.  And now follow the primary steps in that same blog post – deleting the “Standard PC” entry, and now finally it’s installing.

Wait, no, it failed.  “An Error has occurred: One of the update processes returned error code 1601.”  Google again.  Wow, a Technet discussion thread returns a possible solution.  So booting into Recovery Console again to delete some VMWare drivers (VMWare Tools would not uninstall because it had a Windows Installer reference to a network drive on a machine that had been decomissioned…  Yuck.)

No way, Jose.  A bit more research and eventlog spelunking and I realised that Integration Services setup was trying to start Windows Installer — which doesn’t run in Safe Mode.  Yay!  Now what: can’t install network drivers in Safe Mode, can’t login in normal mode because I have no network drivers for boot.  Ah!  Turns out adding a legacy network adapter may give me a way forward here.  Gosh, that actually worked.  Reactivated Windows successfully.  Now I am booted in normal Windows mode, should be able to install Integration Services, right?

Well, I could now use Remote Desktop to connect to the machine — great, now I had a mouse again – and am now installing Integration Services.  We’ll see how it goes — but I’m not waiting around for it any longer tonight.

Was it worth all the pain?  Probably not.  At least I collected a few useful Hyper-V resources.  Perhaps a rebuild would have been smarter but I really like to understand the hows and whys of these problems.  And perhaps this may help someone else.

Playing with the Strava API

I had a couple of free evenings – hard to believe but it happened – and decided to try and have some fun with Strava’s API.  Here’s what I came up with…

The site will give a list of all the segments I’ve attempted, grouped by various classes of segment.  In this view, it orders within each segment by my ranking:

I can also filter those segments to show the ones I’ve never had a good crack at, or the ones where I am just slightly behind the leader, for example:

Then I decided to have some fun with the geolocation services built into iPhone and other mobile browsers (and now some desktop browsers as well!)  With this, I hacked up a little page that would show me all the segments within 5km of my current location, plus the all important competitive data to help me decide which segment to go for today!

Main site: http://durdin.net/cookbook/strava/query.php

Mobile site: http://durdin.net/cookbook/strava/nearby.php

No guarantees on these pages – they are setup to work only for my data at present, unless you know the secret incantations to populate the database with your data as well.

Why /cookbook/?  Well, because I already had a database setup and libraries for it.  In other words, I was being lazy 🙂