Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding "(Not Responding)" label in windows while processing lots of data in one lump

Tags:

c++

windows

mfc

I occasionally need to process a large amount of data from one package off the network, which takes sufficiently long that when the user tries to interact with the application windows adds the "(Not Responding)" string to the window title. I am aware this is because the processing is being done within a call to handle a message (some way up the stack) and therefore is blocking the message pump. I'm also aware the ideal way to deal with this is to process the data asynchronously in a separate thread so the pump can continue running, however this is a LARGE desktop application which is single threaded from top to toe and safely spinning this processing off is not feasible in our time frame.

So with that in mind, is there by any chance a way I can at least avoid the "not responding" moniker (which to most users reads as "has crashed") by telling windows my application is about to be busy before I begin the work? I believe there is something along these lines when responding to a request to close, one can keep asking windows for more time to avoid it proclaiming that your not "closing in a timely fashion"

I should add this is a C++ MFC application.

like image 642
Foo42 Avatar asked Dec 31 '08 11:12

Foo42


6 Answers

Ok, firstly I upvoted Frederick's post because like it or not, the second thread is probably the best way to go.

However, if you really don't want to go down this road, you could manually pump the message queue within your apps inner loop. Something like this;

int Refresh()
{
    MSG       msg;
    if (PeekMessage (&msg, NULL, 0, 0,PM_NOREMOVE))
        if ((msg.message == WM_QUIT) 
          ||(msg.message == WM_CLOSE) 
          ||(msg.message == WM_DESTROY) 
          ||(msg.message == WM_NCDESTROY)
          ||(msg.message == WM_HSCROLL)
          ||(msg.message == WM_VSCROLL)
          ) 
          return(1); 
    if (PeekMessage (&msg, NULL, 0, 0,PM_REMOVE))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    return(0);
}

This is actually a piece of code I used prior to rewriting something similar as a seperate thread. Basically I have a look at the queue, filter out unwanted messages, and post on the rest. It works to an extent, but caused some occasional nasty side effects, hence the rewrite.

like image 181
SmacL Avatar answered Oct 21 '22 14:10

SmacL


I don't think the Windows API can help you here.

Alternatively, how about showing a dialog box with a progress bar and make it run in a separate thread?

A text like "This operation may take half an hour" on the dialog box may be appropriate too.

like image 24
Frederick The Fool Avatar answered Oct 21 '22 15:10

Frederick The Fool


You don't have to actually do anything with the messages from PeekMessage. Just call PeekMessage, you don't even have to remove anything from the queue or process it. As long as it is called every 5 seconds or so, it will cause windows to think the process is still responsive.

An alternative idea is to have a separate process/thread that will appear in the notification tray and inform the user that the process is busy waiting for an internal operation to complete. You'll see these in the later versions of Visual Studio, SQL Server Management Studio, etc.

like image 32
adzm Avatar answered Oct 21 '22 15:10

adzm


Win32 has a method for this in user32.dll.

DisableProcessWindowsGhosting()

Disables the window ghosting feature for the calling GUI process. Window ghosting is a Windows Manager feature that lets the user minimize, move, or close the main window of an application that is not responding.

In addition to the above documented behavior, I also verified here (in a C# application) that this Win32 call also prevents the Not Responding label from appearing on the window as desired.

I found this via the C# answer to similar question here: https://stackoverflow.com/a/15380821/29152.

like image 34
DuckMaestro Avatar answered Oct 21 '22 16:10

DuckMaestro


If you fork off a thread you're most likely worried about some other user action happening which may depend on the result of the long running operation (yeah, concurrency). So expanding on what Fredrick said, if you do spin off a new thread and put up a progress bar, you could lock the focus onto the progress bar to stop a user from interacting with the rest of the application. That should be enough to implement a really simple second thread without really having to worry about concurrency because you're essentially locking out the rest of the app by disabling user interation.

like image 39
user49913 Avatar answered Oct 21 '22 16:10

user49913


You'll need to interleave the processing with message handling somehow. If threads are out of the question, you might want to look at splitting the processing into multiple phases. One way to do this is to do some processing when you first receive the packet, then post a message to the application saying "continue processing here". When the application receives the "continue processing here" message, it will do some more processing, and either send another "continue processing here" message or finish up.

There are a couple of considerations though:

  1. You need to make sure that the state of the application is consistent every time you post a message to yourself and defer to the message loop, as other message handling might happen in the mean-time. This can be done e.g. by only changing the state in the final processing phase.
  2. Another packet might arrive while you are still processing the first packet. If changing the order of processing would be bad for the application, you could handle this by e.g. posting a "remind me to process this packet later" message when this happens.

I don't know whether this would be feasible within the design of your application, but it would be one way to solve the problem.

like image 26
Rune Avatar answered Oct 21 '22 15:10

Rune