如何通过在 MFC 对话框中按下按钮来停止 while 循环

How to stop while loop by pressing a button in MFC dialog

我正在 Visual Studio 2017 年使用 C++ 开发基于对话框的 MFC 应用程序。

在我的应用程序中,我有一个显示文本的 window。当我按下“开始”按钮时,我正在使用无限循环向其中写入数字,当我按下“停止”按钮时,我想停止这个过程。 window 看起来像这样:

为此,在 Start 按钮处理程序中,我使用 while 循环进行计数并在应用程序的 window 中打印计数器。每次进入 while 循环时,我都会检查 ICD_BUTTON2(停止按钮 ID)是否像这样按下:

void CEditableListControlDlg::OnBnClickedButton1()
{
     int counter = 0;
     while ((WM_COMMAND != IDC_BUTTON2)) {
            counter++;
            m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
     }
}

我没有在 Stop 按钮处理程序中输入任何代码。

但是我的应用程序停止响应,我需要使用任务管理器将其关闭。可能是什么问题呢?也许这不是检查是否收到按钮单击消息的正确方法?

谢谢。

这里的根本是你阻塞了主线程。即使您更正了停止按钮上的错误检查,您也不会得到预期的结果。原因是当一个事件被触发时,一个按钮被按下,主线程需要处理那个事件。您的循环妨碍了 why the suggestion of using a timer。这就是为什么你必须终止进程才能结束它。

如果你真的需要做一些非常激烈的事情,那么可能需要一个话题,但我不认为那是你的 objective。

//在.h

UINT_PTR timer;

//在.cpp中

//initialize timer to null in constructor or CEditableListControlDlg::Create

//you may want to disable the this button while the timer is active
//but don't start another timer if active!
void CEditableListControlDlg::OnBnClickedButton1()
{
    if(timer)
        return;
    //200 millisecond events.
    timer= SetTimer( ID_TIMER, 200, NULL );
}

void CEditableListControlDlg::OnTimer( UINT nIDEvent ) 
{
    //if you have more than one timer, if(nIDEvent == ID_TIMER)...;
    counter++;
    m_editCtrl.SetWindowTextA(std::to_string(counter).c_str());
    CWnd::OnTimer( nIDEvent );
}

//you may want to disable the this button while the timer is inactive
void CEditableListControlDlg::OnBnClickedButton2()
{
    if(timer)
        KillTimer(timer);
    timer= nullptr;
}

我看不出计时器如何解决在后台处理某些内容时允许 UI 操作的问题。计时器方式将最多处理帽子计时器事件的一个工作单元。

一个单独的线程是一个合适的解决方案,但对于这种情况来说可能太多了。

中间方法是在繁忙的 while 循环中“推送”消息,详情请参阅: https://docs.microsoft.com/en-us/cpp/mfc/idle-loop-processing?view=vs-2019

每个 GUI 程序在某个地方都有一个消息循环,它检查来自 OS 的传入消息并对其做出反应。通过自己创建无限循环,您可以阻止这种情况的发生——这就是为什么您需要任务管理器来关闭程序。它还会阻止诸如 window 重绘之类的事情。

MFC 提供了一个 OnIdle 函数,当消息循环无事时调用该函数。您可以使用此功能进行后台工作,例如递增计数器。有关详细信息,请参阅 Microsoft 的文档 Idle Loop Processing

要允许主消息循环处理 GUI 事件,例如单击“停止”按钮,请在您的 while 循环中添加并调用此函数 PumpMessages()

void CEditableListControlDlg::PumpMessages()
{
    // Must call Create() before using the dialog
    ASSERT(m_hWnd!=NULL);

    MSG msg;
    // Handle dialog messages
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      if(!IsDialogMessage(&msg))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);  
      }
    }
}