To enable and disable a window, use the EnableWindow function


Commenter Chris 'Xenon' Hanson points out that fiddling with the WS_DISABLED style directly via Set­Window­Long leads to strange behavior. However it isn't the case that "most widget classes work fine." Reaching in and fiddling the style bit directly is like reaching into a program's internal variables and just changing the values: All the other work that is associated with changing the value simply doesn't happen.

It's like taking a book you checked out of the library, re-shelving it, and then going into the library computer and marking it as "returned". The bookkeeping will say that the book has been returned, but all the other processes associated with a book return has not taken place: People who had placed a hold on the book aren't notified. The "number of books checked out" counter isn't updated. (Which gets interesting when you come to the end of your senior year and the system won't let you graduate because its records say that you still have 1 book outstanding, yet when you say "Show me all the books I have checked out" it returns no records.)

In the case of windows, merely setting the WS_DISABLED style does not generate WM_ENABLE messages, it doesn't generate accessibility notifications, it doesn't do focus bookkeeping, all it does is set the flag and goes home. Eventually, some code will stop working because something "impossible" happened (in this case, a window transitioning from enabled to disabled without ever receiving a WM_ENABLE message).

Similarly, the way to change a window's visible state is to use the Show­Window function and not to manipulate the WS_VISIBLE style directly.

"I think I filed a suggestion on MSDN2.microsoft.com's suggestion box to advise people not to fiddle with the WS_DISABLED flag at runtime via Set­Window­Long() since it seems like a viable route if you don't know otherwise."

Actually, the advice already exists right at the top of the Window Styles page where it says "After the control has been created, these styles cannot be modified, except as noted." And for WS_DISABLED, it says "To change this after a window has been created, use Enable­Window."

Comments (22)
  1. Brad Bellomo says:

    The problem is "except as noted" – changing windows styles should never have been allowed.  You could have added wrapper functions to do this for the styles it does work for.

  2. Farproc says:

    So, I'm posting here, completely OT, because the Suggestions Box doesn't have a "Leave a Comment" box.

  3. Zarat says:

    Hmm.. the linked Window Styles page says "After the window has been created, these styles cannot be modified, except as noted." – for most styles including border & caption there is no notice.

    However the documentation of SetWindowPos flag SWP_FRAMECHANGED seems to indicate you are allowed to change them: "Applies new frame styles set using the SetWindowLong function."

    I've seen code fiddling with the frame/caption styles to go fullscreen and back, now I wonder if thats allowed or if the window needs to be recreated.

  4. Joel says:

    I'm sorry, but this is just bad design.  It is equivalent to having a class with public fields that the documentation says you aren't supposed to change.  If they aren't supposed to be fiddled with directly, make them private and have accessor methods or some other type of method that will change them and do all the other bits that need doing.  The API should tell you what you can and can't do, not the documentation.

    Perhaps MS should generate a message when specific styles are changed in SetWindowLong()?  Then it could still have the encapsulation failure, but it would be mitigated.  Kind of like how x86 CPUs implicitly do things when certain bits in eflags are changed.

  5. Brian says:

    Yes, it's bad API design, but it's not like Microsoft can change it now.  Perhaps when they invent the time machine…

  6. Chris 'Xenon' Hanson says:

    Wow. I'm nanofamous for 15 picoseconds.

    In my defense, I'll only say that there's a crapload of example code out there, much of it published by Microsoft in the early days, that horks around with window style bit directly. Also, a lot of things that are implemented by Windows styles don't have a wrapper function, or the wrapper function was added in a later binary API, meaning if you want to manipulate bit X in an executable that's compiled to run on API prior to the introduction of X's wrapper API, the only way to do it is via twiddling the X bit directly. And, a lot of times, this is fine. But sometimes it's not.

    As many other posters have noted, if everyone had to do it all over again, using SetWindowLong to change style bits would never have been done. But that can be said about a lot of things in Windows, and many other OSes and APIs as well. I'm just here as a cautionary tale.

  7. Jules says:

    "Which gets interesting when you come to the end of your senior year and the system won't let you graduate because its records say that you still have 1 book outstanding, yet when you say "Show me all the books I have checked out" it returns no records."

    Which comes back to the old design heuristic: "don't cache information you can regenerate easily because then you risk it getting out of sync", which is closely related to normal forms in database design, the "don't repeat yourself" adage of object-oriented design, and so on…

    "I'm sorry, but this is just bad design.  It is equivalent to having a class with public fields that the documentation says you aren't supposed to change.  If they aren't supposed to be fiddled with directly, make them private and have accessor methods or some other type of method that will change them and do all the other bits that need doing."

    How would you achieve this, given that you need to have a C-compatible API as a basic design requirement?

  8. Leo Davidson says:

    @Jules:

    "How would you achieve this, given that you need to have a C-compatible API as a basic design requirement?"

    Same way you always do, and the same way COM works when using C, by passing the object an argument to the function (which is all C++ really does under the covers anyway).

    (Passing it as an opaque handle if you want to discourage people seeing/touching the internals.)

    Of course, the API already works that way using HWNDs and we don't set the style bits directly (we tell SetWindowLong to set them), nor do we even know where they are in memory. The issue people are criticising is that the API exposes those bits in a poor way (though it was probably a good idea at the time).

  9. Antonio Rodríguez says:

    "I'm sorry, but this is just bad design.  It is equivalent to having a class with public fields that the documentation says you aren't supposed to change.  If they aren't supposed to be fiddled with directly, make them private and have accessor methods or some other type of method that will change them and do all the other bits that need doing."

    True. But take into account that GetWindowLong() and SetWindowLong() have been there from the times of Windows 1.01. Which ran in a 8086 with 256 KB of RAM. In the times where programmers were trusted not to be stupid.

    The current specification reduces the implementation of GetWindowLong to a single line of code (something like WindowDataByHandle(hWnd)->WindowAttributes[nIndex] = dwNewLong; ). The implementation you propose would involve a finite number of Get/Set functions, to handle each of the bits that can be modified. And this should be implemented for each window class that define custom style bits (PBS_MARQUEE comes to mind). So "finite" would probably mean several dozens (or even hundreds) of APIs.

    You choose: 25 bytes of object code, or a few KB. When you are running in 256 KB, the choice is clear.

  10. jobin says:

    "In the times where programmers were trusted not to be stupid."

    I've seen Raymond make such comments before – but I think it less a matter of trust, and more that things were so difficult that if you could accomplish anything, you were probably already not stupid.

  11. Karellen says:

    "Reaching in and fiddling the style bit directly is like reaching into a program's internal variables and just changing the values"

    You mean, apart from the fact that he's calling a public API function with a publicly defined parameter, and the function is returning and saying "yes, that's all OK"? If he was actually calculating offsets to struct pointers, or walking the stack, to find memory locations to poke, I'd accept the point. But as it is, I don't see how it's valid.

    As you point out, the documentation says that these styles cannot be modified. Clearly, this is in error because, as has been shown by demonstration, they *can*. If they could not, SetWindowLong() would return 0 indicating error, and GetLastError() would return ERROR_INVALID_PARAMETER or similar. Rather, you "should not" or "must not" modify the styles except as noted.

  12. Billy O'Neal says:

    @Karellen:

    int *p = null;

    *p = 42;

    is perfectly valid code, which will compile perfectly fine and throw no errors. It will, however, crash at runtime. Just because something is valid in one sense does not mean it is valid in another, and the behavior itself is well documented.

  13. Leo Davidson says:

    There are also weird cases like the progress bar where you have to both send PBM_SETMARQUEE *and* set/clear PBS_MARQUEE to turn marquee mode on/off.

    (MSDN mentions having to set the style but not having to clear it as well. I wonder why either is necessary, since the message handler could implicitly modify the style. Maybe it's unintentional but too late to change.)

  14. steveg says:

    "In the times where programmers were trusted not to be stupid."

    In the times when programmers learnt from books, reading them cover to cover, not from random pages from the interwebnets.

  15. Lawrence says:

    Why is there is always the inevitable "it's just bad design" comment, do these people just forget every time they read a new article that Raymond's talking about decisions made where memory consumption was discussed and measured in bytes?

    (cue geek joke about these people having even less memory than the systems this API was designed for)

  16. JustSomeGuy says:

    "To enable and disable a window, use the EnableWindow function" – from the people that bought you the ability to shut down from the "Start" button :-) And, pre-empting anyone who claims this is snarky, it's just a bit of humour. I actually like the concept of the start button. Far better than trying to find anything in Win 3.11. Maybe it should have been called "Attention" or "Oi!" or something else.

  17. Snarky says:

    Well @JustSomeGuy, you know what they say. If you want to shut down the computer you have to Start somewhere.

  18. Dave says:

    Actually, the advice already exists right at the top of the Window Styles page

    There's a difference between "the information is there, if you interpret the text just right, and in retrospect, after having spent an hour banging your head against the wall when it doesn't work" and "the text adequately notifies the user that doing this is a bad idea".  In this case I'd say it falls firmly into the former class.

    (This is a pet peeve of mine with OSS projects, you report a quite obvious bug and the response is that if you hold the manpage's appendix to the apocrypha upside down in front of a mirror then there's a vague reference there that calling printf() with more than three arguments will reformat your hard drive, therefore this isn't a bug and won't be fixed).

  19. Dave says:

    Actually, the advice already exists right at the top of the Window Styles page

    There's a difference between "the information is there, if you interpret the text just right, and in retrospect, after having spent an hour banging your head against the wall when it doesn't work" and "the text adequately notifies the user that doing this is a bad idea".  In this case I'd say it falls firmly into the former class.

    (This is a pet peeve of mine with OSS projects, you report a quite obvious bug and the response is that if you hold the manpage's appendix to the apocrypha upside down in front of a mirror then there's a vague reference there that calling printf() with more than three arguments will reformat your hard drive, therefore this isn't a bug and won't be fixed).

  20. Jolyon Smith says:

    @Billy:

    int *p = null;

    *p = 42;

    is patently NOT "valid" code.  It is certainly legal, compilable code, but that is NOT what constitutes validity, otherwise there would be no "bugs" in any software produced as the result of a successful compilation.

  21. Pi says:

    "… that bought you the ability to shut down from the "Start" button :-) And, pre-empting anyone who claims this is snarky, it's just a bit of humour."

    Humour that's been done to death. I doubt it can be appreciated by anyone who reads here for more than a week.

  22. Igor Levicki says:

    >>To enable and _disable_ a window, use the EnableWindow() function

    Is it just me or the underlined part somehow doesn't go well with that function name? :p

    Seriously, there should have been DisableWindow() function as well. Having a function that does two things and naming it such that what it does is not obvious unless you examine the passed variables is not a bad design, it is just retarded.

    [Just like how the setbuf function removes buffering and QWidget::setEnabled is used to disable the widget. If it offends you so much, pretend the function is called Set­Window­Enabled­State. -Raymond]

Comments are closed.

Skip to main content