Click here to Skip to main content
Click here to Skip to main content

Auto Wait Cursor

, 31 Dec 2001
Rate this:
Please Sign up or sign in to vote.
Automate changing the mouse cursor to the hourglass wait cursor.

Introduction

It's recommended practice to change the mouse cursor to an hourglass wait cursor to inform the user that the program is working on something. This is easy enough to do when the programmer knows in advance that something is going to take a while. However, there are often circumstances where you can't predict how long something is going to take, so it's hard to know whether to display the hourglass or not. Ideally, you'd like the cursor to change to an hourglass automatically, as soon as your task had run for more than some time limit, e.g. 1/10 of a second. I needed to do this for a project of mine so I searched through my books and the internet, but couldn't find any applicable code. The closest thing I could find was an article explaining how to do it in Java. It didn't seem like it should be too tough to write a Windows/C++ version, but it ended up taking me longer than I expected to get it working properly so I thought I'd share the results.

Implementation

The basic idea I had was to create a secondary thread that would act as a "timer". The message loop would keep resetting the timer so it normally wouldn't run out. However, if a task took longer, then the timer would run out and the secondary thread would change the cursor to an hourglass. It didn't' take long to set this up, but mysteriously, it didn't work. A little debugging reassured me that my code was working properly, but that the SetCursor call in the secondary thread wasn't working. I guessed that SetCursor didn't work from secondary threads, but a search of the MSDN documentation and the internet didn't find anything about this. Finally, I posted a question to comp.os.ms-windows.programmer.win32 and went home. The first few responses I got didn't really help, but the third response turned out to be the key. The message referred me to an article by Petter Hesselberg in the Nov. 2001 Windows Developers Journal - "The Right Time and Place for the Wait Cursor". I couldn't find the article, but the source-code was online and the key turned out to be using AttachThreadInput. Once I added this to my code, things started to work.

I actually thought I was finished until I discovered that when I pulled down a menu (or brought up a context menu) the cursor changed to an hourglass. The problem was that while the menu was displayed, my message loop wasn't running and so the timer wasn't getting reset. After a certain amount of head scratching I came up with the idea of using a GetMessage hook to reset the timer, since I figured the menu must still be calling GetMessage (or PeekMessage). Sure enough this solved the menu problem. (And probably some related issues like modal dialogs.)

Again I thought I was finished, but I found one last glitch. Just before a tool-tip appeared, the cursor flashed to an hourglass and back. I guess tool-tips don't call GetMessage or PeekMessage while they wait. I fixed this by simply making my timer longer than the tool-tip timer.

My last task was to extract the code, which I'd mixed in with my message loop, into something more reusable. I ended up packaging it into a C++ class. To use the class you simply have to create an instance of it inside your message loop. Something like:

#include "awcursor.h"

...

while (GetMessage(&msg, NULL, 0, 0))
{
    AutoWaitCursor awc;
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

The AutoWaitCursor constructor "starts" the timer, and the destructor (called automatically at the end of the loop) restores the cursor if necessary. The constructor also looks after creating the thread and the hook the first time around.

If you look at the code, you may notice that the only concession I've made to multi-threading is declaring several of the variables volatile. I don't make any attempt to synchronize the threads or prevent them from accessing variables at the same time. This was a deliberate choice, because I wanted to make the code as fast as possible in order to not add overhead to the message loop. How do I justify this? First, the variables are plain integers, and therefore reading and writing them is atomic anyway. Second, if in some rare case, a synchronization problem occurs, the worst that can happen is the cursor might be wrong momentarily. This is a small price to pay to keep the code simple and fast. In practice I haven't seen any problems.

For simplicity, I've included the definition/initialization of the static class members in the header file. This isn't the best setup since it means you can't include this header in more than one source file. Ideally, you'd put the definitions in a separate .cpp file. However, you normally only have one message loop in your program anyway, so this setup seems acceptable.

I hope you find the code and the explanation useful. Good luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Andrew McKinlay
Web Developer
Canada Canada
I've been programming for 25 years, first in C and then in C++. I'm a fan of design patterns and agile methodologies like extreme programming. My interests include language and database design, and memory management and garbage collection. The last few years I have been working more or less full time on the open source Suneido project.

Comments and Discussions

 
GeneralAttachThreadInput Pinmemberemshelly20-Jan-05 6:02 
GeneralRe: AttachThreadInput PinmemberAndrew McKinlay20-Jan-05 12:18 
GeneralRe: AttachThreadInput [modified] PinmemberHQH15-Oct-07 15:06 
QuestionHow to use in MFC program? PinmemberLarryLeonard10-Apr-03 4:20 
AnswerRe: How to use in MFC program? PinmemberAndrew McKinlay10-Apr-03 5:42 
GeneralRe: How to use in MFC program? PinmemberStefan Noll25-Aug-04 10:53 
GeneralRe: How to use in MFC program? PinmemberAndrew McKinlay3-Sep-04 8:21 
Questionwhy bother? Pinmemberlucy16-Jan-03 7:51 
GeneralGood Job~ PinmemberRobbie Wang24-Aug-02 0:53 
GeneralRe: Another possible way... PinmemberAndrew McKinlay14-Aug-02 17:15 
GeneralDialog Box Problem PinmemberSonu_P4-Aug-02 18:15 
GeneralRe: Dialog Box Problem PinmemberAndrew McKinlay5-Aug-02 5:11 
GeneralAwesome! Pinmemberswinefeaster17-Jun-02 14:25 
GeneralTHANKS! PinmemberTodd.Harvey31-Mar-09 7:57 
Generalbetter hook PinmemberAndrew McKinlay25-Feb-02 5:17 
GeneralRe: better hook Pinmemberswinefeaster16-Jun-02 16:25 
GeneralRe: better hook PinmemberAndrew McKinlay17-Jun-02 4:49 
GeneralRe: better hook Pinmemberswinefeaster17-Jun-02 14:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 1 Jan 2002
Article Copyright 2002 by Andrew McKinlay
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid