views:

293

answers:

2

I am trying to show a progress bar while my process is going on...in my application there will be a situation where I gotta read files and manipulate them(it will take some time to complete)..want to display a progress bar during this operation..the particular function I am calling is an win 32 ...so if you check my code below i am upto the point of creating the progress bar in a dialog window and creating a thread Now I dont know how to post the message and where to get the message and handle...Please help me..thanks in advance

    //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: using 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);




        }
+1  A: 

There are a few gotchas when sending messages across threads. If you SendMessage from Thread A to Thread B then what happens internally is that Thread A posts the message to Thread B's message queue. It then sits and waits for the message to be processed before sending the result back to Thread A. This means that you need to be pumping messages in Thread B or Thread A will deadlock.

As to your specific problem you can simply put the following code after you create the progress bar:

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

Then you need to pass pBar into your thread. This is pretty easy. You'll note that CreateThread allows you to pass a parameter to the thread function as a void*. So re-write your CreateThread as:

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

Then change your SetFilesForOperation prototype to this:

LPARAM SetFilesForOperation( HWND pBar );

Note thatyou cast the thread function to the right format. So Windows, internally, will just pass the void*. An implicit cast then happens and what you see at the other end is an HWND rather an a void*. You can pass much more data through by passing a pointer to a struct to the thread function. Just remember not to free the struct (either by letting it drop out of scope or explicitly) before SetFilesForOperation has the info it needs out of it. You can solve this with a simple event that gets fired once the thread function has the data it is after and then waiting in the thread that does the creation for the event to be fired.

Then simply add the following message at the end of the loop:

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

This will then advance the bar by one each loop through.

Edit: As pointed out in the comments its worth just using PostMessage as it won't wait for the return meaning that the message is simply posted to the UI thread's message queue. Do note, though, that if you don't pump that message queue you'll be sending messages across and you won't see the progress bar updating as the messages will just back up in the queue.

Edit 2: You are setting the range of your progress bar AFTER the message loop. So the range never gets set. You need to do that before hand. Its also worth noting that your message pump will only exit when you send a WM_QUIT message. This is not ideal. You could though post a message of your own at the end of your thread loop. The changes you would need are as follows. Firstly you need to declare the custom (User) message.

#define WM_EXITTHREAD WM_USER + 1

Then change your message loop to the following:

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

And finally have the following at the end of your thread:

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: What happens if you use a peek message loop like this?

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 );
    }
}
Goz
What's wrong with PostMessage? Then you do not have to worry about the other thread blocking your execution.
taspeotis
You are quite right. You do need to make sure they get processed though if you want to see the progress happening though ;)
Goz
thanx for replyin goz..its good that u explained..i tried implementing it..it seems that it didnt display the progress bar...do u know what cud be the problem
kiddo
OK what are the values of dialog and pBar? ie Are they actually getting created? I see nothing wrong with pBar's create but I wonder if dialog actually contains a valid handle? If either call is failing call "GetLastError" and let me/us know what the error code is.
Goz
goz its actually getting created..but the only problem is I am not able to display them...its actually when i use the following it will display(b4 calling the thread)..MSG msg;while(GetMessage(msg,NULL,0,0)){ TranslateMessage(msg); Dispatch(msg);}as soon as it displays no further process will happen until if ther is any user interaction in the UI(Provided it need to be handled)..I guess am doing something wrong please correct me goz..
kiddo
goz..i have edited ma code..please check that for me
kiddo
A: 

I changed the Postmessage in the thread call and it worked...

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


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