When programs assume that the system will never change, episode 3


One of the stranger application compatibility puzzles was solved by a colleague of mine who was trying to figure out why a particular program couldn't open the Printers Control Panel. Upon closer investigation, the reason became clear. The program launched the Control Panel, used FindWindow to locate the window, then accessed that window's "File" menu and extracted the strings from that menu looking for an item that contained the word "Printer". It then posted a WM_COMMAND message to the Control Panel window with the menu identifier it found, thereby simulating the user clicking on the "Printers" menu option.

With Windows 95's Control Panel, this method fell apart pretty badly. There is no "Printers" option on the Control Panel's File menu. It never occurred to the authors of the program that this was a possibility. (Mind you, it was a possibility even in Windows 3.1: If you were running a non-English version of Windows, the name of the Printers option will be something like "Skrivare" or "Drucker". Not that it mattered, because the "File" menu will be called something like "Arkiv" or "Datei"! The developers of this program simply assumed that everyone in the world speaks English.)

The code never checked for errors; it plowed ahead on the assumption that everything was going according to plan. The code eventually completed its rounds and sent a garbage WM_COMMAND message to the Control Panel window, which was of course ignored since it didn't match any of the valid commands on that window's menu.

The punch line is that the mechanism for opening the Printers Control Panel was rather clearly spelled out on the very first page of the "Control Panel" chapter of the Windows 3.1 SDK:

The following example shows how an application can start Control Panel and the Printers application from the command line by using the WinExec function:

    WinExec("control.exe printers", SW_SHOWNORMAL);

In other words, they didn't even read past the first page.

The solution: Create a "decoy" Control Panel window with the same class name as Windows 3.1, so that this program would find it. The purpose of these "decoys" is to draw the attention of the offending program, taking the brunt of the mistreatment and doing what they can to mimic the original behavior enough to keep that program happy. In this case, it waited patiently for the garbage WM_COMMAND message to arrive and dutifully launched the Printers Control Panel.

Nowadays, this sort of problem would probably have been solved with the use of a shim. But this was back in Windows 95, where application compatibility technology was still comparatively immature. All that was available at the time were application compatibility flags and hot-patching of binaries, wherein the values are modified as they are loaded into memory. Using hot-patching technology was reserved for only the most extreme compatibility cases, because getting permission from the vendor to patch their program was a comparatively lengthy legal process. Patching was considered a "last resort" compatibility mechanism not only for the legal machinery necessary to permit it, but also because patching a program fixes only the versions of the program the patch was developed to address. If the vendor shipped ten versions of a program, ten different patches would have to be developed. And if the vendor shipped another version after Windows 95 was delivered to duplication, that version would be broken when Windows 95 hit the shelves.

It is important to understand the distinction between what is a documented and supported feature and what is an implementation detail. Documented and supported features are contracts between Windows and your program. Windows will uphold its end of the contract for as long as that feature exists. Implementation details, on the other hand, are ephemeral; they can change at any time, be it at the next major operating system release, at the next service pack, even with the next security hotfix. If your program relies on implementation details, you're contributing to the compatibility cruft that Windows carries around from release to release.

Over the next few days, I'll talk about other decoys that have been used in Windows.

[Somebody caught my misspelling of "Drucker" while I was fixing it! - 7:45am]

Comments (37)
  1. Hi,

    not to be picky, but the german word for ‘printer’ is ‘Drucker’. Not ‘Drücker’.

    Drücker is something like a hard pull, but not really used except in the proverb ‘auf den letzten Drücker’ which means something like ‘in the last moment’

    Philip

  2. Diego says:

    I’m happy that we have internet today and companies can update their programs online, it makes possible to break such programms and bug the companies instead of microsoft

    (actually, one of features I miss in windows is a sort of system-integrated automatic updater which updates software looking at a URL provided by the software installer which points out to a XML file which tells the system if there’s a new version, i hope longhorn has it :/)

  3. Nate says:

    I’ve seen "When programs assume that the system will never change, episode 1", and now we are at episode 3. What happened to "episode 2"? Google can’t find it.

  4. FrankPr says:

    Diego: No, Longhorn … err, Windows Vista as the OS cannot provide this feature (except for OS components, which means Microsoft Update), but .NET 2.0, e.g., has something like that, and it’s called ClickOnce.

  5. Spire says:

    Nate: That’s what you get when you assume that the system of numerals will never change. Now poor Raymond will have to create a decoy "episode 2" article to make you happy.

    ("It was just a dream, Bender. There’s no such thing as ‘2’.")

  6. Arnold says:

    Diego

    > I’m happy that we have internet today and companies can update their programs online, it makes possible to break such programms and bug the companies instead of microsoft

    Ah, but how do you tell when it’s the program and when it’s Windows? I think a lot of people would (incorrectly) assume it’s "just another windows brokenness"

    I wish Raymond could say who it was, so we could avoid their broken crap. If they can’t do something this basic correctly, I’m not trusting anything their program does.

  7. Arnold says:

    I wish Raymond could say who it was

    Maybe MS could put up a page of "errata/coding mistakes we’ve noticed" and name these morons…

    This would create a "you better do it right, you don’t want to end up on the errata pages" clue bat.

  8. Universalis says:

    Diego, you mean something like the registry key HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionUninstallappnameURLUpdateInfo ?

  9. Fred says:

    "Nowadays, this sort of problem would probably have been solved with the use of a shim."

    Can you explain how this would have been solved today? I don’t understand what a "shim" would do for you here or why it would be different.

  10. James says:

    Ah, but how do you tell when it’s

    > the program and when it’s Windows?

    > I think a lot of people would

    > (incorrectly) assume it’s "just

    > another windows brokenness"

    With a dialog box? Do it the same way DEP tells you that the application wasn’t written to be forward compatible and is at fault for doing something it isn’t supposed to be doing and to contact the application vendor for an update.

    It seems like many of these compatibility hacks could be phased out (or are they already?): add the compatibility hack but initially warn that the application is doing something wrong. As time progresses, the warnings could become more annoying and the hack eventually could be removed.

    But then, I suppose some users still would say, "I upgraded my OS and now application X stopped working," or, "If Windows knows application X is doing something wrong but goes ahead and makes it work anyway, so why does it need to bother me about it?"

  11. DEP is special in that the boundary between ‘right’ and ‘wrong’ is quite clear. Most compatibility problems are not that easy to detect progammatically. If an app calls lstrcmpi and passes "Printers", is that right or wrong?

  12. Pete says:

    Fred – A shim is per-app, whereas in Win95 the fix had to be in the main .dll (or .exe in this case). The major difference is cleaner code since the main dlls are not cluttered up with thousands of fixes for broken applications.

    In both cases the fix whould have been the same, the only difference is how it gets loaded.

  13. "How did you detect this particular one?"

    The old fashioned way: A bug report was filed against the application, one of my colleagues spent several hours debugging it.

    Sure, code could be added to the core operating system to detect "somebody posting this particular bad WM_COMMAND value to that particular window", but (1) that clutters the core OS with application hacks that are active even for programs not known to be buggy, and (2) you still have to spend the several hours determine which bad WM_COMMAND values and which windows should be flagged. It’s still not automatic. Somebody had to spend the hours debugging the program to figure out what the bad operation is.

    And what if someday there is a change to the Control Panel where that WM_COMMAND value becomes legal again? Are you saying that every change to Explorer must be synchronized with a change to user32?

  14. Dan Maas says:

    "process <whatever> is broken! It’ll work for now, but maybe not if you upgrade Windows"

    Argh, I loathe stuff like this. I already have enough work to do at my job. I should not have to worry about whether my applications will break if I upgrade my OS. MS or the vendor can fix it, I don’t care, just don’t give me more work to do.

    (a related pet peeve: pop-ups like "Adobe Updater needs your attention!" "A flash player update is available!"… Yes, I’m sure the developers are proud of the update system they wrote. But I should not have to deal with this crap popping up when I’m one hour from a client deadline… I’ve already got enough to worry about.)

  15. Jerry Pisk says:

    It was just a dream, Bender. There’s no such thing as ‘2’.

    So true :) But then this would have to be episode 11.

  16. DrDoom says:

    I have been writing code (scripting) for a couple of years now. I am a network administrator and not a programmer; I like to automate mundane tasks that take up most of time.

    What I have found is that there are so many articles on how to performer specific tasks, most of them are cut and paste jobs from Microsoft website. Articles like this one: on what not to do are few. In most cases hhen such articles exist they are designed for general amusement rather then education. A good example of one such site is http://www.thedailywtf.com/

    Most people who post there to criticise someone (rather then provide constrictive feedback) provide solution that are inaccurate at best. Their suggestions are quite funny but the situation is quite tragic as it reminds me of number work situations that I have seen in large software development teams.

  17. Max says:

    Seems Nintendo’s Wifi Adaptor installer doe sa lot of stuff like this.

    I watched it install, and I’m pretty sure I programatically watch it dismiss the Unsigned Driver window, and programatically do the user interaction to set up internet bridging and sharing.

    This seems like it will be very brittle. I understand why Nintendo does it (as little user intervention as possible), but this is specific pretty much down to the service pack.

  18. BryanK says:

    > Most compatibility problems are not that easy

    > to detect progammatically.

    So, er, how did you detect this particular one? I think James is saying that whenever that code triggers, it’d be a good idea to also throw up a message saying "process <whatever> is broken! It’ll work for now, but maybe not if you upgrade Windows". Perhaps when you get the old WM_COMMAND message — or in fact any back-compat specific message on the old window class, for this case — would be a good time. I wouldn’t make it modal, though.

    And seeing how the recent WMF vulnerability was created by back-compat code (targeting 3.1 even!), IMO it’s even more important to put pressure on people to stop relying on back-compat. Having one or two generations of Windows that only show the message would also give users a way to gauge how many programs might break if they upgrade, *before* they spend the money on the upgrade.

  19. PatriotB says:

    To go along with what Max said: Unfortunately, Microsoft is guilty of this very practice (simulating user interaction). Windows Desktop Search includes a deskbar for searching from the taskbar. As Raymond has pointed out earlier, to prevent apps from abusing things that should be under user control, Windows doesn’t include programmatic access to which deskbars are displayed. So, Windows Desktop Search’s Options dialog has a button, "Show Deskbar", that when you click it, simulates the user right-clicking the taskbar, going to the selecting the Toolbars submenu, and selecting Windows Desktop Search. The dialog box even warns you "Your screen may flicker briefly as this happens."

    It’s no wonder people use these types of hacks, especially if Microsoft does it too. Granted, I think Desktop Search probably prevents itself from installing on future versions (because it’ll be moot with Vista), but still, it’s a terrible hack.

  20. PatriotB says:

    One more thing, an extension to Max’s posting. Wouldn’t it be great if Nintendo, instead of putting money into developing these hacks, would put money into developing a quality driver and getting it signed?

    And if I’m not mistaken, Vista is going to block all unsigned drivers, and not even give users a chance to override it. (This will be a good move for security & reliablility.)

  21. Mat Hall says:

    "And if I’m not mistaken, Vista is going to block all unsigned drivers, and not even give users a chance to override it. (This will be a good move for security & reliablility.)"

    What? That would suck. And how would driver developers ever be able to test anything? (Not to mention the fact that signing a driver is still no guarantee of reliability, as anyone who’s owned any soundcards made by a particular manufacturer (who I won’t name) could testify to.)

  22. Not today, Josephine says:

    then this would have to be episode 11.

    It is. As the old joke goes, the world is divided into 10 groups. Those who grok binary and those who don’t.

  23. Jonathan Wilson says:

    The problem with a universal software update system (i.e. windows update but user programs can register for it too) is how to be sure that:

    A.A virus hasnt overridden the update data for <insert program here> (e.g. a virus checker)

    or B.A virus hasnt inserted its own update into the list with an innocuous sounding name.

    Although if a virus did this, I am sure that virus checkers and malware programs would start removing or fixing the bad entries as soon as they are found :)

  24. Neil says:

    "And how would driver developers ever be able to test anything?"

    Using the test mode (as discussed on a previous entry so not worth repeating here).

    I wonder how many implementation details the core teams discovered that the app teams were using? I believe one of the exports still present in the 16-bit GDI.EXE is FixUpBogusPublisherMetafile – I’m hoping that it’s referring to an old Microsoft Publisher version here…

  25. Mark Steward says:

    I can’t think of a good solution for automatic updates. It obviously requires a certificate infrastructure to avoid the problems Jonathan describes. Most binaries aren’t signed, so hijacking updates is already possible, but a central update system will increase the appeal and impact of hacking it.

    It seems clear that Microsoft couldn’t verify the authenticity of every update themselves, but I think there would have to be a website or service, run by Microsoft, to stop companies like we’ve seen building hacks around the system, and to cover the single target/point of failure.

    But there’s still the issue of programmers losing or selling their certificates, turning security into false security (worse than none at all). Catching these would be a lot of work for Microsoft for relatively little return, although perhaps it could become part of their anti-phishing scheme…

    Anyone got an alternative?

    Mark

  26. PatriotB says:

    Ok, maybe I should’ve researched a bit more before posting that thing about unsigned drivers. I recall having read something that they were blocked outright, but it turns out that’s not the case. To quote from http://www.microsoft.com/whdc/winlogo/VistaLogofaq.mspx:

    "Q. Will Windows Vista allow unsigned drivers to be loaded?

    A. Yes, Windows Vista will load unsigned drivers if you sign in with an account that has administrator permissions on the machine or use administrator credentials."

    Funny, because I didn’t know that non-admins could load drivers in the first place… Or maybe that’s just kernel-mode drivers.

  27. BryanK says:

    Raymond: Basically, I was saying that you already have code in place to detect that a program is using that particular method of bringing up the printers UI. (You have to, otherwise the back-compat hack wouldn’t work.) So why not also display something to the user at the same time?

    Yes, it would "clutter" the OS code a bit — but most of that clutter is already there (the part that detects when the back-compat WM_COMMAND has to be available). All the time you speak of that’s needed to debug the problem has already been spent; all I’m saying is it might be a good idea to notify the user that you’re doing a back-compat hack, and that they should try to get an update from the vendor before upgrading Windows.

    If that WM_COMMAND value becomes legal again, then you’ll have a compatibility nightmare anyway. How will you know that a program wants the new behavior instead of the old? Unless, of course, the old behavior has been removed from the OS already, and everyone’s running a version that has it removed. Not just worked around, but completely removed.

    Dan Maas: Yes, it would be annoying. But that’s sort of the point, too; if you get an annoying message every time you run a program, which is telling you to upgrade that program, maybe you should (1) look for an update, if you’re a home user, or (2) tell someone else who can look for an update, if you’re a corporate user. If there is no update (e.g. if the original publisher is out of business), then it might be worth having a way to suppress the messages for a certain program. But the program could still break in the future, if the compatibility workarounds ever get cleaned out.

    Maybe you don’t care who fixes the issue between Microsoft and the vendor, but you probably don’t want back-compat code to be here forever, either, possibly turning into security holes along the way. Microsoft can’t even "fix" it anyway — all they can do is keep their OS backwards compatible by inserting hacks in various places. The vendor has no incentive to fix it either, as long as the back-compat code exists and they don’t get complaints from users.

    PatriotB/Neil: Requiring driver signing will completely break drivers like WinPcap. WinPcap has *zero* budget, so there’s no way to get their driver signed. But oh, that’s right, we’re all supposed to be using netmon anyway, not ethereal or tcpdump. Even though netmon only works on servers, and has an absolutely crappy UI and filtering capability (at least on 2K, I don’t know whether it runs on XP, or if its UI or filters are better). Sigh.

  28. This practice of fixing broken programs on the OS side encourages writing broken programs.

    What should really happen: a public beta of OS is released, software vendors test their software on the beta (they all want their software to work under the new OS, don’t they?), find the reasons why it doesn’t work, and either fix bugs in their software or report bugs to the OS vendor.

  29. Serge Baltic says:

    What surprises me much is the fact these problems are solved on the OS vendor side and not the application vendor’s. Not evident why it should bother. A vendor has its app stop working, users are kicking it in head, it releases an update — that’s how it naturally happens … no?

  30. Neil says:

    And of course, the sort of people who do http://blogs.msdn.com/oldnewthing/archive/2005/12/28/508689.aspx also ask silly questions like "How do I get my program to open the control panel?" when they mean "How do I get my program to change setting X?" and end up with the sort of solution above.

  31. PatriotB says:

    Serge: Raymond’s explained this before, but this is the gist. Say it is 1995, and your company is on Windows 3.1 using WordPerfect. Windows 95 comes out and you upgrade to it. WordPerfect no longer works. Who gets blamed? Windows.

    When OS upgrades cause your existing software to no longer work, that is going to deter people from upgrading. No matter if it is really the application vendor’s fault.

    WordPerfect is just an example I picked here. Who knows what application this blog posting was about, but chances are it is an app that was just about as widely used as WordPerfect.

    In a perfect world, the application vendor would do things right and would have an update ready for those users who upgrade. But remember this posting is talking about 1995–it’s not like users could hop on over to WordPerfect’s web site and check for updates.

  32. Replying to PatriotB:

    > Windows 95 comes out and you upgrade to it.

    Where do I get it from? How do I know about it? From the website? If so, then I’d know that a WordPerfect upgrade is ready, too. It’s WordPerfect’s duty to prepare this upgrade and make it ready during the beta testing period of Windows 95. It’s them who should say: sorry, we had a bug, and now we’ve fixed it. Please download and install this patch for free.

  33. I have talked about the Microsoft Knowledge Base article that describes how to run Regional and Language…

  34. Bad ideas that nevertheless came to fruition.

Comments are closed.