MicroVirus
MicroVirus

Reputation: 5477

How/why does .NET's SuppressKeypress work?

[This question is at its core about WinAPI programming]


In .NET terms:

When the Keydown event fires in WinForms, you can set the KeyEventArgs.SuppressKeypress = true and then the subsequent Keypress/Character is not sent to the control.

I found the following SO answer that details how the Control does it: Using SuppressKeyPress event to block a KeyUp event

But now I don't understand, how can this work properly? What happens if the program builds up a couple of keydown messages in its queue, and the first few you let through, but the last you suppress using the method above, wouldn't the RemovePendingMessages function remove all characters from the queue, rather than just the last one?


In Windows API terms:

Consider a typical message loop that calls TranslateMessage to get WM_CHAR messages from keyboard input. On a WM_KEYDOWN check if the key is recognised as a command, in which case it should not generate a character. How could one go about removing the WM_CHAR message that was potentially posted to the message queue to effectively suppress the keypress? An existing solution in .NET seems to use a looped PeekMessage(.., WM_CHAR, WM_CHAR, PM_REMOVE) to remove all characters from the queue, but wouldn't that remove too many characters if multiple keydown-messages are in the queue?

Upvotes: 4

Views: 441

Answers (1)

Hans Passant
Hans Passant

Reputation: 941307

Your intuition is correct, it does in fact misbehave as you describe. Some code to play with to see this going wrong:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
    }
    int keycnt = 0;

    protected override void OnKeyDown(KeyEventArgs e) {
        keycnt++;
        if (keycnt == 3) e.SuppressKeyPress = true;
        base.OnKeyDown(e);
    }
    protected override void OnKeyPress(KeyPressEventArgs e) {
        Debug.Print("Press {0}", e.KeyChar);
        base.OnKeyPress(e);
    }
    protected override void WndProc(ref Message m) {
        if (m.Msg >= 0x100 && m.Msg <= 0x109) Debug.WriteLine(m);
        base.WndProc(ref m);
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr PostMessage(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp);

    protected override void OnMouseClick(MouseEventArgs e) {
        for (int ix = 0; ix < 5; ++ix) {
            PostMessage(this.Handle, 0x100, (IntPtr)Keys.A, IntPtr.Zero);
        }
    }
}

Click the form to trigger the test. Note how OnKeyDown only suppresses the 3rd keystroke. But only the first 2 make it through, the rest are swallowed.

I've never actually seen anybody complain about this.

Upvotes: 3

Related Questions