Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of all Process* methods and all message's filters?

I notice that WinForms have many methods that process commands or keys (Process*()) and to (pre)filter system's messages, but their respective purposes remain unclear to me.

The official documentation is somewhat obscure and I haven't found any clear and complete response.

I speak about the following methods:

  • PreFilterMessage(ref Message m)
  • ProcessCmdKey(ref Message msg, Keys keyData)
  • WndProc(ref Message m)
  • ProcessDialogKey(Keys keyData)
  • PreProcessMessage(ref Message msg)
  • ProcessKeyMessage(ref Message m)
  • ProcessKeyPreview(ref Message m)

Some are for intercept keys (like ProcessCmdKey or ProcessDialogKey) and some for intercept messages (each others). But why as many methods? What are their purposes and their use cases ?

I suppose that the execution order for each method is different.

Here is what I know (or I think know):

  • PreFilterMessage: first to intercept messages. You can stop the distribution of the message for all following methods here!
  • ProcessCmdKey: intercepts ALL keys, even combinaisons, special and command keys. Good to detect key shortcuts on the entire form (like Ctrl + D). You can stop the distribution of the keys here.
  • WndProc: second to intercept messages after filtering? I use it only to detect if user click on the upper right "X", but I suppose that is possible on another method!
  • ProcessDialogKey: intercepts ONE key only, probably after the ProcessCmdKey and before all key's events of controls.
  • PreProcessMessage: before WndProc and after PreFilterMessage? I don't know exactly why it is used.
  • ProcessKeyMessage: intercepts key message. It seems that it is rarely used.
  • ProcessKeyPreview: intercepts key before preview event? Rarely used too.

In depth, I think that it is the correct execution order:

  1. PreFilter
  2. Filter
  3. PreProcess
  4. Process
  5. Events

Why so many steps?

Any informations or concrete use cases will be appreciated!

like image 842
Kevin Vuilleumier Avatar asked Oct 02 '14 10:10

Kevin Vuilleumier


1 Answers

A native Windows GUI application normally has one message loop, underlying winapi call is GetMessage(). But has many windows that get messages, underlying winapi call is DispatchMessage(). In your .NET app you have one call to Application.Run() but many WndProc() methods, one for each control. Most of them are hidden in .NET Framework code, exposed only if you override it.

There is in general a need to hook into the message loop, intercepting a message before it is dispatched to the control and arrives in WndProc(). The most obvious reason is keyboard shortcuts, you want to act on them regardless which control has the focus. It would of course be very painful if you had to use KeyDown on every control to detect the shortcut. And less obvious reasons, ActiveX controls are notable for having to negotiate with their host for example.

Winforms gives lots of extension points to intercept messages. Too many, really, but a somewhat inevitable side-effect of not wanting to predict in which cases they might be useful. In order:

  • IMessageFilter.PreFilterMessage() lets you peek at the message right after it was retrieved by GetMessage(). Keep in mind that you can only see messages that were posted to the message queue, underlying call is PostMessage(), you cannot see sent messages. Which limits its usability to user input, keyboard and mouse messages. Rare to tinker with this, but you could use it to make the mouse act differently and to detect that the user is interacting with your program. Programmers that want to automatically log-out a user after a period of inactivity use it for example.
  • OnPreviewKeyDown() and PreviewKeyDown event. Specific to the control that has the focus. A way for a control to intercept a keystroke before it is tested for a shortcut. You can detect the cursor keys that way for example, before they are used to move the focus. An alternative to overriding IsInputKey().
  • PreProcessMessage(). Very similar to PreFilterMessage but specific to the control that has the focus. Important to ActiveX controls, I personally never had a use for it.
  • ProcessCmdKey(). This is the workhorse method to implement your own shortcut keystrokes.
  • IsInputKey(), allows a control to vote whether a key that's normally used for navigation should be sent to the control instead. You override this method when you have a use for the cursor keys for example.
  • ProcessDialogKey(). Just like ProcessCmdKey() but filtered for the keystrokes that should be treated like input keys. The default implementation makes the keyboard message bubble to the parent control, giving you a choice where you override ProcessCmdKey(). I can't think of a good reason why you'd want to override it, and never once did, other than to stop the bubbling.
  • IsInputChar(), very similar to IsInputKey but for KeyPress events. Never used it myself but a way to filter input early.
  • ProcessDialogChar(), could be used to give typing keys shortcut keystroke behavior. That's unusual.
  • WndProc(), the workhorse method for processing messages. You override it to allow a control to respond to messages that are not otherwise covered by existing events. Or to customize the behavior of existing native controls.
  • ProcessKeyEventArgs(), the general method that generates the keyboard events (OnKeyDown, OnKeyUp, OnKeyPress), called by Control.WndProc(). I can't think of a reason to override it, notable for implementing the quirky Form.KeyPreview property, a VB6 compat property, possibly the reason that it got exposed.

A twisty maze indeed.

Trying to keep it sane, always override ProcessCmdKey() to implement shortcut keys. Override IsInputKey() to let your control see the navigation keys. And only override WndProc() to customize existing controls.

like image 116
Hans Passant Avatar answered Nov 16 '22 21:11

Hans Passant