How do I enable and disable the minimize, maximize, and close buttons in my caption bar?

Date:June 4, 2010 / year-entry #165
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20100604-00/?p=13803
Comments:    23
Summary:A customer was having problems with the small icon that appears in the upper left corner of the caption: In my program, I need to enable and disable the Close button programmatically, since the program sometimes goes into a state where I don't want the user to close it. I do this by removing the...

A customer was having problems with the small icon that appears in the upper left corner of the caption:

In my program, I need to enable and disable the Close button programmatically, since the program sometimes goes into a state where I don't want the user to close it. I do this by removing the WS_SYS­MENU style when I want to disable the Close button, and adding it back when I want to re-enable it. However, doing this has as a side effect that the icon for my program doesn't appear in the title bar any more. If I never touch the WS_SYS­MENU style, then it works fine (but then I don't get the enable/disable behavior that I want).

Okay, the first problem is that you want to disable the Close button. Usability research indicates that users really don't like it when you disable the Close button. It makes them feel trapped and uncomfortable. This is the reason why standard Windows wizards don't provide a way for you to disable the Close button. The user should always have a way out.

Imagine showing a dialog box saying "By clicking OK, you agree to the following terms," and not having any way for the user to say, "No, thanks." You should leave the Close button enabled, and if the user clicks it at a bad time, you can say, "Okay, I heard you, but I'm right in the middle of something, but once that's done, I'll close."

Okay, but suppose you're in one of those cases where, really, you need to disable the Close button. You've read the guidelines, you understand why they're there, but you believe that you are an exceptional case.

If you never want the Close button enabled, then you can just specify the CS_NO­CLOSE style when you register the class.

To disable the Close button in the caption dynamically, you enable and disable the SC_CLOSE menu item in your system menu.

void DisableCloseButton(HWND hwnd)
{
 EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_CLOSE,
                MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
}

void EnableCloseButton(HWND hwnd)
{
 EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_CLOSE,
                MF_BYCOMMAND | MF_ENABLED);
}

The other two caption buttons are controlled by window styles:

void DisableMinimizeButton(HWND hwnd)
{
 SetWindowLong(hwnd, GWL_STYLE,
               GetWindowLong(hwnd, GWL_STYLE) & ~WS_MINIMIZEBOX);
}

void EnableMinimizeButton(HWND hwnd)
{
 SetWindowLong(hwnd, GWL_STYLE,
               GetWindowLong(hwnd, GWL_STYLE) | WS_MINIMIZEBOX);
}

void DisableMaximizeButton(HWND hwnd)
{
 SetWindowLong(hwnd, GWL_STYLE,
               GetWindowLong(hwnd, GWL_STYLE) & ~WS_MAXIMIZEBOX);
}

void EnableMaximizeButton(HWND hwnd)
{
 SetWindowLong(hwnd, GWL_STYLE,
               GetWindowLong(hwnd, GWL_STYLE) | WS_MAXIMIZEBOX);
}

Why is the close button managed differently from the minimize and maximize buttons?

History.

Originally, the window caption had only two buttons in the upper right corner, the minimize and maximize buttons, and they were controlled with a window style. Windows 95 added the Close button, but then there was the question of knowing when to enable and disable it. But wait, we already know when to enable and disable it: The application told us when it enabled and disabled the SC_CLOSE menu item. Bingo, just hook up the Close button to the existing menu item (which applications were already in the habit of maintaining), and magic, it just works. No need for applications to write special code to support the Close button. They already wrote the code; they just didn't realize it!

Exercise: What's wrong with these alternative functions for enabling and disabling the Close button:

// code in italics is wrong
void DisableCloseButton(HWND hwnd)
{
 SetClassLong(hwnd, GCL_STYLE,
              GetClassLong(hwnd, GCL_STYLE) | CS_NOCLOSE);
}

void EnableCloseButton(HWND hwnd)
{
 SetClassLong(hwnd, GCL_STYLE,
              GetClassLong(hwnd, GCL_STYLE) & ~CS_NOCLOSE);
}

Comments (23)
  1. steven says:

    With the alternative functions, it would also affect other windows of the same window class, even if they're in a different state? MSDN also states "it is possible to change the background color for a class by using SetClassLong, but this change does not immediately repaint all windows belonging to the class.", as an example of undesireable behaviour, so something similar may apply in this case, resulting in the close button appearing enabled/disabled until the NC area is redrawn?

  2. Shaka says:

    Those function will enable/disable the close button for all the windows created from that window class from now, not the close button of the "hwnd" window.

  3. Nick says:

    The problem is that the wrong code doesn't disable the menu item on the system menu.

  4. Nick says:

    I should add, likewise, it doesn't work if the window class was registered initially with CS_NOCLOSE

  5. Crescens2k says:

    In my time as a developer I have had a few situations where the people asking me to write the software wanted to diable the close button for whatever reason. In all cases I managed to get them to rethink the idea by doing simple things by changing the workflow, putting extra thought into what is being done or saving temporary operation data.

    A lot of developers seem to forget about the end user just to make things easier for themselves. But in this situation it can end up a lot worse, because if the user feels really trapped when the window won't close then they can just end the process and that will definitely get the operation to end up in a very bad situation.

    I have a list of actions which would make me not use an application if a program does. Trying to stop the application from closing is actually on that list. So if this application, with the close disabling, came out onto the market then I wouldn't be using it.

  6. Alexandre Grigoriev says:

    Fortunately, these tricks don't disable WM_CLOSE (or it's SC_CLOSE?) sent by Task Manager on Application->End Task button click. And the ultimate kill by TerminateProcess (Task Manager->End Process).

  7. sukru says:

    Doesn't MSI installer (which is a component of Windows) disable close button on the last step of uninstall operations? I believe this and similar cases are really legitimate uses of the functionality.

  8. Dan Bugglin says:

    @Alexandre I think an application can block WM_CLOSE, but if the developer wasn't expecting it (since there's no close button) they may forget to handle it, and the DefWndProc closes the window.

    @sukru I assume because closing is considered equivalent to cancel and that is done to underscore that you can't cancel at that point.  Still weird though.

    I don't know much about window classes but my best guess is Shaka's… the flag is only checked on window creation.

  9. Rick C says:

    Interestingly, under 64-bit Win7, it does NOT seem to affect other windows of the same class, whether already open or not.

    What Raymond was probably referring to, however, is that it disables the close button, but NOT the Close item on the system menu.

  10. Christian says:

    There is one very illegimitate reason for disabling the close button and the remedy is "net stop wuauserv". I heart deleting it's registry keys even allows installing additional updates without rebooting first ;-)

  11. Jens says:

    Yo close button, I'm real happy for you and imma let you finish, but OK is the greatest button of all time… of all time.

  12. Anonymous Coward says:

    Christian

    Or you can just drag the you-want-to-reboot window to the bottom right and reboot when it suits you.

    As to why it wants to reboot, Raymond discussed that before: ‘Windows can but won't’ technet.microsoft.com/…/2008.11.windowsconfidential.aspx

    On the other hand, there was an article on Slashdot some time ago about one of those old systems (think twenty years ago or something), that could achieve close to 100% uptime and get security right. I thought I bookmarked it since it was interesting from a historical perspective, but I can't find it any more.

  13. Rick C says:

    I'd have to check but I'd think that the actual answer is slightly different from Shaka's:  I suspect that SetClassLong would affect *all* windows of that class, even those already existing, and not just new ones.  I don't care enough to write code to test this, but my reasoning is that the change is supposed to happen right then, so it would make sense for it to affect all windows, and this sentence from MSDN "Calling SetClassLong with the GCL_WNDPROC index creates a subclass of the window class that affects all windows subsequently created with the class" implies that by specifically calling out GCL_WNDPROC "affects all windows *subsequently* created" which implies that other values don't.

  14. Joshua says:

    I had to disable the close button and WM_CLOSE once thanks to a third party component (not to name names here). My response to the newly created usability problem was to create a big button on the window labeled "Cancel".

    As for the 100% uptime and getting security right, that is achieved on some Linux systems by patching the running kernel with binary patches.

  15. Nick says:

    "I heart deleting it's registry keys even allows installing additional updates without rebooting first ;-)"

    Wow, and people wonder why the "required yearly re-installation of Windows" myth perpetuates.  My Windows is unstable, even when I fix the registry manually!

  16. Alex Cohn says:

    I wonder if the EnableMenuItem method will work for minimize and maximize, too. After all, these buttons also have siblings in the Alt-space menu.

  17. Neil says:

    I ran into this very problem a few hours ago, when I wanted to try VC2010++Express, and its setup insisted it needed to reboot; the Close button was disabled, as was the "Restart Later" button. I of course followed the path predicted by Crescens2K…

  18. Marquess says:

    VS2010 installs .NET Framework 4.0. I'd say that warrants an unconditional reboot.

  19. Alexander Grigoriev says:

    @Marquess

    Why would installation of a new component warrant an unconditional reboot?

  20. Marquess says:

    Because it's a rather tightly integrated component.

  21. James Schend says:

    On the other hand, there was an article on Slashdot some time ago about one of those old systems (think twenty years ago or something), that could achieve close to 100% uptime and get security right.

    BeOS was close, if you're flexible in your definition of "uptime." You could, for example, kill, patch, and restart the networking subsystem without rebooting the kernel or anything else on the system. (Or any other subsystem on the machine.)

    But then again, if you have to kill networking, you're probably not going to count that as "uptime" except as a technicality.

  22. >I'd say that warrants an unconditional reboot.

    Nothing short of the wrath of Zeus warrants an unconditional reboot.

    Even updating the OS doesn't force a reboot. If your application tries a stunt like that, it had better be *the cure for cancer* or if I ever meet you I will hit you in the face with a brick.

  23. Worf says:

    There are mainframe systems that pretty much have the uptime crown. So much so that a reboot is a Very Serious Deal and can take hours or days, even. Literally 5 9's uptime.

    Hell, the OS won't stop even if hardware fails – the OS merely keeps on trucking avoiding the failed CPU, RAM, disk or other component. And naturally, all hardware is hot swappable.

Comments are closed.


*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index