Why are there two values for PSH_WIZARD97?


Welcome, Slashdot readers. Remember, this Web site is for entertainment purposes only.

If you go cruising through the commctrl.h header file, you'll find that there are two different definitions for PSH_WIZARD97:

#if (_WIN32_IE >= 0x0400)
...
#if (_WIN32_IE < 0x0500)
#define PSH_WIZARD97            0x00002000
#else
#define PSH_WIZARD97            0x01000000
#endif
...

Why are there two values for PSH_WIZARD97?

Set the wayback machine to 1997. (Note: Dates are approximate.) The user interface design team studied how Windows 95 wizards have been faring and have begun using what they've learned to develop the next version of the wizard interface, which they have code-named "Wizard 97". Development in comctl32.dll proceeds based their designs, what with exterior and interior pages, headers and sub-headers, watermarks, all that jazz. Meanwhile, work on Internet Explorer 4 reaches the homestretch, and it's a race to the finish line.

Internet Explorer 4 wins the race and is ready to ship before the work on Wizard 97 is complete. What do you do? Do you say "Um, hi, Internet Explorer team. Could you slip your schedule by about half a year so we can finish our work on this new wizard design?" Yeah, like the Internet Explorer team are going to say, "Oh, sure, no hurry, take your time. We'll wait. It's not like we're going to get ridiculed for being another six months late."

And monkeys might fly out of my butt.

Out the door it goes, with a Wizard 97 design that hadn't gone through a full usability review. To discourage programmers from using this half-finished wizard, put the sentence "Don't use this flag" in the documentation. Boy, that was a close call.

Since they already "missed the boat", the urgency to complete the work on Wizard 97 vanished. One of the things that came up during the usability review was that the full-page watermark wasn't such a great idea after all. It made the text hard to read, users had difficulty figuring out what they should do, various problems like that. The design for Wizard 97 changed; now the watermark was just a small picture that goes in the corner of the wizard. Of course, the changes weren't just design changes; there were also bug fixes. All the work was done in time for Windows 2000, yippee.

And then the compatibility bugs started coming in.

Wait, compatibility bugs? How can there be compatibility bugs? Nobody is using Wizard 97 yet; there's nobody to be compatible with!

Well, it turns out that programs were indeed using the PSH_WIZARD97 flag from Internet Explorer 4 and expecting to get that old half-baked Wizard 97 design. (After some investigation, I believe we discovered that somebody somewhere had told the MFC folks, "Hey, there's this cool Wizard 97 thing you should use. It's pretty awesome." That someone was overcome with excitement and didn't realize that the work on Wizard 97 was not yet finished and that Wizard 97 was not yet ready for prime time. People who used MFC to write their programs then said, "Hey, look, MFC has support for this new type of wizard!" and started using it.)

Oh, great, how do you fix this?

It wasn't pretty, but it had to be done. (The preferred first choice, using a time machine, was unavailable.) The fix was to have two types of Wizard 97; there's the "Internet Explorer 4 version" and the "Windows 2000 version". Of course, nobody wants to officially admit that the Internet Explorer 4 version exists, since during its brief lifetime, it was documented as "Don't use me!" All the changes that were made after Internet Explorer 4 shipped had to be revisited. Each time there was a code change (either to support a new design or just to fix a bug), a test against the wizard flavor had to be made, and the old behavior restored if the old flag was used.

If new code was added for the Windows 2000 version of Wizard 97, whether it be due to a design change or a bug fix, then it had to be placed behind a check:

    if (program asked for Windows 2000-style Wizard 97) {
        // new behavior here
    }

Conversely, if code was deleted, it had to be brought back with a version check:

    if (program asked for Internet Explorer 4-style Wizard 97) {
        // old behavior here
    }

And if behavior changed, you need to have both versions:

    if (program asked for Internet Explorer 4-style Wizard 97) {
        // old behavior here
    } else {
        // new behavior here
    }

Giving the old and new Wizard 97 flags different values allows the property sheet wizard code to tell whether the program was designed for the bogus Internet Explorer 4 pre-release version of Wizard 97 or the finished Windows 2000 version.

Comments (25)
  1. John says:

    So what happens if you set both bits?  If there a check for that or would you get both old and new behavior in different areas?

    [Why does it matter what happens? -Raymond]
  2. DrkMatter says:

    @John

    Try it and see! Though this is the equivalent of asking: "What happens if I swallow a gallon of liquid laundry detergent?" Nobody knows for SURE, but anybody can tell it’s a bad idea.

  3. Gabe says:

    It’s a shame they couldn’t just create a new flag called PSH_WIZARD2k for the new behavior.

  4. John says:

    It matters because I’m going to set both bits in all my future applications.  I hope the crash dumps find their way to you.

    [And people wonder why there’s so much bad software out there. -Raymond]
  5. Nawak says:

    Maybe there is something I missed, but why was Internet Explorer developped against a WIP comctl32.dll? Even if competitors could have done the same (which I doubt) why hadn’t the IE team have to live with the consequence (waiting for the linked-against dll to be finished)?

    It seems that some lawsuits were well deserved…

    It is sad that it is eventually the shell team that had to pay the price, but I hope somebody took note and won’t let it happen again.

    That being said, as Gabe said, why not manage the problem with a new wizard ID, just like MS had to do for the Wizard95->Wizard97 evolution? Let the MFC and applications that were so eager to use the bugged version use it and create a new clean version that eventually every toolkit will adapt to. You would have the 95, 97-don’t-use-kthx and the 2k-ooh-this-one-is-shiny versions available depending on the flag… Of course MS thought of this and still didn’t choose to do that… Come on, Raymond, don’t leave us hanging there, tell us why!! :)

  6. JD says:

    Gabe: Well they probably could have, but nobody should be developing against the incomplete buggy version anyway so why should they have? They’re doing you a favor by not letting you use something that’s broken.

  7. Igor Levicki says:

    Raymond, John has a point (sort of).

    If you have two separate bits that share the same name for a single feature, then you can pretty much expect that people will set both.

    Never mind the warnings on the MSDN page, people will do it "to be on the safe side".

    Heck, they might even hard-code the constant instead of using #defines and the next thing you know there will be yet another compatibility shim to write.

    Bonus question:

    What happens if you have a library compiled with #define _WIN32_IE 0x0400, an application compiled with #define _WIN32_IE 0x0500, and they need to communicate those flags?

  8. Tom says:

    Thanks for the info, Raymond.  I was wondering what WIZARD97 did, so I poked into the MSDN documentation and found out about the guidelines for developing a wizard.  Although I suppose that the MFC CPropertyPage class, etc. can be used in implementing a wizard, it would be nice if there were some built-in windows messages like WM_WIZNEXT and WM_WIZFINISH, and maybe some windows classes that I could use to implement a wizard "old-school" style (i.e. using C instead of MCF/C++).

  9. Tom says:

    Yeah…about that messages and windows classes.  Nevermind.  I spoke too soon.

    http://msdn.microsoft.com/en-us/library/bb774544.aspx

  10. ERock says:

    I propose all bits are changed to trinary to prevent this. Ok, Microsoft, FUND IT. :)

  11. Nathaniel says:

    So… I didn’t see an article on this on Slashdot, so either the message is preemptive or my search-fu has gone downhill…

  12. Yuhong Bao says:

    By Windows 2000 do you mean IE 5? Because when you are talking about the shell, you seems to sometimes say Win98 when you really mean IE 4, I can tell because I know that the version of the shell shipped with Win98 is the same as the version shipped with the IE 4 desktop update for Win95.

  13. Maurits says:

    In hindsight, the obvious solution is to put the flag into a private, internal header and not move it to a public header until the feature is ready to ship.

  14. Miral says:

    No, in hindsight Nawak was right.  Either IE4 shouldn’t have started using the control library until it was finished (properly) or they should have waited for development on it to be complete instead of shipping a half-baked version.  If they didn’t want to wait, then they should have removed the code that used it.

    You can’t ship a product until all of its dependencies are shippable.  That’s just the basic rules.

    [IE4 didn’t use Wizard97. -Raymond]
  15. Miral says:

    … and if Wizard97 did end up being considered complete enough to ship, then the followup to it should have been Wizard2000 or Wizard97 v2 (with a version field or API call instead of a flag), so that apps could consciously make the decision about which one they wanted to use.

  16. Drak says:

    It’s all nice to say ‘they should have done it better’, but this sort of stuff just happens, and I think Microsoft dealt with it in a reasonable way.

    And about setting both flags: if you use the .h file, and the nicely defined constants, how would you go about setting both. It’s not gonna happen unless you go out and define your own constants (which is a bad idea anyway).

  17. Medinoc says:

    How did the IE4 Wizard97 style look like?

  18. Anonymous Coward says:

    Obviously, since IE didn’t use Wizard 97, it didn’t need the flag. In fact, since Wizard 97 wasn’t ready, no one except the Wizard 97 development team needed the flag and therefore it shouldn’t have been in any publicly (as in outside of the team) shipped header files. And of course, there’s something to be said for the “we didn’t cause this, not our problem” approach. Although I imagine that that would be kind of impossible if the problem is in MFC. I thought this was an interesting read, since it was a preventable problem and reading about these things can help prevent similar mistakes in the future.

    [The Wizard97 team expected to be finished in time, so they put the flags in the public header file. By the time they realized that they weren’t going to make it, the header files had already been frozen. I think people don’t understand how methodical the release process is (and how long it takes). -Raymond]
  19. DysgraphicProgrammer says:

    OK, if IE4 didn’t need it, why not #ifdef out it’s entire existence? Make the offending functions not callable, and not linkable? Or if you can’t do that for some DLL compatibility reason make the functions crash the whole program noisily if called. That way if any one tried to use them, they would not get past debugging.  

    [That’s a big code change to make just days before RTM. It would almost certainly reset escrow. -Raymond]
  20. CGomez says:

    This is an awesome story about how real world software teams have to deal with concurrent development.

    You can’t have developers sitting on their hands waiting for another part of the team to finish something.  And that is a smaller example of what a large software corporation that works on many products has to deal with.

    Very good lesson to be learned here.  The idiot says, "Just don’t use something half finished."  The realist nods and recalls a situation where his own team had to use new features that would work for the instant case but might not work in all cases.

    This is truly one of the best stories I’ve ever read on this blog.  An instant classic.

  21. Mike Dimmick says:

    It *wasn’t* complete enough to ship, but was in the source code for comctl32.dll, including all other changes in prsht.h. This was before Microsoft had a strong branching/merging source control solution for the Windows source code, so work in progress was all in the same tree (this was sorted out for Windows XP). IE4 shipped a new comctl32.dll – which had always housed the property sheet and wizard code – with various other new controls and new features, so the half-finished solution was shipped.

    The documentation said ‘don’t use it’, but unfortunately the header didn’t.

    Changing the value of the define is a little dodgy, as a release built with the new SDK will pick up the new flag; a patch release might not get complete coverage testing, although you shouldn’t change your SDK when building maintenance releases anyway.

  22. katastrofa says:

    Raymond: "The Wizard97 team expected to be finished in time, so they put the flags in the public header file. By the time they realized that they weren’t going to make it, the header files had already been frozen. I think people don’t understand how methodical the release process is (and how long it takes)."

    Well it isn’t very methodological if it allows half-finished features to slip through to final "frozen" release.

    Oh, and why did Microsoft start to use source control with branching so late?

  23. old-new-skool says:

    very nice tale…  It made me recall my desire to denounce all things C in the late 80’s/early 90’s — not sure why…   :)

    c’mon naysayers… what OS/Software Suite the size (and mass) of the world have you deployed or written lately, and how did you make it perfect with a Marketing department ultimately pulling the strings?  :D

  24. But…time travel is possible.

    http://www.chronos.ws

    Who do you think I will be working for 20 years from now?  <g>

  25. Tim Sneath says:

    As everyone must know by now, the Windows 7 Release Candidate is broadly released and available for download

Comments are closed.