Geoff Chappell - Software Analyst
Not in some 20 years of working with computers have I ever used commercial software that crashes anything like as often as Expression Web. How often is often? The Problem Reports and Solutions (from the Control Panel) records that Expression Web has “stopped working” for me 29 times in barely a year—and that’s not counting the times that Expression Web hangs on opening its first page (or did, until I devised a work-around).
Only after nine months did I get the luxury of closing this program just for having finished the work that I had wanted to do with it. I have instead become much too familiar with seeing Expression Web acquire the pale glaze that heralds the Windows Error Reporting dialog. Mostly, I have accepted the direction to go online and report the problem to Microsoft. Only once has Microsoft’s server asked for additional information. Never has it responded with any sort of suggested solution—not even that I pay Microsoft more money for a new version that is perhaps available by now.
Sometimes the crash is unexpected. Sometimes it is less of a surprise since the program was already misbehaving more than usual. For instance, a perceived increase in the frequency of the Code Error Detected warning while I write in the Design view, i.e., of Expression Web detecting errors in its own code generation, is not uncommon before the program crashes. Note that it’s not that the program starts misbehaving. It’s that the program misbehaves more than usual. As demonstrated in my growing list of Expression Web bugs, Expression Web is not a program that looks well written once you look more than casually. It has numerous little bugs, inconsistencies and irritations—but its crashes take it beyond the pale.
Of course, I didn’t buy Expression Web as a case study in defective software. I bought it as a tool for the real work of writing up results (of other research) for publication at a website. When Expression Web crashes, I usually attach a debugger to see if the crash is amenable to a quick explanation or even if it looks likely to yield to a couple of hours of inspection, but since most of the crashes occur deep in very large DLLs for which I don’t even have symbol files, it’s usually clear very soon that the time needed for investigation would be seriously disruptive of the work that I bought the program to help me with. The best it seems I shall be able to do is catalogue the crashes, just so that there is at least some record, if only to assure others that they are not alone if they too find that Expression Web crashes often.
All the crashes that I record on this page occurred in the EXPRWD.EXE process, running on 32-bit Windows Vista SP1. The EXPRWD.EXE version is 12.0.4518.1014. (Yes, this is for the original Expression Web. At the time of writing, in March 2009, at least one computer shop in a national chain here in Australia still sells Expression Web only in the original version, though some other chains now advertise Expression Web 2. My experience of the program is that it is so defective that Microsoft ought to be offering free upgrades.)
Arguably the largest component of Expression Web is the DLL that acts as the Microsoft Expression Web Editor. Its name, FPEDITAX.DLL, recalls its origins as the Microsoft FrontPage Editor. In size, it is just short of 9MB, of which well over 6MB is code.
The first Expression Web crash that I have recorded from ordinary use has the Exception Offset 0x0011FF12 in FPEDITAX (version 12.0.4518.1014). It has recurred twice. The immediate cause of the fault is simple enough: the instruction
mov ecx,dword ptr [eax+14h]
has executed while the eax register contains zero. The function in which this happens is a member of a class that is nested at offset 0x08 in a class whose name is CStyleCacheWrapper (according to run-time type information in the FPEDITAX executable).
Four crashes, including one while writing the introduction to this page, cite “unknown” as the Fault Module Name and may therefore be too difficult for Microsoft’s automated data collection to classify. However, at each occurrence, the very first DWORD on the stack at the time of the fault is offset 0x001D27AB in FPEDITAX. What has happened is that FPEDITAX has executed the instruction
call dword ptr [eax+4]
at offset 0x001D27A8 while the eax register is corrupt but is not corrupt enough for the call instruction itself to fault. Trouble is not detected until execution at the more-or-less random target address, but the trigger is plainly in FPEDITAX.
On two more occasions, 0x001D27A8 has itself been cited as the faulting address.
One more crash in FPEDITAX is very similar to the first. At the Exception Offset 0x0057796F, the instruction
mov ecx,dword ptr [eax+14h]
has executed while the eax register contains zero.
The Microsoft Office FrontPage Client Library, FPWEC.DLL (version 12.0.04518.1014), is installed with Expression Web but as a component of the Web Server Extensions. It seems to be the primary module for FPEDITAX’s access to a website of any sort.
FPWEC has crashed nine times with the Exception Offset 0x002A5DD. The circumstances have been as varied as opening a file, saving a file, inserting a hyperlink, and publishing to a remote site. The faulting instruction is
call dword ptr [ecx+8]
but executes with a corrupt ecx. This instruction is intended to call a COM object’s Release method from a destructor for a class that implements an expandable array of pointers to COM objects.
Another crash in FPWEC, now seen four times, is also a case of trying to use an object that has already been released. In this case, the object is implemented using Microsoft’s Attribute Template Library (ATL) and specifically using the CComObject template class. A distinctive feature of this ATL support is that the IUnknown methods are implemented only by the template class. For the object as the programmer defines it, the virtual function table maps the QueryInterface, AddRef and Release methods to the _purecall function. Once the object’s destructor has started executing, and got as far as restoring the virtual function table, any further attempt to use the object by calling an IUnknown method will execute the _purecall function. In FPWEC, this function is at offset 0x000B6718 and its implementation is the standard one of crashing the program by writing to address zero. Note that this offset in FPWEC can execute because of potentially very many different bugs of the same sort.
In one of the observed cases, the function at offset 0x00218EE3 in EXPRWD.EXE thinks it has a pointer to an object (belonging to a class named CFpUrl) and calls this object’s QueryInterface method in the hope of obtaining the object’s IEnumFpUrl pointer, but the object’s destructor has already executed and its QueryInterface method is therefore a _purecall.
The second time this case was observed, the cause combined with the preceding crash. The instruction at offset 0x0002A5DD in FPWEC executes to call a COM object’s Release method, as above, but instead of ecx being corrupt, the problem is that the object’s destructor has already executed and its Release method is therefore a _purecall.
Only once has Windows Error Reporting sought more information about a crash. This same crash has occurred twice while trying to close a page that has frames. Instead of a prompt to save the edited page, MFC80U.DLL (version 8.0.,50727.762) crashed with the Exception Offset 0x00033569. This is very near the start of the exported (documented) function
CFrameWnd *CWnd :: GetParentFrame (void);
The function has gone wrong for being called in a way that not very much can be done about short of running with a debug build that does such things as check the run-time type information of this pointers before doing any actual work. In a retail build, the function reasonably assumes that it will be called with the ecx register containing the address of a CWnd object. When it is instead called with a small number in ecx, the function crashes. The problem really is with the caller, which it should not surprise to know is FPEDITAX.
The problem develops in a routine at offset 0x00435874, which is a virtual member function (at offset 0x08 in the virtual function table) of a class that the run-type type information names as CFrameViewer. A call to a routine at offset 0x00435656 is supposed to obtain a CWnd pointer to be passed as this for the routine at offset 0x00130D5E but instead gets a small number (perhaps an index or handle).
After nearly 2 dozen crashes, I find myself not so much past caring but past even thinking that there’s any point studying crashes when they occur—and then Expression Web surprises with a new case. While calling up the Find and Replace dialog, Expression Web crashed at offset 0x68B2 in RICHED20.DLL. Yet again the immediate problem is an attempted dereference of a NULL pointer.
If a program corrupts memory often enough and randomly enough, it’s only inevitable that the corruption gets discovered by the memory manager, which is what happens with one more crash of Expression Web. The Exception Offset in NTDLL.DLL (version 6.0.6001.18000) is 0x000659C3 in a routine which Microsoft’s published symbol files name as RtlpLowFragHeapFree. This executes to help the HeapFree function, but the address to be freed is no longer the address of a valid heap block. Most likely, the immediate caller (RICHED20.DLL) has done nothing wrong. The address has become invalid because data used for managing the heap has been corrupted. This corruption may have occurred long before, by code from another module, possibly executing in another thread. The immediate caller is most likely involved only as the first whose memory-management request happens to require access to the corrupted data.
For anyone to experience anything like this many crashes during ordinary use of a program from a major software manufacturer is plainly ridiculous. How are standards so poor in the software industry that a program can be so badly misbehaved and yet not have got laughed out of the market during pre-release testing and reviewing?