Why do property sheets sometimes take a first-chance exception?


Reader cmonachan asked why CPropertySheet::DoModal can cause a first-chance exception. This is mentioned in Knowledge Base article 158552. But why take the exception in the first place?

First off, let's take MFC out of the picture. The first-chance exception is coming from the property sheet manager. MFC is just the middle-man.

Okay, so why the first-chance exception? It's not obviously backwards compatibility, since property sheets were new for Windows 95; there was no old property sheet manager to be compatible with.

But yes, it was for compatibility. Not with an imaginary "earlier version" of the property sheet manager, but with real honest-to-goodness dialog box editing tools.

Property sheets are nested dialogs. The styles for nested dialogs must include DS_CONTROL and WS_CHILD. What's more, it can't contain styles like DS_ABSALIGN and WS_CAPTION which make no sense for child windows. Most of those styles you can convince your dialog box editing tool to add or remove, but DS_CONTROL is a different story. Since this flag was new for Windows 95, no existing dialog box editing tool supported it.

Okay, so now you're designing a property sheet manager. What do you do with dialog box templates that have the wrong style?

Proposal 1: Don't write any special code. Just create the dialog box on the assumption that the caller set the styles correctly.

The upside of this proposal is that it's the least amount of work. The downside is that if the caller messes up, they just get bizarre behavior (property sheet pages that pop out of the frame, for example) and have no real clue what they did wrong, much less how they can go about fixing it.

Proposal 2: Write special code to detect incorrect dialog boxes and reject them. Okay, well instead of bizarre behavior, the caller just gets nothing. Still not much help.

There's a serious problem with the first two proposals: How do you get people to set the styles correctly if their tools aren't capable of doing it? Fire up the dialog box editing tool you've been using and it won't have a check-box for DS_CONTROL because your dialog box editing tool was written for Windows 3.1, and DS_CONTROL didn't exist then.

You might say, "Well, those people will have to upgrade their dialog box editing tools in order to write programs for Windows 95." The problem with that position is that it creates a chicken-and-egg problem. It's 1994. Windows 95 Beta 1 has just come out. A company wants to "catch the wave" and have a Windows 95 version of their program ready on the day that Windows 95 hits the shelves, so they anxiously install the beta, install the beta SDK, they see this cool new property sheet thing so they sit down to give it a shot and... they're stuck. They can't use property sheets because they need the Windows 95 version of Microsoft Visual C++ or Delphi or Turbo Pascal. But those products don't exist yet because Windows 95 isn't out yet. And it's not just the development tools that would need to be upgraded. It's also the translation tools and all the other tools that manipulate dialog box templates.

This isn't an imaginary scenario. The Windows 95 team faced this very problem! In order to use these cool property sheet things, they needed to add the DS_CONTROL style to their dialog boxes, but Visual C++ didn't support this style since that style existed only in an operating system that hadn't been released yet.

Proposal 3: Write special code to detect incorrect dialog boxes and fix them so that people can start writing Windows 95 programs now.

To fix the dialog box requires modifying the styles, which means modifying the dialog template, and that's where the first-chance exception comes from. When the property sheet manager writes to the dialog template to fix the style, a first-chance exception is raised because resources start out as read-only pages. The kernel catches this exception and write-enables the page, then returns EXCEPTION_CONTINUE_EXECUTION to say, "I fixed the problem, try it again."

That's where the first-chance exception comes from.

How do you avoid this first-chance exception? Easy. Get your dialog box styles right in the first place. If you get the styles right, then the property sheet manager won't have to fix them. For the record, here are the style requirements for property sheets:

Must have Optional
DS_3DLOOK
DS_CONTROL
WS_CHILD
WS_TABSTOP
DS_SHELLFONT
DS_LOCALEDIT
WS_CLIPCHILDREN

Any other WS_* and DS_* styles not listed above are forbidden. (Note that I'm talking about styles and not extended styles.)

Comments (19)
  1. Jason says:

    Why doesn’t the property sheet manager create a copy of the entire DLGTEMPLATE/DLGTEMPLATEEX structure, set the style there, and call CreateDialogIndirectParam? (If the caller passed in an actual DLGTEMPLATE pointer to CreatePropertySheetPage, you would have to walk the struct to determine the length)

    Also, why isn’t the WS_VISIBLE style bit needed/enforced?

    [You can figure this out for yourself. Write (or pretend to write) the code that edits the template. Repeat with the code that copies the template (walking it if necessary), calls CreatedialogIndirectParam, and then frees the template. Compare how much time it took, the impact on the product schedule, and the memory footprint. -Raymond]
  2. Stu says:

    Proposal 4: Ship a tool with the beta SDK that could set or remove the flag on a pre-existing dialog template.

    Upside: No ugly hacks that become obsolete and bothersome in the relatively near future.

    Downside: Developers have to run the tool each time they edit their dialog.

  3. GregM says:

    Wow, 10 years developing Windows software and I’ve never heard of DS_CONTROL before.  Thanks a lot for this very helpful post!

    I just checked my resources file, and out of 132 dialogs that are WS_CHILD dialogs, the only two dialogs that have this style are a file dialog addition, and a copy of that same dialog.  I’m pretty sure that resource originally came from CodeProject or something like that.  So, none of our property pages have this style, including the ones created using the "new property page" option in the resource editor in VC5 through 2003.  I guess we need to make sure that someone on the VS team sees this post too if this hasn’t been added for 2005.

    Raymond, I’m assuming that "For the record, here are the style requirements for property sheets" means "the style requirements for dialog resources used as pages of property sheets".

    I’ve passed on the link to the previous blog post about DS_CONTROL to another developer that has been strugging with getting tabbing working correctly in a dialog that uses child dialogs.  None of the children use DS_CONTROL, which is probably why we’ve had to do everything ourselves.  We thought there should have been a way to get Windows to do this for us, but we couldn’t find it.

  4. Jason says:

    "[You can figure this out for yourself. Write (or pretend to write) the code that edits the template. Repeat with the code that copies the template (walking it if necessary), calls CreatedialogIndirectParam, and then frees the template. Compare how much time it took, the impact on the product schedule, and the memory footprint. -Raymond"

    :) We have had to do this — older Windows Mobile Smartphone devices (2003) did not support PropertySheet at all, so we implemented a substitute call (via #ifdefs) that has a tab-less PropertySheet. Our version does just this, and it works OK without any exceptions being thrown. Of course, it works against our code only and is not tested against all types of DLGTEMPLATEs, but the code in question is under 100 lines of code.

    This just seems easier than the exception handling approach you mentioned; I was wondering if there is a historical issue with legacy applications, such as an application that needed their own template in memory, perhaps when writing a custom Dialog class?

  5. OMG.  That’s where that behavior came from…

    It turns out that the PlaySound API relied on that behavior when playing sounds on 64bit machines.

  6. GregM says:

    Wow, the only style from that list of 7 that they all had was WS_CHILD.

    We also have the DS_FIXEDSYS, WS_DISABLED, and WS_CAPTION.  Should those really not be specified?  I assume that DS_FIXEDSYS is ok in combination with DS_SETFONT.  Also, if WS_CAPTION isn’t specified, then the dialog box editor removes the CAPTION statement from the resource, so there’s no tab title.  I assume it’s OK to leave that style.  What about WS_DISABLED?

  7. Coleman says:

    "We thought there should have been a way to get Windows to do this for us, but we couldn’t find it."  Sigh.  IMO, searching the MSDN for these types of "pearls" is a daunting task, which is one of the reasons I read this blog everyday, and also why I’ve pre-ordered the book.  BTW, Amazon says the book won’t ship until March now.  

    Unless I’m missing something, the DS_CONTROL isn’t documented in the VS2005 MSDN Library for the STYLE statement.  Through another search for "DS_CONTROL" I did find it under the "About Dialog Boxes" article.  

    Shouldn’t this be documented under the PropertySheet documentation somewhere?  It would certainly prevent the continuation of using the wrong styles for property pages.    

    I’ve submitted a support/feedback request (ID 250615) to hopefully get this put into the MSDN.  

  8. A says:

    “Compare how much time it took,”

    Convincing the kernel team to implement a hack in UnhandledExceptionFilter just to make property sheets work didn’t take time?

    “and the memory footprint.”

    If I understand correctly, the overall memory footprint would actually be lower. Each time this hack kicks in and changes a page’s protection from READONLY to READWRITE, 4 KB of memory is leaked. If it made a copy of the template on the heap, more memory might be used during the creation of the dialog, but in the end no memory would be leaked.

    [There was no “convincing” necessary. That code existed in Windows NT already. The property sheet code just took advantage of what was already there. -Raymond]
  9. B says:

    "Convincing the kernel team to implement a hack in UnhandledExceptionFilter just to make property sheets work didn’t take time?"

    I guess the property sheet manager maps the dialog template as copy-on-write.

    In a way, this is a bit like when relocations need to be applied to executable code – map the whole image is read only, copy on write. Then as you change it, the pages you change will get writeable copies generated on the fly.

  10. Mike Swaim says:

    [Quote]I’ve submitted a support/feedback request (ID 250615) to hopefully get this put into the MSDN.[Quote]

    But you didn’t vote on it. You can vote on your own items, you know.

  11. PatriotB says:

    There’s often a gap between new OS features and tools support for those features.  For example, VS 2005 doesn’t support using Vista PNG icons in your resources.  Even the just-released SP1 doesn’t fix this. :-(

  12. Coleman says:

    @Mike:  Yikes.  It looks like no body votes for their own items.  FWIW, I just voted for mine.  Truth be told, I was ignoring the voting system altogether.  

    On another note, my copy of the book shipped from Amazon yesterday and should be here on the 11th.  

    @PatriotB:  This isn’t a new OS feature.  

  13. Yesterday’s post by Raymond struck a chord when I read it. He discussed how the property sheets wrote

  14. Paul Woodward says:

    I am confused.  

    My understanding: Applications write to their own resources, not just the property sheet manager mentioned here.  Since the resources are not initially writable, writing to them causes a fault.  UnhandledExceptionFilter() looks at the fault, changes the memory to writable, and returns to the faulting instruction.

    My confusion: All the callers up the chain have to know about this.  If some unsuspecting caller uses Structured Exception Handling, it all fails.  Also, if you implement a debugger you have to handle this case because UnhandledExceptionFilter is not called when running under a debugger.  

    I think making resources writable from the getgo would be more robust.  I’m sure I’m missing something.  What is the value of loading them as read only if you want writes to succeed?

  15. GregM says:

    Paul, Raymond never said anything about it being the unhandled exception filter that does this.  He said that the kernel handled the exception.  Someone else threw in the unhandled exception filter bit.  Also, as far as I know, it’s very rare for an application to have to write to its own resources.  I’ve never heard of anything having to do it before.

  16. Norman Diamond says:

    Monday, January 15, 2007 11:50 PM by GregM

    > He said that the kernel handled the exception.

    You’re right, he said that, and now I’m confused again.  Larry Osterman had also posted that the kernel decided whether the read-only pages should be changed to read-write, but later said that the decision was really made in user-mode.  Now the vote looks like about 1.5 to 0.5 in favour of being done by the kernel, and I still wonder why it’s the kernel.

    >> The kernel catches this exception and

    >> write-enables the page, then returns

    >> EXCEPTION_CONTINUE_EXECUTION to say, “I

    >> fixed the problem, try it again.”

    [You’re reading too much into that one word. I’m using the word “kernel” loose to mean “some code that runs in the operating system at a comparatively low level.” Could be kernel32, could be kernel mode, could be ntdll—it’s not important where precisely. -Raymond]
  17. Norman Diamond says:

    > You’re reading too much into that one word.

    What(‘s) a word.  To anyone who has written/modified/whatever a device driver or task scheduler (no not _that_ task scheduler) or interrupt handler or any other part of a k..k…..something, it’s not exactly obvious that the word “kernel” was abused that way.  To anyone who ever compiled a Linux kernel and had someone point them to an interesting entry in the credits file, it’s even less obvious.  But I think you’ve clarified (indirectly) that this adjustment is decided by user-mode, thank you.

    [There’s a user-mode component called “kernel32″—obviously the word “kernel” doesn’t mean “kernel mode”. And how did I clarify that it happens in user mode? I said that it “could be kernel32, could be kernel mode, could be ntdll—it’s not important where precisely.” Everything is still on the table. It has to be, because I don’t know the answer. I’m kind of baffled how you concluded the opposite of what I was saying. -Raymond]
  18. ToyOS says:

    kernel runs in user mode. lol.

  19. Paul Woodward says:

    GregM,

    I was working from memory of an actual commercial application that wrote to its own resources.  I ran into it about 5 years ago.  My investigation at the time found UnhandledExceptionFilter made the memory writable.  I just verified this with a test app where I write to the resources with different __except blocks around it.

    If you have written your own debugger you have to make the resources writable when UnhandledExceptionFilter would, or else these applications will crash.  The msdev debugger provides this feature.

Comments are closed.