所有Process *方法和所有消息过滤器的目的是什么?

时间:2014-10-02 10:04:45

标签: c# .net windows winforms message-queue

我注意到WinForms有很多方法可以处理命令或密钥(Process*())和(预)过滤系统的消息,但我们不清楚它们各自的用途。

官方文档有点模糊,我没有找到任何明确和完整的回复。

我谈到以下方法:

  • 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)

一些用于拦截键(如ProcessCmdKeyProcessDialogKey),一些用于拦截消息(彼此)。但为什么有很多方法呢?他们的目的和用例是什么?

我认为每种方法的执行顺序都不同。

这是我所知道的(或者我认为知道):

  • PreFilterMessage:首先拦截消息。您可以在此停止为以下所有方法分发消息!
  • ProcessCmdKey:拦截所有键,甚至组合键,特殊键和命令键。很好地检测整个表单上的关键快捷键(如Ctrl + D)。您可以在这里停止分发密钥。
  • WndProc:第二个在过滤后拦截消息?我只用它来检测用户是否点击右上方的“X”,但我想这可能是另一种方法!
  • ProcessDialogKey:仅截取一个密钥,可能在ProcessCmdKey之后和所有密钥的控件事件之前。
  • PreProcessMessageWndProc之前和PreFilterMessage之后?我不知道为什么要使用它。
  • ProcessKeyMessage:拦截关键信息。它似乎很少使用。
  • ProcessKeyPreview:在预览事件之前截取密钥?很少使用。

深入,我认为这是正确的执行顺序:

  1. 预过滤
  2. 过滤
  3. 预处理
  4. 过程
  5. 活动
  6. 为什么这么多步骤?

    任何信息或具体用例都将受到赞赏!

1 个答案:

答案 0 :(得分:10)

本机Windows GUI应用程序通常具有一个消息循环,基础winapi调用是GetMessage()。但是许多窗口获取消息,底层的winapi调用是DispatchMessage()。在您的.NET应用程序中,您只能调用Application.Run(),但有许多WndProc()方法,每个控件一个。其中大多数都隐藏在.NET Framework代码中,只有在覆盖它时才会公开。

通常需要挂钩到消息循环,在将消息分派给控件并到达WndProc()之前拦截消息。最明显的原因是键盘快捷键,无论哪个控件都有焦点,您都希望对它们采取行动。如果必须在每个控件上使用KeyDown来检测快捷方式,那当然会非常痛苦。不太明显的原因,例如,ActiveX控件因必须与其主机进行协商而着名。

Winforms提供很多的扩展点来拦截消息。实际上太多了,但有些不可避免的副作用是不想预测哪些情况下它们可能有用。按顺序:

  • IMessageFilter.PreFilterMessage()可让您在GetMessage()检索消息后立即查看消息。请记住,您只能看到发布到消息队列的消息,底层调用是PostMessage(),您无法看到已发送的消息。这限制了它对用户输入,键盘和鼠标消息的可用性。很难对此进行修改,但您可以使用它来使鼠标采取不同的行为并检测用户是否正在与您的程序进行交互。想要在一段时间不活动后自动注销用户的程序员可以使用它,例如。
  • OnPreviewKeyDown()和PreviewKeyDown事件。特定于具有焦点的控件。控件在测试快捷方式之前拦截击键的一种方法。例如,在用于移动焦点之前,您可以检测光标键。替代重写IsInputKey()。
  • PreProcessMessage()。与PreFilterMessage非常相似,但特定于具有焦点的控件。对ActiveX控件很重要,我个人从来没有使用它。
  • ProcessCmdKey()。这是实现自己的快捷键击的主力方法。
  • IsInputKey()允许控件投票决定是否应将通常用于导航的密钥发送给控件。例如,当您使用光标键时,可以覆盖此方法。
  • ProcessDialogKey()。就像ProcessCmdKey()一样,但是对于应该被视为输入键的键击进行过滤。默认实现使键盘消息冒泡到父控件,使您可以选择覆盖ProcessCmdKey()的位置。我想不出一个很好的理由,为什么你要覆盖它,从来没有过,除了停止冒泡。
  • IsInputChar(),与IsInputKey非常相似,但是对于KeyPress事件。从来没有使用它,而是一种过早过滤输入的方法。
  • ProcessDialogChar(),可用于给键入键快捷键击行为。这很不寻常。
  • WndProc(),用于处理消息的主力方法。您可以覆盖它以允许控件响应现有事件未包含的消息。或者自定义现有本机控件的行为。
  • ProcessKeyEventArgs(),生成键盘事件(OnKeyDown,OnKeyUp,OnKeyPress)的一般方法,由Control.WndProc()调用。我无法想到覆盖它的理由,值得注意的是实现古怪的Form.KeyPreview属性,VB6 compat属性,可能是它暴露的原因。
确实是一个曲折的迷宫。

尝试保持理智,始终覆盖ProcessCmdKey()以实现快捷键。覆盖IsInputKey()以让控件查看导航键。并且只覆盖WndProc()来自定义现有控件。