Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a MFC dialog with a progress bar in a separate thread?

My application may take a while to connect to a database. This connection is made with a single library function call, i.e. I cannot put progress updates in there and make callbacks or something similar.

My idea was to create a dialog with a progress bar in a separate thread before connecting to the DB. This dialog will continually change the progress status with CProgressCtrl::StepIt() so the user sees something happening.
After that dialog is set up and doing its thing I want to call the DB connection function from the main thread. After the connection function completed, I want to stop the progress bar thread.

Let me paint a picture:

CMyApp::       ProgressThread
InitInstance()      .
    |               .
    |               .
    +-Create Dialog-+
    |               |
    |             Animate
 Connect          Progress
    to             Bar
    DB              |
    |               |
    +-Destroy Dlg---+
    |               .
    |               .

Is that possible? If yes, how?

Maybe the whole thing would work using timers, too. Would probably be much simpler but I couldn't get that to work either.

  1. I am aware of CProgressCtrl::SetMarquee() which might do exactly what I need but I can't use it because the application does not have Unicode support.
  2. I could move the db connection call into a separate thread but that way it looks like a lot of changes to the code and extra handling of connection errors.

Update 2
I got it working the way AlexEzh and Javier De Pedro suggested: Put the DB stuf into its own thread.
initially I had concerns about how error handling could be done but it's actually quite similar to how it was before.

  1. In the main thread I create a struct with connection parameters, result flag and thread-running-flag. The latter is initially set to true.
  2. I create a thread and pass that struct as parameter.
  3. I create a dialog that displays a progress bar in the main thread.
  4. Also in the main thread there is a loop that runs while the thread-running-flag is set. It calls CMyDialog::Animate() which calls CProgressCtrl::StepIt() and then Sleep()s a bit.
  5. The thread executes the db-connection code and sets the running-flag to false when done.
  6. When the main thread exits the loop it can handle errors exactly as it did before.

Disadvantage: Moving the mouse over the window doesn't work. It's invisible. Thus no cancel-button or other interactive dialog elements can be used. I can live with that, however.

Since you liked the diagram, here is how it now looks like:

CMyApp::        WorkerThread
InitInstance()      .
    |               .
    |               .
Create Dialog       .
    |               .
    +-Start Thread--+
    |               |
    |             Connect
 Animate            to
 Progress           DB
   Bar              |
    |               |
    +-Thread Ends---+
    |               .
 Destroy Dlg        .
    |               .
like image 360
foraidt Avatar asked Nov 03 '09 17:11

foraidt


4 Answers

I hope this article about creating own-thread splash screen with progress bar could be helpful. I wrote it while solving the problem with thread locking at MFC message queue level.

like image 50
Kirill V. Lyadvinsky Avatar answered Sep 18 '22 10:09

Kirill V. Lyadvinsky


It would still be safer to move the DB connection logic to the separate thread. With DB on the dialog thread, you will be able to repaint the progress bar but not other controls in the dialog.

like image 27
AlexEzh Avatar answered Sep 19 '22 10:09

AlexEzh


  1. Create worker thread using AfxBeginThread.
  2. In that thread Create a CProgressCtrl and call Create, pass the dialog as the parent of the CProgressCtrl, use the marquee style for the progress control.
  3. In the Thread create a message waiting loop:

    MSG msg;
    while(GetMessage(&Msg, NULL, 0, 0))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }

  4. The message loop need to check a global flag to see if to exit the loop.

like image 31
Shay Erlichmen Avatar answered Sep 21 '22 10:09

Shay Erlichmen


Have you tried to use SendMessage with PBM_SETMARQUEE instead of SetMarquee. I've never tried myself but it should work.

In my opinion the easiest way to achive what you want to do is making both the ProgressBar and DB connection in the ui thread and using OnTimer to call StepIt in the progress bar. You can also create the progress bar in the ui thread and use a custom message for the working thread to modify the progress status.

Anyway, I agree with AlexEzh that the best way to do it is making the whole non-UI work in the working thread.

like image 21
Javier De Pedro Avatar answered Sep 18 '22 10:09

Javier De Pedro