【问题标题】:How to create a MFC dialog with a progress bar in a separate thread?如何在单独的线程中创建带有进度条的 MFC 对话框?
【发布时间】:2010-12-12 17:53:15
【问题描述】:

我的应用程序可能需要一段时间才能连接到数据库。这种连接是通过单个库函数调用建立的,即我不能将进度更新放在那里并进行回调或类似的东西。

我的想法是在连接到数据库之前在单独的线程中创建一个带有进度条的对话框。此对话框将使用CProgressCtrl::StepIt() 不断更改进度状态,以便用户看到正在发生的事情。
在设置好对话框并完成它的事情之后,我想从主线程调用数据库连接函数。 连接功能完成后,我想停止进度条线程。

让我画一幅画:

CMyApp::       ProgressThread
InitInstance()      .
    |               .
    |               .
    +-Create Dialog-+
    |               |
    |             Animate
 Connect          Progress
    to             Bar
    DB              |
    |               |
    +-Destroy Dlg---+
    |               .
    |               .

这可能吗?如果是,怎么做?

也许整个事情也可以使用计时器。可能会简单得多,但我也无法让它工作。

  1. 我知道CProgressCtrl::SetMarquee() 可能完全符合我的需要,但我无法使用它,因为该应用程序不支持 Unicode。
  2. 我可以将 db 连接调用移动到单独的线程中,但这样看起来对代码进行了很多更改并额外处理了连接错误。

更新 2
我让它按照 AlexEzh 和 Javier De Pedro 建议的方式工作:将 DB stuf 放入自己的线程中。
最初我担心如何进行错误处理,但实际上它与以前非常相似。

  1. 在主线程中,我创建了一个带有连接参数、结果标志和线程运行标志的结构。后者最初设置为true
  2. 我创建了一个线程并将该结构作为参数传递。
  3. 我在主线程中创建了一个显示进度条的对话框。
  4. 在主线程中还有一个循环在设置线程运行标志时运行。它调用了CMyDialog::Animate(),它调用了CProgressCtrl::StepIt(),然后又调用了Sleep()s。
  5. 线程执行 db-connection 代码并在完成后将运行标志设置为 false
  6. 当主线程退出循环时,它可以像以前一样处理错误。

缺点:将鼠标移到窗口上不起作用。它是看不见的。因此不能使用取消按钮或其他交互式对话框元素。不过,我可以忍受。

由于您喜欢该图表,现在它的外观如下:

CMyApp::        WorkerThread
InitInstance()      .
    |               .
    |               .
Create Dialog       .
    |               .
    +-Start Thread--+
    |               |
    |             Connect
 Animate            to
 Progress           DB
   Bar              |
    |               |
    +-Thread Ends---+
    |               .
 Destroy Dlg        .
    |               .

【问题讨论】:

  • 您可以在 ANSI 应用程序中调用 SetMarquee(),但这对您没有帮助,因为在主线程上也会调用动画进度条的计时器
  • 在我的 afxcmn.h 中看起来像这样: ** if (WIN32_WINNT >= 0x0501) && defined(UNICODE) \n BOOL SetMarquee(_In BOOL fMarqueeMode, In int nInterval); \n #endif // _WIN32_WINNT >= 0x0501 && defined(UNICODE) \n ** 这就是我不能调用它的原因。
  • 标题中的一群,复制该代码(它只是发送一条消息)并使用它。
  • 我现在在一个单独的测试项目中进行了尝试:必须满足以下所有条件才能使选取框工作: 1. 在资源属性(PBS_MARQUEE)中启用“选取框”。 2. 向控件发送消息 PBM_SETMARQUEE。 3. 使用 Unicode 支持编译。
  • 您可以通过使用模态对话框和计时器来摆脱这个缺点。在 OnInitDialog 中启动线程并设置计时器。在 OnTimer 中调用 StepIt 来更新进度。但请确保您控制了关闭对话框的所有可能方式,要么不让用户访问,要么停止线程。

标签: c++ multithreading mfc dialog progress-bar


【解决方案1】:

我希望this 关于使用进度条创建自己的线程启动画面的文章可以有所帮助。我是在解决 MFC 消息队列级别的线程锁定问题时写的。

【讨论】:

    【解决方案2】:

    将数据库连接逻辑移动到单独的线程仍然会更安全。使用对话框线程上的 DB,您将能够重绘进度条,但不能重绘对话框中的其他控件。

    【讨论】:

    • 我可以通过调用 RedrawWindow() 重新绘制整个对话框。如果我不这样做,只有进度条是动画的。
    【解决方案3】:
    1. 使用AfxBeginThread 创建工作线程。
    2. 在该线程中创建CProgressCtrl 并调用Create,将对话框作为CProgressCtrl 的父级传递,使用选框样式进行进度控制。
    3. 在Thread中创建一个消息等待循环:

      味精味精;
      while(GetMessage(&Msg, NULL, 0, 0))
      {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }

    4. 消息循环需要检查一个全局标志,看是否退出循环。

    【讨论】:

    • @Kirill 这对我有用,在我的项目中,究竟什么不应该工作?
    • MFC 线程可能在 Create 函数中死锁。为避免这种情况,您应该使用非 MFC 线程。
    • @Kirill 死锁是什么?相互锁定的两个资源是什么?
    【解决方案4】:

    您是否尝试过使用SendMessagePBM_SETMARQUEE 而不是SetMarquee。我从未尝试过自己,但它应该可以工作。

    在我看来,实现您想要做的最简单的方法是在 ui 线程中建立 ProgressBar 和 DB 连接,并使用 OnTimer 在进度栏中调用 StepIt。 您还可以在 ui 线程中创建进度条,并为工作线程使用自定义消息来修改进度状态。

    无论如何,我同意 AlexEzh 的观点,即最好的方法是让整个非 UI 都在工作线程中工作。

    【讨论】:

      【解决方案5】:

      创建一个成员变量为

      CProgressCtrl m_progress;
      

      DDX_Control 中添加m_progress in DoDataExcchange 和进度条ID

      在按钮点击功能下添加如下代码。

      m_progress.setRange(0,100);
      m_progress.SetPos(1);
      

      【讨论】:

      • 这只是非常基本的信息,既没有要求也没有解决多线程问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多