Preventing Window Closure in MFC Applications
Blocking window closure in MFC applications is necessary for critical operations, multi-step dialogs, and workflows that require user confirmation before exit. This requires handling both the close button and the Alt+F4 keyboard shortcut.
Removing the Close Button from the System Menu
The simplest approach is to remove SC_CLOSE from the system menu in your dialog’s WM_INITDIALOG or WM_CREATE handler:
CMenu *pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL) {
pSysMenu->RemoveMenu(SC_CLOSE, MF_BYCOMMAND);
}
This grays out and disables the X button in the title bar, but it doesn’t prevent Alt+F4—users can still close the window with that keyboard shortcut.
Why Removing WS_SYSMENU Doesn’t Work
You might think completely removing the WS_SYSMENU style would help:
// DON'T do this
ModifyStyle(WS_SYSMENU, 0);
This removes the entire system menu, including minimize, maximize, and restore buttons. You can’t selectively restore individual buttons afterward—Windows doesn’t provide granular control over title bar buttons through style flags alone.
Intercepting Alt+F4 and Close Events
Override OnSysCommand() in your dialog class to catch SC_CLOSE:
void MyDialog::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == SC_CLOSE) {
MessageBox(_T("This window cannot be closed at this time."), _T("Action Not Allowed"));
return;
}
else if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else {
CDialog::OnSysCommand(nID, lParam);
}
}
The 0xFFF0 mask filters out the low 4 bits, which contain state information—this compares only the command ID itself.
Also intercept WM_CLOSE for complete coverage, since some closure paths (taskbar close, DestroyWindow() calls) don’t go through OnSysCommand():
void MyDialog::OnClose()
{
MessageBox(_T("Cannot close this window."), _T("Action Not Allowed"));
return; // Don't call CDialog::OnClose()
}
Add the message map entry:
BEGIN_MESSAGE_MAP(MyDialog, CDialogEx)
ON_WM_CLOSE()
END_MESSAGE_MAP()
Conditional Closing (Recommended Pattern)
In practice, permanently blocking closure is poor UX. Instead, block closing only during specific operations:
void MyDialog::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == SC_CLOSE) {
if (m_isOperationInProgress) {
MessageBox(_T("Please wait for the operation to complete."), _T("Busy"));
return;
}
CDialog::OnSysCommand(nID, lParam);
return;
}
CDialog::OnSysCommand(nID, lParam);
}
void MyDialog::OnClose()
{
if (m_isOperationInProgress) {
MessageBox(_T("Cannot close while operation is in progress."), _T("Busy"));
return;
}
CDialog::OnClose();
}
Add a member variable and toggle it during operations:
// In your dialog header
bool m_isOperationInProgress = false;
// When starting an operation
m_isOperationInProgress = true;
// When the operation completes or errors
m_isOperationInProgress = false;
Disabling Minimize and Maximize
If you need to lock down all title bar interactions during critical operations:
void MyDialog::OnSysCommand(UINT nID, LPARAM lParam)
{
UINT command = nID & 0xFFF0;
if ((command == SC_CLOSE || command == SC_MINIMIZE || command == SC_MAXIMIZE)
&& m_isOperationInProgress) {
MessageBox(_T("Window is locked during this operation."), _T("Locked"));
return;
}
CDialog::OnSysCommand(nID, lParam);
}
Optionally remove these from the system menu preemptively:
CMenu *pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL) {
pSysMenu->RemoveMenu(SC_CLOSE, MF_BYCOMMAND);
pSysMenu->RemoveMenu(SC_MINIMIZE, MF_BYCOMMAND);
pSysMenu->RemoveMenu(SC_MAXIMIZE, MF_BYCOMMAND);
}
Best Practices
Be temporary and user-friendly. Blocking closure should be brief and conditional:
- Provide clear feedback. Always explain why closing is blocked. Use progress bars, status text, or explicit message boxes.
- Ensure legitimate exit paths. Never trap users permanently. Your application must have a way to close.
- Use for modal dialogs only. Reserve this technique for modal dialogs and specific workflow states—never the main application window.
- Respect system shutdown. Don’t block
WM_QUERYENDSESSIONor system shutdown requests. Clean up resources and allow graceful termination. - Test edge cases. Verify that blocking close doesn’t interfere with taskbar actions, Alt+Tab, or programmatic window destruction.
- Consider accessibility. Standard window controls are expected to work. Document any restrictions clearly for users relying on accessibility tools.
