Как реализовать прогрессбар (чтобы показать прогресс) с использованием концепции потоков в win 32? - PullRequest
2 голосов
/ 31 марта 2010

Я пытаюсь показать индикатор выполнения, пока идет мой процесс ... в моем приложении будет ситуация, когда мне придется читать файлы и манипулировать ими (это займет некоторое время) .. хочу отобразить индикатор выполнения во время этой операции .. конкретная функция, которую я вызываю, является win 32 ... поэтому, если вы проверите мой код ниже, я дошел до создания индикатора выполнения в диалоговом окне и создания потока. Теперь я не знаю как отправить сообщение и где взять сообщение и как с ним обращаться ... Пожалуйста, помогите мне .. спасибо заранее

    //my  function
    int Myfunction(....)
    {
     MSG msg;
     HWND dialog = CreateWindowEx(0,WC_DIALOG,L"Proccessing...",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
         600,300,280,120,NULL,NULL,NULL,NULL);
     HWND pBar =  CreateWindowEx(NULL,PROGRESS_CLASS,NULL,WS_CHILD|WS_VISIBLE,40,20,200, 20,
           dialog,(HMENU)IDD_PROGRESS,NULL,NULL);


      while(GetMessage(&msg,NULL,0,0))
{
    TranslateMessage(&msg);
     Dispatch(&message);
}
SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

     HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
        NULL,NULL,0);

    }


    LPARAM SetFilesForOperation(...)       
    {

     for(int index = 0;index < noOfFiles; index++)
     {


      *checkstate = *(checkState + index);
      if(*checkstate == -1)
      {
       *(getFiles+i) = new TCHAR[MAX_PATH];
       wcscpy(*(getFiles+i),*(dataFiles +index));
       i++;

      }
      else
      {
       (*tempDataFiles)->Add(*(dataFiles+index));
       *(checkState + localIndex) = *(checkState + index);
       localIndex++;
      }

      PostMessage(pBar,PBM_SETPOS,(WPARAM)index,0);
     }
    }

EDIT2: используя AFXTHREAD

//instead of createthread i used AfxBegin thread
    ptrThread = AfxBeginThread((AFX_THREADPROC)SetFilesForOperation(pBar,checkstate,checkState,noOfFiles,i,getFilesforcompression,dataFiles,&tempDataFiles,localIndex),
        NULL,THREAD_PRIORITY_ABOVE_NORMAL,NULL,NULL,NULL);


for(int index = 0;index < noOfFiles; index++)
    {

        MSG msg;
        *checkstate = *(checkState + index);
        if(*checkstate == -1)
        {
            *(getFilesforcompression+i) = new TCHAR[MAX_PATH];
            //*(getFilesforcompression+i) = L"C:\\Documents and Settings\\rakesh\\Desktop\\try2_Extracted";
            wcscpy(*(getFilesforcompression+i),*(dataFiles +index));
            i++;

        }
        else
        {
            (*tempDataFiles)->Add(*(dataFiles+index));
            *(checkState + localIndex) = *(checkState + index);
            localIndex++;
        }


        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );
        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_STEPIT, (WPARAM)index, 0 );
        PostMessage( pBar, MSG_PROGRESS_VALUE, 0, 0 );


        while(1)
        {
            while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
            {


                AfxGetThread()->PumpMessage();
                Sleep(10);




        }

Ответы [ 2 ]

3 голосов
/ 31 марта 2010

Есть несколько ошибок при отправке сообщений между потоками. Если вы посылаете сообщение из потока A в поток B, то внутренне происходит то, что поток A отправляет сообщение в очередь сообщений потока B. Затем он сидит и ждет обработки сообщения, прежде чем отправить результат обратно в поток А. Это означает, что вам нужно перекачать сообщения в поток B, или поток A заблокируется.

Что касается вашей конкретной проблемы, вы можете просто поставить следующий код после создания индикатора выполнения:

SendMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );

Тогда вам нужно передать pBar в вашу ветку. Это довольно легко. Вы заметите, что CreateThread позволяет передавать параметр в функцию потока как void *. Поэтому переписать ваш CreateThread как:

HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
                                (void*)pBar,NULL,0);

Затем измените ваш прототип SetFilesForOperation следующим образом:

LPARAM SetFilesForOperation( HWND pBar );

Обратите внимание, что вы привели функцию потока к нужному формату. Так что внутренне Windows просто пропустит пустоту *. Затем происходит неявное приведение, и то, что вы видите на другом конце, - это HWND, а не пустота *. Вы можете передать гораздо больше данных, передав указатель на структуру функции потока. Только не забывайте освобождать структуру (либо позволяя ей выпадать из области видимости, либо явным образом) до того, как SetFilesForOperation получит из нее необходимую информацию. Это можно решить с помощью простого события, которое запускается, когда функция потока получает данные, за которыми оно следует, и затем ожидает в потоке, который создает событие, которое будет запущено.

Затем просто добавьте следующее сообщение в конце цикла:

PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );

Это будет затем продвигать планку на единицу каждый цикл.

Редактировать: Как указано в комментариях, стоит просто использовать PostMessage, так как он не будет ждать возврата, что означает, что сообщение просто отправляется в очередь сообщений потока пользовательского интерфейса. Однако обратите внимание, что если вы не прокачаете эту очередь сообщений, вы будете отправлять сообщения через нее и не увидите обновления индикатора выполнения, поскольку сообщения просто будут резервироваться в очереди.

Редактировать 2: Вы устанавливаете диапазон индикатора выполнения ПОСЛЕ цикла сообщения. Таким образом, диапазон никогда не устанавливается. Вы должны сделать это заранее. Также стоит отметить, что ваш насос сообщений будет выходить только при отправке сообщения WM_QUIT. Это не идеально. Вы можете написать собственное сообщение в конце цикла. Вам потребуются следующие изменения. Во-первых, вам нужно объявить пользовательское (пользовательское) сообщение.

#define WM_EXITTHREAD WM_USER + 1

Затем измените цикл сообщений на следующий:

SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{ 
    if ( msg.message == WM_EXITTHREAD )
    {
         break;
    }
    TranslateMessage(&msg); 
    Dispatch(&msg); 
}

И, наконец, в конце вашей темы есть следующее:

PostMessage( pBar, WM_EXITTHREAD, 0, 0 );
EndThread( 0 );  // This is the preferred way of exiting a thread in C
return 0; // This is the preferred way of exiting a thread in C++ so that destructors get called.

Edit3: Что произойдет, если вы будете использовать цикл просмотра сообщений, как это?

while( 1 )
{
    MSG msg;
    if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        if ( msg.message == WM_EXITTHREAD )
        {
            break;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        Sleep( 0 );
    }
}
0 голосов
/ 02 апреля 2010

Я изменил сообщение в вызове потока, и оно сработало ...

while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
                {


                    AfxGetThread()->PumpMessage();
                    Sleep(10);
}
...