【问题标题】:Updating progress bar in windows C++在 Windows C++ 中更新进度条
【发布时间】:2019-05-05 16:35:45
【问题描述】:

我用 C++ 创建了一个简单的 Windows 控制台程序,它执行一些处理和一个带有进度条的窗口,该进度条会随着处理的进行而更新。问题是进度条没有更新。

下面是我的示例应用程序的代码。

#include <iostream>
#include <stdlib.h>
#include "atlstr.h"
#include "pch.h"
#include <string.h>
#include "tchar.h"
#include <vector>
#define ISOLATION_AWARE_ENABLED 1
#include <windows.h>
#include <commctrl.h>

using namespace std;

#define WM_UPDATEPROGRESS WM_USER + 1

typedef struct {
    HWND    window;
    HWND    hwndPB;
} OPERATION_INFO;

void DoOperation(LPVOID lpThreadParams) {
    //reading ThreadParams
    OPERATION_INFO *pOperationInfo = (OPERATION_INFO *)lpThreadParams;
    //run a test loop that updates bar
    for (int i = 1; i <= 10; i += 1) {
        // do something
        Sleep(500); // dummily waiting some time
        // send message to update progress bar
        PostMessage(
            pOperationInfo->window, WM_UPDATEPROGRESS, 0, NULL);
    }
}

LONG_PTR CALLBACK WindowProcedure(
    HWND window, unsigned int msg, WPARAM wp, LPARAM lp)
{
    RECT rcClient;
    HWND hwndPB = NULL;
    OPERATION_INFO operationInfo;
    static bool flag_activate = FALSE;

    switch (msg)
    {
    case WM_CREATE:

        // create progress bar
        GetClientRect(window, &rcClient);
        hwndPB = CreateWindowEx(0, PROGRESS_CLASS,
            (LPCWSTR)NULL, (WS_CHILD | WS_VISIBLE),
            20,
            20,
            rcClient.right - rcClient.left - 40,
            20,
            window, (HMENU)0, GetModuleHandle(0), NULL);

        // progress bar settings
        SendMessage(hwndPB, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
        SendMessage(hwndPB, PBM_SETPOS, (WPARAM)0, 0);
        SendMessage(hwndPB, PBM_SETSTEP, (WPARAM)10, 0);

    case WM_ACTIVATEAPP:
        // if message already fired then do nothing:
        // because in this message we have code to initialize thread 
        // that updates progress bar and the thread initialization, 
        // obviously, must be done once.
        if (flag_activate)
            break;
        else
        {
            // store message MW_ACTIVEAPP fired
            flag_activate = TRUE;

            // initialize thread
            OPERATION_INFO operationInfo = { window, hwndPB };
            HANDLE threadHandle;
            // make thread call DoOperation routine
            threadHandle = CreateThread(
                NULL, 0, 
                (LPTHREAD_START_ROUTINE)DoOperation, 
                (LPVOID)&operationInfo, 0, 0);
        }
        break;

    case WM_UPDATEPROGRESS:
        //get info with HWND of the progress bar to update
        //update the progress bar
        SendMessage(hwndPB, PBM_STEPIT, NULL, 0);
        //release memory
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0L;

    case WM_LBUTTONDOWN:
        break;

    default:
        return DefWindowProc(window, msg, wp, lp);
    }
}

int main()
{
    // Window creation

    // structure initialization
    LPCWSTR myclass = L"myclass";
    WNDCLASSEX wndclass = { 
        sizeof(WNDCLASSEX), CS_DBLCLKS, 
        WindowProcedure, 0, 0, GetModuleHandle(0), 
        LoadIcon(0,IDI_APPLICATION), LoadCursor(0,IDC_ARROW), 
        HBRUSH(COLOR_WINDOW + 1), 0, myclass, 
        LoadIcon(0,IDI_APPLICATION) };
    // preliminar action
    if (RegisterClassEx(&wndclass))
    {
        // create window
        HWND window = CreateWindowEx(0, myclass, L"Processing",
            WS_OVERLAPPED | WS_SYSMENU,
            CW_USEDEFAULT, CW_USEDEFAULT,
            300, 150,
            0, 0, GetModuleHandle(0), 0);
        // if OK
        if (window)
        {
            // show window
            ShowWindow(window, SW_SHOWDEFAULT);
            MSG msg;
            while (GetMessage(&msg, 0, 0, 0)) 
                DispatchMessage(&msg);
        }
    }
}

我希望进度条每秒更新一次,但这不会发生:进度条什么都不做。

怎么了?

【问题讨论】:

  • 警告:如果您使用的是 Visual Studio,您会发现 #include "pch.h" 的放置是不必要的或致命的。如果使用了预编译的头文件,#include "pch.h"MUST be the first non-comment in the file。之前的一切都被默默地忽略了。如果未使用预编译的头文件,则无需包含 pch.h。
  • 几年前我看到了一个类似的问题:没有代码调用 Invalidate,所以它从来没有调用 Paint,所以它从来没有更新任何东西。 STEPIT 消息是否做了所有必要的事情?
  • hwndPB 是一个局部变量。如果您在 WM_UPDATEPROGRESS 处理程序上设置断点,您会注意到这一点。
  • 其他问题包括演员(LPTHREAD_START_ROUTINE)DoOperation。如果DoOperation 有错误的签名,正如编译器告诉你的,你必须修复签名。你的演员只是告诉编译器一个谎言,即你声称DoOperationLPTHREAD_START_ROUTINE 而不是。

标签: c++ winapi


【解决方案1】:

您的代码有两个类似的问题:

  1. 在 WM_ACTIVATEAPP 处理程序中,您正在分配 CreateThread(...) 的 lpParameter 参数,要传递给线程的变量, 指向堆栈上定义的本地结构的指针。只要 else-clause that call is in end that struct is out of scope and 指针无效。

  2. 在 WM_UPDATEPROGRESS 处理程序中,您正在向一个 未定义变量的窗口句柄。该变量仅在您的 WM_CREATE 处理程序中定义。

在实际代码中,您希望以更健壮的方式处理这些问题,但为了让此代码执行您期望的操作,请将您的两个状态变量设为静态:

static HWND hwndPB = NULL;
static OPERATION_INFO operationInfo;

并修改 WM_ACTIVATEAPP 处理程序,使其将指针传递给这个静态变量,而不是传递给它自己的本地 OPERATION_INFO。

基本上,您需要记住 WNDPROC 是适用于正常范围规则的正常 C 函数。它们不是任何类型的类定义。如果您想维护与某个窗口关联的状态,该状态在对其窗口过程的调用中是良好的,您必须显式维护这种状态。

【讨论】:

  • 感谢 jwezorek。我知道出了什么问题。你能告诉我更多关于处理这些问题的更可靠的方法吗?
  • 将它们设为静态的问题在于,该解决方案仅适用于窗口的一个实例。如果您想要一些与有多个实例的 Window 类的每个实例相关联的数据,一种方法是动态分配一个结构并通过 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) pMyData)
  • 另一件事是您实际上根本不需要存储进度条子控件的句柄。如果使用子 id 创建它,则可以通过 GetDlgItem(...) 恢复其 HWND
【解决方案2】:

我遇到了同样的问题,但是当我在 Delphi 工作时。最可能的原因是处理需要花费大量时间,而且由于处理和条形更新发生在同一个线程上,因此处理完成后条形会填充到 100%。

解决方案:尝试在单独的线程中更新进度条。更新函数从主线程中被调用,每秒(或你希望的频率)。

【讨论】:

  • UI 必须在主线程之外运行。解决方案是抽出消息队列,或者在不同的线程中运行长计算。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多