I use the WAIT WINDOW, but "tweaked" it.
What I did was rolled up a simple procedure that takes two parameters, one is a the message to put up, the other is a timeout. When it's called with no parameters, it simply issues a WAIT CLEAR to clear any previous messages. When it's called with just the message parameter, it puts up the wait window with the message in it until it's called again with no parameters (or a WAIT CLEAR is issued). If both parameters are passed, the message is put up and a timeout is set
The only "issue" I found with the wait window is with it's positioning - I used the MROW() and MCOL() values to figure out approximately where the message should be displayed. It takes a bit of experimentation, but does the job rather nicely.
Good luck!
~Erik Folley