Block mouse and keyboard input

G

Guest

I would like to block input to prevent controls like buttons from being
pressed for a short period of time while a sound is playing.

While my app is doing something like playing a sound synchronously, the user
can press buttons or other controls. These mouse events are queued up and
then my message pump processes them *after* the sound is done playing.

In this scenario, a user can press a button with the stylus but the program
won't act on it until several seconds later (depending on the duration of the
sound). This behavior is unwanted. Rather, it is preferred that such button
presses are ignored, which is preventable by blocking/filtering mouse and
keyboard input.

I have tried a couple of various methods but each one comes with its own
downfall and none have fully solved this problem.

What I would really like is to be able to make a call that tells the OS to
stop sending my application input messages, and then a call to tell the OS to
start sending them again. Any input between the two calls would be blocked
from making it to my application. There appears to be a BlockInput(bool) call
that does just this, but it's not supported in Windows CE as far as I can
tell.

NOTE: I am NOT trying to block CTRL+ALT+DEL or any other system key combos.
Just trying to block regular input, and thus preventing control events.
 
G

Guest

Thought of that one, but then the screen gets filled with grayed out controls
and is kind of ugly. Ran that one by the project lead already and he doens't
like it.

I also thought of taking a screen shot, then puting it up over the form so
it looked like the form and all the controls were there but it would just be
a non-interactive image. But that's even uglier (design wise).

I hope the above solutions don't need to be taken and that there are better
alternatives.

Thanks though.
 
G

Guest

Here is what I have tried along with the problems that came from them.

1) When playing a sound, add a message filter to the main forms message
pump, start a new thread and play the sound in the new thread, when the sound
is done remove the message filter. The message filter removes all mouse and
keyboard events while the sound is playing.

The problem is that the call to play the sound is within a loop that
iterates through some method calls. So although the input is being blocked,
the main form is not being blocked because the sychronous sound is now in a
different thread. Thus the for loop continues and this is undesirable.

2) Add a message filter before playing the sound. Play the sound in the same
thread (main form), when the sound is done send a custom application message
using PostMessage(...). What I was hoping for here was that the message
filter would filter out all the input message up until the custom message is
received.

The problem here is that the OS stores the input messages in the system
message queue and sends them to the thread message queue later (after the
sound is done), thus my custom message is received before any of the input
messages.

3) Same as attempt 2, but before sending the custom message I tried calling
Application.DoEvents() to get the message pump to process the input message
and filter them out.

Problem is code re-entry, for every stylus tap I did during the sound play,
the sound would play again. Application.DoEvents() warns against re-entrant
code which I apparently did.

Thanks in advance
 
T

Tim Wilson

Fair enough. One thing to consider though is that the user might be confused
as to why he/she can tap of buttons that look active and yet nothing
happens. This is the main reason as to why controls gray-out so as to
visually indicate to the user that the control is inaccessible.
 
C

Chris Tacke, eMVP

Why not strip the message queue (ApplicationEx would allow it) after playing
then continue processing as normal after that?
 
G

Guest

How so? I am using ApplicationEx.

I'm not exactly sure what you mean by "strip the message queue", although I
can take a good guess.

Thanks
 
G

Guest

Fair enough indeed. I ran the "disable the controls" idea by the project lead
and he wasn't too fond of it. However, it is still quite tempting to take
that route. Thanks for you input Tim.
 
C

Chris Tacke, eMVP

Well all taps, etc are going to get posted to the message queue, so you
simply need to pull them off before continuing. So something like this
pseudocode

MyApp()
{
DoStuff()
PlaySound()
StripQueue()
DoStuff()
}

void StripQueue()
{
while(PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
GetMessage(out msg, IntPtr.Zero, 0, 0);
}

Of course you might have a conflict for messages with the main pump, so that
might have to be disabled. A far better thing now that I think about it may
to be just run the sound in a separate thread, and in the main thread add an
IMessageFilter implementation that throws out all messages, enable it, play
the sound and disable it.
 
G

Guest

That would conflict with the main threads messages if any non-input messages
were in the queue. I tried something similar where I called DoEvents()
instead of StripQueue() in your example, and had an IMessageFilter
implementation to remove input, but that caused re-entrant code and the sound
played multiple times.

Your second suggestion I also already tried:

Add IMessageFilter to remove input messages
Start new Thread
Play sound in the new thread
While sound is playing, main thread filters out input messages
When sound is done, remove filter to re-enable input messages

This is the closest working attempt I've got so far. The only problem here
is where the PlaySound call is being done. Its being called inside of a loop
that later does some other stuff after calling PlaySound. If PlaySound is in
the same thread and it's called synchronously then the other stuff if the
loop doesn't happen until after the PlaySound method returns (synchronous
flag set).

If PlaySound is in a seperate thread like above, then everything works
except that the other stuff done by the loop happens right after the sound
starts rather than when the sound ends because the loop is in the main thread.

Due to its design I can't move the loop into the sound thread because that
would cause all sort of other issues.

Possible Solution:

Maybe I can just add a semphore that can be checked within the loop which
calls PlaySound. If it's set, then the loop will jump into an inner loop that
calls DoEvents() until the semphore is released. This will forego the futher
calls within the loop and allow for message filtering.

I'll give it a shot. Thanks for your comments Chris.
 
G

Guest

I solved my problem if anyone is interested, here are the steps:

1) Add an implementation of IMessageFilter that filters out mouse and
keyboard messages

2) Call PlaySound with synchronous flag set

3) When sound is done, send a keyboard event via method keybd_event(...)
using a virtual key id that is undefined (therefore should never be sent from
physical key presses)

4) IMessageFilter implementation also watches for the undefined virtual key
id and stops filtering once received.

This works because the simulated KEY_DOWN message is queued up behind all
the actual keyboard and mouse message that happen while the sound is playing.

Might be slightly hacky, but it works marvelously.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top