Обновление индикатора выполнения в Windows C ++ - PullRequest
0 голосов
/ 05 мая 2019

Я создал простую консольную программу Windows на C ++, которая выполняет некоторую обработку и окно с индикатором выполнения, который обновляется по мере продвижения обработки. Проблема в том, что индикатор выполнения не обновляется.

Ниже приведен код моего примера приложения.

#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);
        }
    }
}

Я ожидаю, что индикатор выполнения обновляется каждую секунду, но этого не происходит: индикатор выполнения ничего не делает.

Что не так?

Ответы [ 2 ]

1 голос
/ 06 мая 2019

В вашем коде есть две похожие проблемы:

  1. В обработчике WM_ACTIVATEAPP вы присваиваете CreateThread (...) параметр lpParameter, переменную для передачи в поток,указатель на локальную структуру, определенную в стеке.Как только предложение else, вызывающее этот вызов, заканчивается, структура выходит за пределы области действия, а указатель становится недействительным.

  2. В обработчике WM_UPDATEPROGRESS вы отправляете сообщение SendMessage в дескриптор окнаэто неопределенная переменная.Эта переменная определяется только в вашем обработчике WM_CREATE.

В реальном коде вы хотели бы решить эти проблемы более надежными способами, но для того, чтобы этот код выполнял то, что вы ожидаете, сделайте две переменные состояния статическими:

static HWND hwndPB = NULL;
static OPERATION_INFO operationInfo;

и измените обработчик WM_ACTIVATEAPP таким образом, чтобы он передавал указатель на эту статическую переменную, а не на свой локальный OPERATION_INFO.

По сути, вам нужно помнить, что WNDPROC - это обычные функции C, где обычная область действияправила применяются.Это не какое-либо определение класса.Если вы хотите сохранить состояние, связанное с каким-либо окном, которое является хорошим для всех обращений к его оконной процедуре, вы должны явно поддерживать такое состояние.

0 голосов
/ 05 мая 2019

У меня была такая же проблема, но когда я работал в Delphi.Наиболее вероятная причина заключается в том, что обработка занимает значительное количество времени, и, поскольку обработка и обновление полосы выполняются в одном потоке, полоса заполняется до 100% после завершения обработки.

Решение: попробуйте обновить индикатор выполнения в отдельном потоке.Функция обновления вызывается из основного потока каждую секунду (или так часто, как вы хотите).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...