Код завершения приложения MFC всегда 20

Используя однодиалоговое приложение на основе 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.

Почему в вашем диалоге есть кнопка с идентификатором IDCANCEL, если вы не хотите, чтобы менеджер диалогов закрывал диалог при нажатии [Esc]? Просто удалите его и вам не придется бороться с менеджером диалогов.

IInspectable 23.07.2024 19:54

Потому что, если я полностью удалю кнопку «Отмена», поведение клавиши [Esc] по умолчанию все равно будет вызывать закрытие диалогового окна, и именно это я пытаюсь предотвратить.

user20716902 23.07.2024 20:03

@user20716902 в Интерфейс клавиатуры диалогового окна, попробуйте обработать WM_COMMAND и отбросить сообщение, когда wParam=IDCANCEL

Remy Lebeau 23.07.2024 21:14

@RemyLebeau Спасибо, да, я могу отказаться от IDCANCEL, чтобы предотвратить закрытие диалога с помощью [Esc] Но это не решает мою основную проблему при закрытии с помощью кнопки закрытия диалогов X. Мое приложение закрывается с кодом выхода 20, как я могу изменить это поведение. Я хотел бы, чтобы код выхода был 0

user20716902 23.07.2024 21:35

@user20716902 user20716902 Вместо обработки WM_COMMAND вы также можете просто переопределить CDialog::OnCancel(). Но в любом случае, пожалуйста, отредактируйте свой вопрос, включив в него минимально воспроизводимый пример. EndDialog() не имеет ничего общего с кодом завершения процесса, значение, которое вы передаете EndDialog(), просто передается обратно вызывающей стороне, которая модально отображает диалог. Больше ничего. Таким образом, ваш код выхода 20 должен исходить откуда-то еще, чего мы не видим.

Remy Lebeau 23.07.2024 21:47

Это странно. EndDialog() правильно возвращает аргумент, когда DoModal() возвращается в пользовательский код (InitInstance()). Однако, возвращаясь оттуда, код попадает в реализацию AfxWinMain() MFC, которая выполняет какую-то странную очистку и в конечном итоге перезаписывает код возврата возвращаемым значением из синглтона CWinApp::ExitInstance() приложения. При этом он пытается обработать (конечные) сообщения (в частности, WM_NCLBUTTONDOWN) и возвращает свой wParam.

IInspectable 23.07.2024 21:51

@IInspectable Это будет означать, что для кода выхода установлено значение HTCLOSE. Что имеет смысл, поскольку диалог закрывается. Но не имеет смысла, что 1) WM_NCLBUTTONDOWN будет обрабатываться, когда WinMain() очищается после закрытия диалога, и 2) зачем кому-то устанавливать код выхода на wParam из WM_NCLBUTTONDOWN для начала?

Remy Lebeau 23.07.2024 22:00

Что именно вы пытаетесь сделать? Для приложения, отображающего пользовательский интерфейс, средство запуска обычно НЕ проверяет код завершения, как в случае с приложениями командной строки. А из-за отсутствия кнопок [ОК] и [Отмена] единственный способ закрыть приложение — из системного меню (или кнопки X в строке заголовка). Так что же именно вернет код выхода?

Constantine Georgiou 23.07.2024 22:08

@ConstantineGeorgiou, я пытаюсь установить код выхода на 0, когда пользователь нажимает правую верхнюю системную кнопку [X]. И предотвратить закрытие по умолчанию, когда пользователь нажимает клавишу [ESC]. Например, когда Блокнот закрывается нажатием [X], код возврата равен 0.

user20716902 23.07.2024 22:15

@RemyLebeau Возврат wParam в последнем сообщении — стандартная практика. Если вы не делаете что-то безумное, это сообщение будет WM_QUIT, а его wParam содержит предполагаемый код выхода (переданный в PostQuitMessage()).

IInspectable 23.07.2024 22:15

@IInspectable Да, я знаю об этом. В стандартном цикле сообщений WM_QUIT всегда является последним сообщением, поскольку это единственное сообщение, которое заставляет GetMessage() разорвать цикл. В этом случае для меня не имеет смысла, чтобы MFC обрабатывал WM_NCLBUTTONDOWN и копировал его wParam в WM_QUIT, чтобы вернуть его в качестве кода выхода. Я не спорю, что вы видите, как это происходит. Я просто говорю, что это кажется глупым поступком.

Remy Lebeau 23.07.2024 22:18

@user20716902 user20716902, если вы хотите принудительно установить код выхода на 0, то почему бы вам просто не обновить WinMain() вашей программы, чтобы он возвращал 0 вместо возврата того, что AfxWinMain() возвращает?

Remy Lebeau 23.07.2024 22:21

@RemyLebeau MFC не позволяет вам настраивать свою _tWinMain реализацию. Однако работает замена вызова EndDialog() вызовом PostQuitMessage(), но я не понимаю последствий. Другой вариант — взять дело в свои руки и сразу ExitProcess() после закрытия диалога.

IInspectable 23.07.2024 22:31
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
13
90
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда вы создаете «Диалоговое приложение» 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.

user20716902 24.07.2024 10:40

Переопределение ExitInstance решило проблему. Поскольку MS заявляет, что «0 указывает на отсутствие ошибок, а значения больше 0 указывают на ошибку». Именно поэтому я в первую очередь поднял этот вопрос, поскольку был удивлен, увидев коды завершения перенастройки моего приложения, отличные от 0, хотя я знал, что ошибок не было. произошло.

user20716902 24.07.2024 10:46

Другие вопросы по теме