Используя однодиалоговое приложение на основе MFC по умолчанию, я пытаюсь предотвратить поведение по умолчанию, когда клавиша Escape или Enter приводит к закрытию диалогового окна.
Однако, хотя мне это удалось, я обнаружил, что код выхода приложения теперь всегда возвращает 20, независимо от значения, которое я передаю в EndDialog.
В приведенном ниже тесте я запретил нажатие клавиши Escape:
BEGIN_MESSAGE_MAP(CMFCApplication10Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDCANCEL, &CMFCApplication10Dlg::OnBnClickedCancel) // Triggers when Escape key pressed
ON_WM_CLOSE() // Added WM_CLOSE to handle the close
END_MESSAGE_MAP()
OnBnClickedCancel происходит при нажатии Escape, поэтому, чтобы предотвратить закрытие приложения, я не вызываю OnCancel
void CMFCApplication10Dlg::OnBnClickedCancel()
{
//CDialogEx::OnCancel(); // Not calling OnCancel
}
Я добавил OnClose, чтобы диалоговое окно по-прежнему закрывалось с помощью верхней правой кнопки «Закрыть».
void CMFCApplication10Dlg::OnClose()
{
CDialogEx::EndDialog(0); // Call the EndDialog with 0
}
Как я могу отключить клавишу escape, а также вернуть код выхода 0
Ожидается: код выхода 0.
Актуально: получение кода выхода 20.
Потому что, если я полностью удалю кнопку «Отмена», поведение клавиши [Esc]
по умолчанию все равно будет вызывать закрытие диалогового окна, и именно это я пытаюсь предотвратить.
@user20716902 в Интерфейс клавиатуры диалогового окна, попробуйте обработать WM_COMMAND
и отбросить сообщение, когда wParam=IDCANCEL
@RemyLebeau Спасибо, да, я могу отказаться от IDCANCEL, чтобы предотвратить закрытие диалога с помощью [Esc]
Но это не решает мою основную проблему при закрытии с помощью кнопки закрытия диалогов X. Мое приложение закрывается с кодом выхода 20, как я могу изменить это поведение. Я хотел бы, чтобы код выхода был 0
@user20716902 user20716902 Вместо обработки WM_COMMAND
вы также можете просто переопределить CDialog::OnCancel()
. Но в любом случае, пожалуйста, отредактируйте свой вопрос, включив в него минимально воспроизводимый пример. EndDialog()
не имеет ничего общего с кодом завершения процесса, значение, которое вы передаете EndDialog()
, просто передается обратно вызывающей стороне, которая модально отображает диалог. Больше ничего. Таким образом, ваш код выхода 20 должен исходить откуда-то еще, чего мы не видим.
Это странно. EndDialog()
правильно возвращает аргумент, когда DoModal()
возвращается в пользовательский код (InitInstance()
). Однако, возвращаясь оттуда, код попадает в реализацию AfxWinMain()
MFC, которая выполняет какую-то странную очистку и в конечном итоге перезаписывает код возврата возвращаемым значением из синглтона CWinApp::ExitInstance()
приложения. При этом он пытается обработать (конечные) сообщения (в частности, WM_NCLBUTTONDOWN
) и возвращает свой wParam
.
@IInspectable Это будет означать, что для кода выхода установлено значение HTCLOSE
. Что имеет смысл, поскольку диалог закрывается. Но не имеет смысла, что 1) WM_NCLBUTTONDOWN
будет обрабатываться, когда WinMain()
очищается после закрытия диалога, и 2) зачем кому-то устанавливать код выхода на wParam
из WM_NCLBUTTONDOWN
для начала?
Что именно вы пытаетесь сделать? Для приложения, отображающего пользовательский интерфейс, средство запуска обычно НЕ проверяет код завершения, как в случае с приложениями командной строки. А из-за отсутствия кнопок [ОК] и [Отмена] единственный способ закрыть приложение — из системного меню (или кнопки X в строке заголовка). Так что же именно вернет код выхода?
@ConstantineGeorgiou, я пытаюсь установить код выхода на 0, когда пользователь нажимает правую верхнюю системную кнопку [X]. И предотвратить закрытие по умолчанию, когда пользователь нажимает клавишу [ESC]. Например, когда Блокнот закрывается нажатием [X], код возврата равен 0.
@RemyLebeau Возврат wParam
в последнем сообщении — стандартная практика. Если вы не делаете что-то безумное, это сообщение будет WM_QUIT, а его wParam
содержит предполагаемый код выхода (переданный в PostQuitMessage()
).
@IInspectable Да, я знаю об этом. В стандартном цикле сообщений WM_QUIT
всегда является последним сообщением, поскольку это единственное сообщение, которое заставляет GetMessage()
разорвать цикл. В этом случае для меня не имеет смысла, чтобы MFC обрабатывал WM_NCLBUTTONDOWN
и копировал его wParam
в WM_QUIT
, чтобы вернуть его в качестве кода выхода. Я не спорю, что вы видите, как это происходит. Я просто говорю, что это кажется глупым поступком.
@user20716902 user20716902, если вы хотите принудительно установить код выхода на 0, то почему бы вам просто не обновить WinMain()
вашей программы, чтобы он возвращал 0 вместо возврата того, что AfxWinMain()
возвращает?
@RemyLebeau MFC не позволяет вам настраивать свою _tWinMain
реализацию. Однако работает замена вызова EndDialog()
вызовом PostQuitMessage()
, но я не понимаю последствий. Другой вариант — взять дело в свои руки и сразу ExitProcess()
после закрытия диалога.
Когда вы создаете «Диалоговое приложение» MFC, вы обычно хотите отключить обработку клавиш Enter и Esc, поскольку это главное окно приложения, но при этом иметь возможность закрыть приложение из системного меню или кнопки закрытия в строке заголовка.
Для клавиши [Enter] вы можете переопределить элемент OnOK()
диалогового окна (реализация по умолчанию закрывает диалоговое окно):
void CMyDlg::OnOK()
{
// CDialogEx::OnOK(); Don't Close
}
Для клавиши [Esc] существуют различные приемы. Один из них — переопределить член OnCancel()
:
void CMyDlg::OnCancel()
{
// Disable Closing on Esc press
if ((GetKeyState(VK_ESCAPE) & 0x8000) == 0)
CDialogEx::OnCancel(); // Close only if NOT caused by [Esc]
}
Или переопределите OnCancel()
и добавьте несколько строк в элемент OnSysCommand()
, созданный мастером:
void CMyDlg::OnCancel()
{
//CDialogEx::OnCancel(); Don't Close
}
void CMyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else if (nID == SC_CLOSE) // Add these 2 lines
CDialogEx::OnCancel();
else CDialogEx::OnSysCommand(nID, lParam);
}
Другие разработчики отменяют член PreTranslateMessage()
.
Что касается кода выхода, то я думаю, что он вообще не имеет смысла, как я уже отметил в комментариях. Приложение вернет 0, если вы нажмете кнопку [X], но что еще оно должно вернуть? И при каких условиях?
В любом случае, CWinApp
является производным от CWinThread
, вы можете попробовать переопределить Run()
и в реализации вызвать родительский класс Run()
(CWinApp::Run()
или CWinAppEx::Run()
), отбросить его возвращаемое значение и всегда возвращать 0. Сгенерированное мастером переопределение InitInstance()
возвращает FALSE
для диалога app, что означает сбой инициализации, и, возможно, именно поэтому вы получаете ненулевой код выхода.
Примечание: если ваш проект новый и все, что вам нужно, это приложение MFC, отображающее «основную форму», как в VB или C#, вы можете попробовать использовать не шаблон «Диалоговое приложение», а вместо этого приложение SDI без документа. /просмотр поддержки (снимите флажок с этой опции). Однако он все еще содержит объект представления. Типом представления может быть CFormView
, который представляет собой представление на основе диалогового ресурса, и приложение ведет себя лучше, избегает проблем, подобных тем, что есть в вашем приложении, поддерживает такие функции, как обработчики ON_UPDATE_COMMAND_UI
и ускорители клавиатуры, а также предлагает такие полезные функции, как панели инструментов, статус- панель и закрепляемые панели, созданные мастером. То есть это настоящее приложение, а не диалоговое окно, перепрофилированное в окно приложения.
Обновлено:
Проверил это в тестовом приложении и обнаружил, что трюк переопределения Run()
не работает, поскольку Run()
не вызывается, если InitInstance()
возвращает FALSE
, что относится к созданному мастером «Диалоговому приложению MFC». Однако вы можете переопределить ExitInstance()
, который всегда вызывается перед завершением:
int CMyApp::ExitInstance()
{
CWinAppEx::ExitInstance(); // Discard the return value
return 0;
}
Возвращаемое значение, кстати, было 2 (ERROR_FILE_NOT_FOUND
), а не 20. Кажется, MFC возвращает эту псевдоошибку, если InitInstance()
возвращает FALSE
.
Спасибо за подробный ответ. Когда я использовал метод, описанный в моем вопросе, с использованием OnClose
и EndDialog(0)
, я определенно получал возвращаемое значение 20, но ваш подход проверки состояния ключа в OnCancel исправляет это поведение, и тогда я получаю возвращаемое значение 2.
Переопределение ExitInstance
решило проблему. Поскольку MS заявляет, что «0 указывает на отсутствие ошибок, а значения больше 0 указывают на ошибку». Именно поэтому я в первую очередь поднял этот вопрос, поскольку был удивлен, увидев коды завершения перенастройки моего приложения, отличные от 0, хотя я знал, что ошибок не было. произошло.
Почему в вашем диалоге есть кнопка с идентификатором
IDCANCEL
, если вы не хотите, чтобы менеджер диалогов закрывал диалог при нажатии[Esc]
? Просто удалите его и вам не придется бороться с менеджером диалогов.