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.
- 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. - 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.
- In the main thread I create a struct with connection parameters, result flag and thread-running-flag. The latter is initially set to
true
. - I create a thread and pass that struct as parameter.
- I create a dialog that displays a progress bar in the main thread.
- Also in the main thread there is a loop that runs while the thread-running-flag is set. It calls
CMyDialog::Animate()
which callsCProgressCtrl::StepIt()
and thenSleep()
s a bit. - The thread executes the db-connection code and sets the running-flag to
false
when done. - 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 .
| .
AfxBeginThread
.CProgressCtrl
and callCreate
, pass the dialog as the parent of the CProgressCtrl, use the marquee style for the progress control.In the Thread create a message waiting loop:
MSG msg;
while(GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
The message loop need to check a global flag to see if to exit the loop.
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.
Create a member variable as
add the
m_progress
inDDX_Control
inDoDataExcchange
with the progress bar IDadd the following code under Button click function.
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.
Have you tried to use
SendMessage
withPBM_SETMARQUEE
instead ofSetMarquee
. 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.