[WTL] CProgressDlg
21.07.2014, 18:25

Описание

К сожалению, современные компьютеры еще не настолько быстрые, чтобы выполнять все, что надо от них пользователю, мгновенно. Естественно, что эти операции делают в отдельном потоке, чтобы те, кто сидит за компьютером, не думали, что программа повисла. При этом хорошо бы было еще показывать процент выполнения и оставшееся время до конца операции. Вот для облегчения этого случая я и написал этот класс. Только здесь надо сказать об одном предположении. Считается, что во время выполнения операции пользователь больше ничего не может делать паралелльно, т.е. процесс выполнения показывается в модальном диалоге.
Вообще то CProgressDlg я делал ужасно давно, уже даже не помню под какую версию Visual C++. А тут недавно мне напомнили про этот класс, я попробовал его скомпилить в Visual C++ 2005, получил ошибки и, мягко выражаясь, удивился насколько плохо был написал этот класс. Поэтому сейчас я его значительно исправил, да и описание дополнил.

Использование

Создать структуру параметров для потока, например
struct ThreadParams
{
  int foo;
  int bar
};

 
В будущем этот тип пудет передаваться в качестве параметра шаблона.
Создать в ресурсах диалог, например, пусть у него идентификатор будет IDD_PROGRESS. У этого диалога должно быть текстовое поле, в которое будет писаться оставшееся время, например с идентификатором IDC_TIME.
Создать потоковую функцию такую, как описано в MSDN. Т.е. она должна иметь вид
DWORD WINAPI ThreadFunc(void *params);
 
Только здесь есть одна особенность. В эту функцию передается не та структура, которую мы создали ранее на первом шаге (ThreadParams), а другая, описанная ниже
template<class T> struct ProgressParam
{
        T* pParams;
        IProgress* pProgress;
};

 
Здесь тип T как раз и должен быть типом для параметра, который мы определили выше (в данном случае ThreadParams), первое поле pParams - это и есть этот самый параметр, а второй - указатель на интерфейс работы с диалогом. Он объявлен вот так:
class IProgress
 {
 public:
     virtual void SetProgressRange (int minval, int maxval, int currval) = 0;
     virtual void SetProgress(int val) = 0;
 };

 
Функция SetProgressRange() задает интервал некоторой измеряемой величины, которая символизирует процент выполнения, а SetProgress() устанавливает новый процент. Слово "процент" здесь используется как аналогия, т.е. величина может меняться в любых пределах.
Создаем переменную типа CProgressDlg.
CProgressDlg <ThreadParams, IDD_PROGRESS, IDC_TIME > dlg (ThreadFunc, 
        &params, 
        _T("Пожалуйста, подождите") );

 
В шаблон класса мы передаем тип параметров, идентификатор диалога IDD_PROGRESS и идентификатор текстового поля IDC_TIME. Конструктор класса CProgressDlg принимает функцию, выполняемую в отдельном потоке, параметры для потока (переменная станет членом pParams) и заголовок диалога.
Вот пример функции потока:
// Эта функция выполняется в отдельном потоке
DWORD WINAPI ThreadFunc (void* params)
{
         // Получаем экземпляр класса ProgressParam<ThreadParams>,
         // который хранит указатель на IProgress и на параметры
     ProgressParam<ThreadParams> *pp = (ProgressParam<ThreadParams>*)params;

         // Получаем параметры, но в данном случае ничего с ними не делаем
         ThreadParams* threadParams = (ThreadParams*)pp->pParams;

         // Устанавливаем диапазон изменения индикатора прогресса
     pp->pProgress->SetProgressRange(0, 100, 0);

         // Выполянем долгий цикл
     for (int i = 0; i < 100; ++i)
     {
         Sleep (100);
         pp->pProgress->SetProgress(i + 1);
     }
     return 0;
}

 
И, наконец, запускаем наш поток, и одновременно открывается диалог.
dlg.Start();
 
Полностью код с использованием WTL выглядит следующим образом (функция tWinMain была сгенерирована мастером):
#include "stdafx.h"

#include "resource.h"

#include "ProgressDlg.h"

CAppModule _Module;

struct ThreadParams
{
        int foo;
        int bar;
};

// Эта функция выполняется в отдельном потоке
DWORD WINAPI ThreadFunc (void* params)
 {
         // Получаем экземпляр класса ProgressParam<ThreadParams>,
         // который хранит указатель на IProgress и на параметры
     ProgressParam<ThreadParams> *pp = (ProgressParam<ThreadParams>*)params;

         // Получаем параметры, но в данном случае ничего с ними не делаем
         ThreadParams* threadParams = (ThreadParams*)pp->pParams;

         // Устанавливаем диапазон изменения индикатора прогресса
     pp->pProgress->SetProgressRange(0, 100, 0);

         // Выполянем долгий цикл
     for (int i = 0; i < 100; ++i)
     {
         Sleep (100);
         pp->pProgress->SetProgress(i + 1);
     }
     return 0;
 }

int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
        ThreadParams params;
        params.foo = 10;
        params.bar = 20;

        CProgressDlg <ThreadParams, IDD_PROGRESS, IDC_TIME > dlg (ThreadFunc, &params, _T("Пожалуйста, подождите") );
        dlg.Start();
        return 0;
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
        HRESULT hRes = ::CoInitialize(NULL);
// If you are running on NT 4.0 or higher you can use the following call instead to 
// make the EXE free threaded. This means that calls come in on a random RPC thread.
//      HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
        ATLASSERT(SUCCEEDED(hRes));

        // this resolves ATL window thunking problem when Microsoft Layer for Unicode (MSLU) is used
        ::DefWindowProc(NULL, 0, 0, 0L);

        AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls

        hRes = _Module.Init(NULL, hInstance);
        ATLASSERT(SUCCEEDED(hRes));

        int nRet = Run(lpstrCmdLine, nCmdShow);

        _Module.Term();
        ::CoUninitialize();

        return nRet;
}

Просмотров: 577 | Загрузок: 0 | Рейтинг: 5.0/1
X
Ссылка:
BB-код:
HTML-код:
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
%