my PROGRESS BAR redraws not all, WINAPI - winapi

in my application I am entering a loop of unknown number of elements,
so I am showing a progress bar to inform that application is running,
and this code works FINE, progres bar is refreshing independly:
do
{
...
SendMessage(hPBLoading, PBM_STEPIT, 0, 0);
...
} while(true);
but unfortunately the rest of window doesn't refresh (which is obvious due to the loop)
and in Windows 7 after a few seconds Windows treat my app like it break down, and it back and refresh after ending the loop,
so I figured out that I need to dispatch the message queue and I changed my code to this:
do
{
...
SendMessage(hPBLoading, PBM_STEPIT, 0, 0);
...
us_tmpBreakCounter++;
...
if (us_tmpBreakCounter >= 10)
{
us_tmpBreakCounter = 0;
UpdateSomeOtherElementsinWindow();
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} while(true);
It works fine, the applcation is updated and refreshed after ex. 10 counts so its never break down,
but unfortunately another problem occured, now the PROGRESS BAR doesn't redraw fully - the beveled frame dissapears after a few seconds, check this image below:
http://mstanisz.website.pl/temp/progressbar_01.jpg
Thanks For Your Help!
m.

Seems like you're using a GUI thread to do some long task. That's not a good practice. When you want to perform long tasks, use a worker thread for that, and let the GUI thread handle the GUI while the worker works. You'll have to do some extra work taking care of synchronization between the two threads (stop the worker if the GUI is closed, avoid starting a task before the current one has ended and so on), but that's the way it works.

Related

Calling ShowWindow(SW_SHOW) on ATL modeless dialog not bringing window to front

I have a modeless dialog which I am showing and hiding programmatically from a worker thread. The problem is that the CWindow::ShowWindow(SW_SHOW) function is sometimes bringing the dialog to the front, and sometimes not.
Task: Show/Hide a simple logging & control window for an ATL COM object (dll) that runs in a separate thread. The COM object isn't a control: it is being used to wrap processing code for use in Excel VBA.
I've created a CMyDialog class derived from the CAxDialogImpl template. Then I'm using the CWorkerThread template for the worker thread. This is the Execute block (ie the function that runs in the worker thread).
HRESULT CLogProcessor::Execute(DWORD_PTR dwParam, HANDLE hObject)
{
m_bRunning = true; //Winging it as this isn't thread-safe, but I don't think that's the issue.
if (m_pDlg == 0)
{
m_pDlg = new CMyDialog(); //My dialog class
m_pDlg->SetStopEvent(m_hStopEvent); //Save the handle of the stop event in the Dialog class
m_pDlg->Create(NULL); //Create with no parent
m_pDlg->ShowWindow(SW_SHOW); //Show the window
}
//Loop here until told to stop by the Stop Event being signalled in the calling thread
while (WaitForSingleObject(m_hStopEvent, 0) == WAIT_TIMEOUT) //Polling Stop Event
{
//Have to keep the message loop going!
MSG uMsg;
while (PeekMessage(&uMsg, m_pDlg->m_hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
}
//The thread uses a protected "stack" of string messages to write to my logging window
string strMsg;
while (m_MsgStack.PopFront(strMsg))
{
std::wstring ws = std::wstring(strMsg.begin(), strMsg.end());
CWindow eb(m_pDlg->GetDlgItem(IDC_EDIT1));
eb.SendMessage(EM_REPLACESEL, 0, (LPARAM)ws.c_str());
}
}
//Stop event has been signalled so clear up the dialog
m_pDlg->DestroyWindow();
delete m_pDlg;
m_pDlg = 0;
m_bRunning = false;
return S_OK;
}
My Log object has two functions, StartLog() and StopLog() which are called by the COM object which is doing the main work of data processing.
void CLogProcessor::StartLog()
{
if (!m_bRunning)
{
::SetEvent(m_hStartEvent);
}
}
void CLogProcessor::StopLog()
{
if (m_bRunning)
{
::SetEvent(m_hStopEvent);
}
}
This works fine. I run VBA code in an Excel spreadsheet with buttons that call (via the COM object) StartLog() and StopLog(). The first time the Log Dialog appears all is good: it is activated and comes to the front. Firing StopLog() closes the dialog and it disappears. But the second time I call StartLog() the dialog window appears but it is not at the front, but behind other windows. I have tried a CWindow::BringWindowToTop() and/or SetFocus() call straight after the ShowWindow() but it makes no difference.
Another interesting feature is that my dialog has a system menu and so an 'X' to close it in the top-right hand corner. In the Dialog's message map, I am handling the WM_CLOSE message:
BEGIN_MSG_MAP(CMyDialog)
... Other entries
MESSAGE_HANDLER(WM_CLOSE, OnClose)
...
CHAIN_MSG_MAP(CAxDialogImpl<CMyDialog>)
END_MSG_MAP()
With OnClose() setting the Stop Event handle (previously passed to the Dialog object). Ie doing pretty much the same as the StopLog() function.
LRESULT CMyDialog::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
::SetEvent(m_hStopEvent);
return 0;
}
If I close the log dialog with the mouse click, it goes away as before. BUT the next time I call StartLog() the dialog does what it is supposed to do and comes to the front, which is the action I want. So I replaced the StopLog() code with a SendMessage(WM_CLOSE) to my dialog: it closed the window, but again it did not reappear on top. Another difference is that using the Close X on the dialog means that the Dialog has the focus when it is closed. Closing it from the Excel button means that the Excel window has the focus.
I realize this is rather a long question, and thanks very much if you have soldiered down to the end! I'd been keen for any solution / insight.
Update: I didn't solve the problem as stated, just re-worked the code to use a modal Dialog box in the worker thread. When I am done with the dialog, I PostMessage(WM_CLOSE) from the main thread: the dialog ends and the worker thread finishes (having set an event to say it is done, so that the thread is not destroyed before the Dialog is cleaned up).
The dialog polls the stack of strings to display in response to a WM_USER+xxx message, posted from the main thread using PostMessage();
I still have the same issue that the dialog doesnt appear on top the second time I run the worker thread, but this time adding a ShowWindow(SW_SHOW) in the OnInitDialog() handler seems to bring the dialog to the top when it appears.
It also means I don't need my own message loop.

How to keep UI responsive when consuming items produced by background thread producer?

I've offloaded a long-running, synchronous, operation to a background thread. It takes a while to get going, but eventually it starts producing items very nicely.
The question is then how to consume then - while maintaining a responsive UI (i.e. responding to Paint and UserInput messages).
One lock-free example sets up a while loop; we consume items while they are items to consume:
// You call this function when the consumer receives the
// signal raised by WakeConsumer().
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
}
}
The problem is that the background thread, once it gets going, can produce the items very quickly. This means that my while loop will never have a chance to stop. That means it will never go back to the message queue to respond to pending paint and mouse input events.
I've turned my asynchronous multi-threaded application in a synchronous wait as it sits inside:
while (StuffToDo)
{
Consume(item);
}
Posting Messages
Another idea is to have the background thread PostMessage a message to the main thread every time an item is available:
ProduceItemsThreadMethod()
{
Preamble();
while (StuffToProduce())
{
Thing item = new Item();
SetupTheItem(item);
InterlockedAddItemToTheSharedList(item);
PostMessage(hwndMainThreadListener, WM_ItemReady, 0, 0);
}
}
The problem with this is that any posted message is always higher priority than any:
paint messages
mouse move messages
So as long as there is posted messages available, my application will not be responding to paint and input messages.
while GetMessage(out msg)
{
DispatchMessage(out msg);
}
Every call to GetMessage will return a fresh WM_ItemReady message. My Windows message processing will be flooded with ItemReady messages - preventing me from processing paints until all the items have been added.
I've turned my asynchronous multi-threaded application in a synchronous wait.
Limiting the number of posted messages doesn't help
The above is actually worse than the first variation, because we flood the main thread with posted messages. What we want to do is only post a message if the main thread hasn't dealt with the previous message we posted. We can create a flag that is used to indicate if we've already posted a message, and if the main thread still hasn't processed it
ProduceItemsThreadMethod()
{
Preamble();
while (StuffToProduce())
{
Thing item = new Item();
SetupTheItem(item);
InterlockedAddItemToTheSharedList(item);
//Only post a message if the main thread has a message waiting
int oldFlagValue = Interlocked.Exchange(g_ItemsReady, 1);
if (oldFlagValue == 0)
PostMessage(hwndMainThreadListener, WM_ItemReady, 0, 0);
}
}
And in the main thread we clear the "ItemsReady" flag when we've processed the queued items:
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
The problem again is that the thread can fill the list faster than we can consume it; so we never get a change to fall out of the ConsumeWork() function in order to handle user input.
As soon as ConsumeWork returns, the background producer thread generates a new WM_ItemReady message. The very next time i call GetMessage
while GetMessage(out msg)
{
DispatchMessage(out msg);
}
it will be a WM_itemReady message. I will be stuck in a loop.
I've turned my asynchronous multi-threaded application in a synchronous wait.
Limiting ourselves to a count of items doesn't help
We could try forcing a break out of the while loop after, say, processing 100 items:
void ConsumeWork()
{
int itemsProcessed = 0;
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
itemsProcessed += 1;
if (itemsProcessed >= 250)
break;
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
This suffers from the same problem as the previous incarnation. Although we will leave the while loop, the very next message we will recieve will again be the WM_ItemReady:
while (GetMessage(...) != 0)
{
TranslateMessge(...);
DispatchMessage(...);
}
that's because WM_PAINT messages will only appear if there are no other messages. And the thread is itching to create a new WM_ItemReady message and post it in my queue.
Pumping the message loop myself?
Some people cry a little inside when they see people manually pumping messages to fix unresponsive applications. So lets try manually pumping messages to fix unresponsive applications!
void ConsumeWork()
{
Thing item;
while ((item = InterlockedGetItemOffTheSharedList(sharedList)) != nil)
{
ConsumeTheThing(item);
ManuallyPumpPaintAndInputEvents();
}
Interlocked.Exchange(g_ItemsReady, 0); //tell the thread it can post messages to us again
}
I won't go into the details of that function, because it leads to the re-entrancy problem. If the user of my library happens to try to close the window they're on, destroying my helper class with it, i will suddenly come back to execution inside a class that has been destroyed:
ConsumeTheThing(item);
ManuallyPumpPaintAndInputEvents(); //processes WM_LBUTTONDOWN messages will closes the window which destroys me
InterlockedGetItemOffTheSharedList(sharedList) //sharedList no longer exist BOOM
Down and down I go
I keep going in circles trying to solve the problem of how to maintain a responsive UI when using background threads. I've tinkered with four solutions in this question, and three others before asking it.
I can't be the first person to have used the Producer-Consumer model in a user interface.
How do you maintain a responsive UI?
If only i could post a message with priority lower than Paint, Input, and Timer :(

CFRunLoopSourceSignal doesn't work

I'm debugging Qt5.3.1 on Mac, because my program freezes sometimes (intermittent ). I discovered that it is because the QTimer can't work properly.
In Qt code, they use the following two lines to trigger function activateTimersSourceCallback
CFRunLoopSourceSignal(d->activateTimersSourceRef);
CFRunLoopWakeUp(mainRunLoop());
void QCocoaEventDispatcherPrivate::activateTimersSourceCallback(void *info)
{
static int counter = 0;
NSLog(#"finished activeteTimersSourceCallback %d", counter++);
}
but sometimes, these two lines doesn't work, activateTimersSourceCallback won't get called.
I googled, but I couldn't find any solution? is this a known OS bug?
the initialization details:
// keep our sources running when modal loops are running
CFRunLoopAddCommonMode(mainRunLoop(), (CFStringRef) NSModalPanelRunLoopMode);
CFRunLoopSourceContext context;
bzero(&context, sizeof(CFRunLoopSourceContext));
context.info = d;
context.equal = runLoopSourceEqualCallback;
// source used to activate timers
context.perform = QCocoaEventDispatcherPrivate::activateTimersSourceCallback;
d->activateTimersSourceRef = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
Q_ASSERT(d->activateTimersSourceRef);
CFRunLoopAddSource(mainRunLoop(), d->activateTimersSourceRef, kCFRunLoopCommonModes);
Such behavior very likely can occur when UI event loop is overloaded with events or some business logic takes too long time. You should to check your business logic and move it to separate thread or run asynchronous.

Keep rendering a 3D window during modal dialogs/opening main menu?

(I use Ogre3D for the rendering but the question should be generic.)
The problem: most 3D aplications use a cycle which iterates rendering a frame and checking for messages and processing them. However if a dialog is opened (MessageBox or similar), it blocks the execution of the thread and is running it's own message cycle, but it obviously does not call the 3D rendering function in it.
What is the preferred, or "best" way of keeping rendering the 3D scene even when dialogs are open? The normal applications do not suffer from this problem, because their re-rendering is handled by WM_PAINT messages and similar, and since modal dialogs do have internal message loop, the window proc get's called when needed and everything looks fine. In my 3D project however, "when needed" is all the time, because the window has to be updated, even without WM_PAINT messages.
The simple solution that comes to mind is to register a timer for the time when dialogs are open, and render 3D scene from the WindowProc, but is it really the best? Seems very dirty...
I don't know that this is the best way, but I think it will work.
Add a handler for WM_ENTERIDLE that uses PeekMessage to do something like:
case WM_ENTERIDLE:
while (!PeekMessage())
{
DoYourRendering();
}
return 0;
I would suggest having the code post a custom message to itself when entering the modal operation. You can then render the current frame when the modal loop dispatches the message, and then post another message to keep a rendering loop running. Once the modal operation finishes, you can stop posting messages to yourself and go back to your normal rendering logic. For menus, you can catch the WM_ENTERMENULOOP and WM_EXITMENULOOP messages to detect when the modal menu message loop begins and ends.
For example:
const UINT WM_RENDER_FRAME = WM_USER+100:
.
BOOL m_InModalOp = FALSE;
.
case WM_ENTERMENULOOP:
m_InModalOp = TRUE;
PostMessage(hwnd, WM_RENDER_FRAME, 0, 0);
break;
case WM_EXITMENULOOP:
m_InModalOp = FALSE;
break;
case WM_RENDER_FRAME:
if (m_InModalOp)
{
// render a frame...
PostMessage(hwnd, WM_RENDER_FRAME, 0, 0);
}
break;
.
m_InModalOp = TRUE;
PostMessage(hwnd, WM_RENDER_FRAME, 0, 0);
MessageBox(...);
m_InModalOp = FALSE;

threaded NSProgressIndicator problem

I'm trying to figure out how to update an indeterminate NSProgressIndicator in the UI using a secondary thread while the main thread does some heavy lifting, just like dozens of apps do.This snippet is based on Apple's "Trivial Threads" example using Distributed Objects (DO's):
// In the main (client) thread...
- (void)doSomethingSlow:(id)sender
{
[transferServer showProgress:self];
int ctr;
for (ctr=0; ctr <= 100; ctr++)
{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
NSLog(#"running long task...");
}
}
// In the secondary (server) thread...
- (oneway void)showProgress:(Controller*)controller
{
[controller resetProgressBar];
float ticks;
for (ticks=0; ticks <= 100; ticks++)
{
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[controller updateProgress:ticks];
NSLog(#"updating progress in UI...");
}
}
Unfortunately however, there's no way I can get both threads to run concurrently. Either the secondary thread will run and the main thread waits until it's finished OR the main thread runs followed by the secondary thread -- but not both at the same time.
Even if I pass a pointer to the server thread and ask it to update the progress bar directly (without calling the main thread back) it makes no difference. It seems that once the main thread enters a loop like this it ignores all objects sent to it. I'm still a novice with Obj-C and I'd really appreciate any help with this.
AppKit isn't thread-safe, at all. You have to update the UI from the main thread, or all sorts of crazy stuff will happen (or it just won't work).
The best way is to do your work on the secondary thread, calling back to the main thread when you need to update the UI:
-(void)doSomethingSlow:(id)sender {
[NSThread detachNewThreadSelector:#selector(threadedMethod) toTarget:self withObject:nil];
// This will return immediately.
}
-(void)threadedMethod {
int ctr;
for (ctr=0; ctr <= 100; ctr++) {
NSLog(#"running long task...");
[self performSelectorOnMainThread:#selector(updateUI)];
}
}
-(void)updateUI {
// This will be called on the main thread, and update the controls properly.
[controller resetProgressBar];
}
You might want to try switching your threads. In general, UI updates and user input are handled on the main thread, and heavy tasks are left for secondary threads.

Resources