If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register or Login
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

Re: How to UseWaitCursor correctly

Originally Posted by Arjay

The screen shot doesn't already have a cancel button. What you have is a close button (i.e the 'X' button).

Ah, yes, of course, sorry... The screen shot you are referring to is a few versions of the app back. I have attached a current one to this post. (I hope this doesn't look like image overkill à la firoz.raj now. )

You can disable the 'X' button. Not sure how off the top of my head, but I'll look in google in a minute.

Sorry again: I just posted that I found the solution for that issue (including the solution itself) 11 minutes before your post I'm just replying to. Looks like we encountered some kind of synchronisation problem here. Maybe we should use some kind of semaphore or the like? I hope you read this one in time before you waste any time.

The user prompt to verify canceling isn't like a "are you really sure?". It's pretty much the standard when cancelling a long running operation in this manner. If you don't have it, your users will be pissed when they thought the 'X' would merely hide the progress dialog.

I 100% agree when it comes to the X button. But is this really appropriate (or even required) for a button explicitly labelled "Cancel"?

Re: How to UseWaitCursor correctly

Originally Posted by Eri523

I 100% agree when it comes to the X button. But is this really appropriate (or even required) for a button explicitly labelled "Cancel"?

It depends on the 'severity' of what happens if the user cancels. If the user can restart the operation easily enough and the operation doesn't take much time, then you don't need the "are you sure" check. On the other hand, if the operation is destructive or takes a lot of time to run, then you should have one.

Re: How to UseWaitCursor correctly

Originally Posted by Arjay

It depends on the 'severity' of what happens if the user cancels. If the user can restart the operation easily enough and the operation doesn't take much time, then you don't need the "are you sure" check. On the other hand, if the operation is destructive or takes a lot of time to run, then you should have one.

Ok, then I don't think I need this extra check. Processing can be restarted easily by just clicking a button and picking a file in a file open dialog. In the current version, processing of the 731 MB test file takes slightly below 30 seconds. It is faster now again, which is a side-effect of changing from synchronous to asynchronous file processing. But I don't think this is because the worker thread is that amazingly fast as-such, rather it is because I reduced the frequency of progress updates. And as I think I'm limited to a maximum file size of 2 GB anyway, total processing time can't grow that much.

As a contrast, BTW, processing time for a small 6 kB file grew from about 15 ms to 250 ms.

I started changing the program from synchroous to asynchronous processing more or less right after writing my post #14. I had to go some distance before I got to a state that I could compile and run, but than it worked almost instantly.

... as long as I didn't click the Cancel button. If I did that, I ran into a debug assertion. At least it was one that I placed in the program as a precaution myself. Secifically, it was the one on bwDoCount->IsBusy in Form1::DoCount(FileStream ^) (see below).

The current version does an assertion failure on the line just above that, in the assertion on bEwhSignaled.

EDIT: One really important thing I forgot to mention: To my big surprise, Form1::bwDoCount_RunWorkerCompleted() appears to not even get called at all when I click the Cancel button. Or, and that might be even a bit stranger, it actually is called, but the breakpoint I placed there has no effect.

Actually I had to do some brain twisting to get the combination of the worker thread and the modal progress dialog to work at all, as I obviously can't do any work in the GUI thread as long as the progress dialog is open. Maybe the solution I came up with is a bit screwed up, though I don't hope so.

I have been struggling with that a whole lot of time since then, and I can hardly believe that I started this just last sunday. It feels like ages...

I did a lot of fiddling around with the code to no avail. Finally, I reconstructed a state that is close to the initial one and of which I think it's a reasonable point to start over.

As I have run out of ideas now, I hope for some help from you guys around here. Below are the functions that I think are relevant. All variables that are not local are members of the respective classes. I hope they're self-explanatory enough, as I won't include all the declarations for now.

Re: How to UseWaitCursor correctly

Well, in the meantime I've gathered some more information about this issue. (But they're really few as I'm still short of ideas.)

But first let me pick up the following statement from my own previous post:

Originally Posted by Eri523

EDIT: One really important thing I forgot to mention: To my big surprise, Form1::bwDoCount_RunWorkerCompleted() appears to not even get called at all when I click the Cancel button. Or, and that might be even a bit stranger, it actually is called, but the breakpoint I placed there has no effect.

I edited it into the middle of the post, two hours after doing the post itself. Maybe some people who have read the original post short time after I did it missed this one, as I placed it right in the middle of that really long post. ... really my fault...

An update closely related to this one: Form1::bwDoCount_RunWorkerCompleted() actually is called if file processing is completed the normal way, i.e. I don't click the Cancel button. And this confirms, in addition, that the breakpoint I set in the ..._RunWorkerCpleted() handler is indeed effective. It only isn't called if I click the Cancel button.

In the Fibonacci sample from MSDN, the completion function is called in both cases, like I (and probably everyone else) had expected. But the MSDN sample does invocation of the background worker, progress display and cancellation handling all within a single form, and thus was not really that good a model for what I'm doing in my app. (Actually, this was the reason for me having to do a lot of initial thinking before writing my code.)

Another fact: Any of the BackgoundWorker event handlers (except ..._DoWork() of course) runs in the context of the thread owning the BackgroundWorker object, IOW the GUI thread. But that's exactly what I expected, and anyone who really knows about that subject would have known that even more likely.

A funny side note at the end: If the MSDN docs were right, the debugger of the Express Edition wouldn't even have a Threads window. Luckily, this is not the case.

Re: How to UseWaitCursor correctly

Originally Posted by Arjay

Zip up the project and include any data files. Also include what is ocurring and what you expect.

Thank you for your effort Arjay, both backward and forward in time.

I have attached the zipped project in question. I did a clean-up on the project before zipping it, so I hope it doesn't contain any files that are unwanted here. If it does inspite of this, please tell me their extensions and I'll add them to my clean-up settings.

I also added two screen shots. One is the outcome of processing the plain text file Test.txt that I attached as well, when I don't click the Cancel button. But that's not really the issue.

The other screen shot shows the assertion failure I get when I actually click the Cancel button (after some waiting of course). Obviously, I can't attach the 731 MB file that gives me enough processing time to do so. But you can try any large file you want: The actual contents of the file doesn't really matter.

Re: How to UseWaitCursor correctly

Ok, now that I'd waited 1+ week for getting additional insights from here without success, I resorted to a Google search for "BackgroundWorker RunWorkerCompleted not called". Although I didn't expect much from that because I interpreted the lack of resonance here as an indication for the problem not being really common, I found something (19.600 hits).

This taught me at least three things: 1) The problem is not really that uncommon. 2) It is not limited to C++/CLI. 3) It is not new: The search hits date back to at least 2007.

I found at least a partial solution at http://www.experts-exchange.com/Prog..._22354237.html. The user posting there, who was using VB .NET, first resorted to calling Application::DoEvents(). What I suppose to be the real solution was unfortunately blurred on that page and covered by a box suggesting to sign up.

However, I tried adding a single (!) call to Application::DoEvents() before the line waiting for ewhWorkerReallyFinished to get signaled. (Those who didn't download the entire zipped project may have a look at the code excerpt in post #18.) And suddenly it worked like expected!

Yes, I know that calling Application::DoEvents() is evil, and actually the introduction of the background worker was initially intended to remove just that. But it simply works...

So now, can someone tell me why it works now and maybe how I could solve the problem in a more elegant or correct way?

And there's another thing I've learned: People are always told on this site to first try a Google (or whatever) search. Maybe I myself should take that option into account more often...

Re: How to UseWaitCursor correctly

And there's another thing I've learned: People are always told on this site to first try a Google (or whatever) search. Maybe I myself should take that option into account more often...

That should always be your first attempt at solving the problem. More often than not, you can find the answer yourself.

I haven't looked at your code because I don't have VS2010 handy right now. If adding a DoEvents call causes your program to work, whatever you're doing is simply causing the UI thread not to be able to pump messages.

Re: How to UseWaitCursor correctly

This thread is too long, and has too much code, I don't think that anyone really reads this code. However, the last post about DoEvents gives me some idea. There is classic deadlock situation with a worker thread. Consider working thread sending synchronous (Invoke) messages to the main thread. At some point main thread starts to wait for worker thread end in wait operation, this means, it becomes unresponsive and doesn't handle any messages, including Invoke. At the same time, worker thread calls Invoke: deadlock. DoEvents in the main thread solves the problem, handling Invoke call and releasing the worker thread.
This situation is described here: http://www.codeproject.com/KB/cs/workerthread.aspx
Use BeginInvoke instead of Invoke, and this solves the problem. Generally, asynchronous call must be always default choice vs synchronous.
Also, it is a good idea to create thread manually at least once, instead of using BackgroundWorker. This allows to understand what really happens in multithreaded program. BackgroundWorker hides important implementation details.

Re: How to UseWaitCursor correctly

This thread is too long, and has too much code, I don't think that anyone really reads this code.

Well, I already was afraid of something like that some messages up the thread...

But I promise it will be marked resolved soon. Soon, however, not now...

However, the last post about DoEvents gives me some idea. There is classic deadlock situation with a worker thread. [...]

Yes apparently this is exactly the problem I encountered. I didn't take into account that BackgroundWorker might use ordinary Windows messages (or is there something else that's handled by the message pump?) internally for its inter-thread communication.

Use BeginInvoke instead of Invoke, and this solves the problem. [...]

I didn't call either of the two myself, but I strongly suspect that BackgroundWorker did it internally. I'll go and read up on these methods the next days to learn more about the mechanisms of multithreading in the CLR environment.

Also, it is a good idea to create thread manually at least once, instead of using BackgroundWorker. This allows to understand what really happens in multithreaded program. BackgroundWorker hides important implementation details.

I'm afraid your'e pretty darn right! I already considered using Threading::Thread instead of BackgroundWorker while looking for solutions, mostly because the convenience of its Join() method. But then I would have had to rewrite significant amounts of code and lose some of the convenience offered by BackgroundWorker, and so I refrained from doing so. Maybe I would have found a solution much quicker if I hadn't...

Thanks for the link to codeproject.com. I have seen the sample code there uses Threading::Thread, so I will study it in detail soon.

And now another question to the entire audience, that leads back to the original title of that thread: While fighting the deadlock problem I had put aside the fact that now again setting the form's UseWaitCursor property to true near the beginning of Form1::DoCount(FileStream ^) apparently has no effect. (I do see a wait cursor, however, for a short period after closing the progress dialog and before repainting the Chart control.)

I found a discussion about this problem here at MSDN, but the solution of calling the evil Application::DoEvents() right after setting UseWaitCursor didn't help either. The alternative of using Form::Cursor::Current instead, that I think I found elsewhere on MSDN was no solution too. A lack of message processing doesn't seem to be the problem here, as repainting works like a charm. Any tips?

What I don't want to do is setting the wait cursor application-wide, as I don't want it for the progress dialog. But maybe that wouldn't work either...

(The majority of you who didn't download the entire project from the attachment to post #21 may refer to the code excerpt in post #18. There have been some changes to the project in the meantime, but I don't think they're relevant here.)

Unfortunately: No effect, regardless of whether I follow the first code snippet by a call to Application::DoEvents() or not.

Re: How to UseWaitCursor correctly

Originally Posted by Eri523

I didn't take into account that BackgroundWorker might use ordinary Windows messages (or is there something else that's handled by the message pump?) internally for its inter-thread communication....
I didn't call either of the two myself, but I strongly suspect that BackgroundWorker did it internally.

BackgroundWorker doesn't send anything itself, only your own code. I remember that in the beginning of this question we were talking about progress dialog. How progress information is sent to the main thread?

Re: How to UseWaitCursor correctly

Originally Posted by Arjay

One problem you have is that csrSaved needs to be a class field, saved off in the DoCount and restored in the RunWorkerCompleted.

This doesn't help either. Without wanting to doubt your expertise, I didn't put much hope into this change anyway: The problem is not restoring the original cursor, but getting the wait cursor displayed in the first place.