WinForm中DefWndProc、WndProc与IMessageFilter的区别

所属分类: 软件编程 / C#教程 阅读数: 107
收藏 0 赞 0 分享

一般来说,Winform的消息处理机制多数时候是通过事件处理程序进行的,但当没有对应的事件时通常的做法是声明DefWndProc或者WndProc或者IMessageFilter,经常在网上看见有文章将三者并列,那么它们有什么区别呢?本文对此做一简单分析如下:

DefWndProc和WndProc都是继承自Control类中的虚方法,其原型如下:

protected override void DefWndProc(ref Message m)
{
 ....
   base.DefWndProc(m);
}
  
protected override void WndProc(ref Message m);
{
 .....
   base.WndProc(m);
}

所有的有用户界面的控件都继承自Control,这种方式需要创建对应控件的派生类,不能统一对各个窗口的消息进行拦截处理,因为从根本上说这两者都是Windows的窗口过程,只有收到针对本窗口自身的消息。

通过复习Windows的消息处理机制,对这三者的关系可以有更好的理解。应用程序的消息来自于系统消息队列,被应用程序的主程序中的消息循环所处理。这个消息循环从应用程序的消息队列中取出消息,进行预处理,然后派发到消息对应的窗口过程,窗口过程在被调用后根据消息的类型进行相应的处理,有些可以由Windows默认处理的消息就调用Windows的DefWindowProc

这里的WndProc就是对应控件窗口的窗口过程,而DefWndProc会被WndProc调用,处理那些WndProc中未处理的消息(包括WndProc未吞掉的),因此DefWndProc收到的消息会比WndProc少。

IMessageFilter的调用发生在应用程序的消息循环中,是消息预处理的一部分,所以它收到的消息是更全的(除了直接发送到窗口过程不进入消息队列的那些消息)。使用方式如下:

public class MessageFilter : IMessageFilter
{
 public bool PreFilterMessage(ref Message msg)
 {
   //识别消息并处理
  //return true;//吞掉消息,不派发
   return false;//进入下一步派发到对应窗口过程
 }
}
  
//在应用程序消息循环中加入消息过滤器
MessageFilter f = new MessageFilter(this.lbMsg);
Application.AddMessageFilter(f);

三者都有一个共同的参数类型Message,它封装了Windows消息。同时还包括一个很方便的ToString方法,可以将Message对象转换成包括消息名称(WM_XXX)在内的字符串,通过Reflector可以看到实现是通过一个内部类MessageDecoder,使用一个很长的switch语句将消息ID转换成消息名称。

Message的定义如下:

[StructLayout(LayoutKind.Sequential), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
public struct Message
{
   private IntPtr hWnd;
   private int msg;
   private IntPtr wparam;
   private IntPtr lparam;
   private IntPtr result;
   public IntPtr HWnd { get; set; }
   public int Msg { get; set; }
   public IntPtr WParam { get; set; }
   public IntPtr LParam { get; set; }
   public IntPtr Result { get; set; }
   public object GetLParam(Type cls);
   public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
   public override bool Equals(object o);
   public static bool operator !=(Message a, Message b);
   public static bool operator ==(Message a, Message b);
   public override int GetHashCode();
   public override string ToString();
}
  

其中hWnd是消息对应的窗口句柄,根据上面的分析可以知道在窗口过程(DefWndProc,WndProc)中收到的窗口句柄都是该窗口的句柄,而在PreFilterMessage中收到的消息的窗口句柄则根据触发消息的窗口不同而不同。

在PreFilterMessage中收到消息时,可以使用Control.FromHandle得到窗口对应的控件对象,原型如下:

//Declaring Type: System.Windows.Forms.Control //Assembly: System.Windows.Forms, Version=2.0.0.0 public static Control FromHandle(IntPtr handle);通过这种方式可以监测各消息的信息来自哪个控件。
public bool PreFilterMessage(ref Message msg)
{
   Control c = Control.FromHandle(msg.HWnd);
   if (c == null)
      System.Diagnostics.Debug.WriteLine("Filter:NULL" +"-" + msg.ToString());
   else
      System.Diagnostics.Debug.WriteLine("Filter:" +c.Name+"-"+ msg.ToString());
   return false;
}

从Visual Studio的输出窗口监视到的调试输出如下图所示:

希望本文所述分析对大家深入理解WinForm的消息处理机制有所帮助。

更多精彩内容其他人还在看

c#开发word批量转pdf源码分享

已经安装有Office环境,借助一些简单的代码即可实现批量Word转PDF,看下面的实例源码吧
收藏 0 赞 0 分享

c# xml API操作的小例子

这篇文章主要介绍了c# xml API操作的小例子,有需要的朋友可以参考一下
收藏 0 赞 0 分享

c#唯一值渲染实例代码

这篇文章主要介绍了c#唯一值渲染实例代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

淘宝IP地址库采集器c#代码

这篇文章主要介绍了淘宝IP地址库采集器c#代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

C#在后台运行操作(BackgroundWorker用法)示例分享

BackgroundWorker类允许在单独的专用线程上运行操作。如果需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用BackgroundWorker类方便地解决问题,下面看示例
收藏 0 赞 0 分享

c#文本加密程序代码示例

这是一个加密软件,但只限于文本加密,加了窗口控件的滑动效果,详细看下面的代码
收藏 0 赞 0 分享

c#生成站点地图(SiteMapPath)文件示例程序

这篇文章主要介绍了c#生成站点地图(SiteMapPath)文件的示例,大家参考使用
收藏 0 赞 0 分享

C# 键盘Enter键取代Tab键实现代码

这篇文章主要介绍了C# 键盘Enter键取代Tab键实现代码,有需要的朋友可以参考一下
收藏 0 赞 0 分享

C# WinForm导出Excel方法介绍

在.NET应用中,导出Excel是很常见的需求,导出Excel报表大致有以下三种方式:Office PIA,文件流和NPOI开源库,本文只介绍前两种方式
收藏 0 赞 0 分享

C#串口通信程序实例详解

在.NET平台下创建C#串口通信程序,.NET 2.0提供了串口通信的功能,其命名空间是System.IO.Ports,创建C#串口通信程序的具体实现是如何的呢?让我们开始吧
收藏 0 赞 0 分享
查看更多