如何在C#应用程序和DLL中使用消息

在C#是一种类似 Java 的完全面向对象的高级编程语言,其处理过程采用事件驱动方式,但在实际的使用过程中,有时候通过调用系统原有的消息处理起来会更简单,特别是在处理与DLL文件的交互时,实践证明的确是非常方便的。

在C#中使用自定义消息

在C#中使用自定义消息非常简单,只需要下面几个简单的步骤就可以了:

1、定义消息

定义消息的方法与在VC中定义消息有一点点不同,比如在VC中申明一个自定义消息是这样的:

1. #define WM_TEST WM_USER + 101

而在C#中消息需要定义成 Windows 系统中的原始的16进制数字,比如自定义消息

1. publicconst int USER = 0x0400;

那么我们在VC中申明的自定义消息,在C#中就可以做对应的声明:

1. publicconst int WM_TEST = USER+101;

2、发送消息

消息发送是通过 Windows 提供的 API 函数 SendMessage 来实现的,它的原型定义:

1. [DllImport("User32.dll",EntryPoint="SendMessage")]
2. privatestatic externint SendMessage(
3.        IntPtr hWnd,     // handle to destination window
4.        uint Msg,        // message
5.        uint wParam,     // first message parameter
6.        uint lParam      // second message parameter
7. );

3、消息接收

消息发出之后,在Form中如何接收呢?我们可以重载DefWinproc函数来接收消息。

01. protectedoverride void DefWndProc ( ref System.Windows.Forms.Message m )
02. {
03.             switch(m.Msg)
04.             
05.                 caseMessage.WM_TEST: //处理消息
06.                 break;
07.                 default:
08.                 base.DefWndProc(ref m);//调用基类函数处理非自定义消息。
09.         break;
10.             }
11. }

在C#中使用系统消息

我们以WM_PAINT消息的处理为例,在C#中处理消息与MFC的消息处理是类似的,但更为简单。MFC中需要使用DECLARE_MESSAGE_MAP来定义消息映射,在C#就不需要了。比如WM_PAINT消息,我们只要重载父类中的OnPaint虚拟方法即可,方法如下:

在菜单View->Other Windows->Object Browser打开对象浏览窗口(或用CTRL+ALT+J打开),在我们的工程名下找到Form并选中,这时在右边的窗口列出所有Form类的成员函数。

我们选中OnPaint(System.WinForms.PaintEventArgs)此时在下面会显示完整的OnPaint函数protected void OnPaint ( System.WinForms.PaintEventArgs e )我们将这一行字符串Copy下来。打开Form1.cs进行代码编辑,我们把刚才拷贝下来的函数定义复制到Form1类里面,并加上override关键字,此时我们便可以在里面添加我们的消息处理代码了,请参考如下代码段:

01. protectedoverride void OnPaint (System.Windows.Forms.PaintEventArgs e  ) 
02. {
03.      Font font =new Font("黑体",28);///定义字体:黑体,大小:28
04.      SolidBrush bluepen =new SolidBrush(Color.Blue);///创建蓝色画笔
05.      SolidBrush blackpen =new SolidBrush(Color.FromARGB(0xa0,0xa0,0xb0));///创建黑色画笔
06.      e.Graphics.DrawString("VC知识库",font,blackpen,65,25);///写字符串
07.   
08.      ///偏移4个象素用不同的颜色再写一次,达到立体效果
09.      e.Graphics.DrawString("VC知识库",font,bluepen,61,21);}

示例应用

1、定义消息

我们在工程中添加一个Message类用来定义消息。然后添加了三个成员变量,其中USER为自定义消息的初始值,相当与MFC中的WM_USER。WM_TEST为自定义的用来响应应用程序的消息,WM_MSG为自定义的用来响应DLL传递过来的消息。如何在DLL定义消息请参考文章:VC.Net从DLL传递消息到DLL。

1. publicclass Message
2. {
3.      publicconst int USER = 0x0400;
4.      //as mfc Define WM_TEST WM_USER + 101
5.      publicconst int WM_TEST = USER+101;
6.      publicconst int WM_MSG = USER+102;
7. }

2、声明引用函数

在使用消息的地方,申明引用的函数,我们这里在MsgForm.cs文件中申明:

01. //申明发送消息函数
02. [DllImport("User32.dll",EntryPoint="SendMessage")]
03. privatestatic externint SendMessage(
04.     IntPtr hWnd,     // handle to destination window
05.     uint Msg,        // message
06.     uint wParam,     // first message parameter
07.     uint lParam      // second message parameter
08.     );
09.    
10. //申明DLL中启动消息函数
11. [DllImport("MessageDLL.dll",EntryPoint="StartSendMessage")]
12. privateextern staticvoid StartSendMessage(IntPtr hWnd);

3、处理系统消息

01. protectedoverride void OnPaint (  System.Windows.Forms.PaintEventArgs e ) 
02.          {
03. ///定义字体:黑体,大小:28
04.               Font font =new Font("黑体",28);
05. ///创建蓝色画笔
06.               SolidBrush bluepen =new SolidBrush(Color.Blue);
07. ///创建黑色画笔             
08. SolidBrush blackpen =new SolidBrush(Color.FromArgb(0xa0,0xa0,0xb0));
09. ///写字符串
10. e.Graphics.DrawString("VC知识库",font,blackpen,65,25); 
11. ///偏移4个象素用不同的颜色再写一次,达到立体效果
12.              e.Graphics.DrawString("VC知识库",font,bluepen,61,21);        
13. }

4、 触发自定义消息

01. //测试应用程序消息
02. privatevoid TestAppbutton_Click(object sender, System.EventArgs e)
03. {
04.     SendMessage(this.Handle,Message.WM_TEST,100,200);
05. }
06.   
07. //测试DLL消息
08. privatevoid TestDLLbutton_Click(object sender, System.EventArgs e)
09. {
10.     StartSendMessage(this.Handle);
11. }

5、响应和处理自定义消息

01. protectedoverride void DefWndProc ( ref System.Windows.Forms.Message m )
02. {
03.     string message;
04.     switch(m.Msg)
05.     {
06.         caseMessage.WM_TEST://处理消息
07.                 message = string.Format("收到从应用程序发出的消息!参数为:{0},{1}",m.WParam,m.LParam);
08.             MessageBox.Show(message);///显示一个消息框
09.             break;
10.         caseMessage.WM_MSG:
11.             message = string.Format("收到从DLL发出的消息!参数为:{0},{1}",m.WParam,m.LParam);
12.             MessageBox.Show(message);///显示一个消息框
13.             break;
14.         default:
15.             base.DefWndProc(ref m);//调用基类函数处理非自定义消息。
16.   
17.             break;
18.      }
19. }

程序运行结果:

当我们点击测试DLL消息时,弹出消息框显示收到消息的参数,窗口也会调用WM_PAIN函数对窗口进行重新绘制。

说明:本文是根据VC知识库《在线杂志》中王骏的一篇文章:“C#开发WINDOWS应用程序时消息的处理”整理而成。

 

===========================================================================================================================

 

谈到Winform的消息处理,多数时候是通过事件处理程序进行的,但当没有对应的事件时通常的做法是声明DefWndProc或者WndProc或者IMessageFilter,经常在网上看见有文章将三者并列,那么它们有什么区别呢?

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

   1:  protected override void DefWndProc(ref Message m)
   2:  {
   3:  ....
   4:      base.DefWndProc(m);
   5:  }
   6:   
   7:  protected override void WndProc(ref Message m);
   8:  {
   9:  .....
  10:      base.WndProc(m);
  11:  }

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

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

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

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

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

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

Message的定义如下:

   1:  [StructLayout(LayoutKind.Sequential), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
   2:  public struct Message
   3:  {
   4:      private IntPtr hWnd;
   5:      private int msg;
   6:      private IntPtr wparam;
   7:      private IntPtr lparam;
   8:      private IntPtr result;
   9:      public IntPtr HWnd { get; set; }
  10:      public int Msg { get; set; }
  11:      public IntPtr WParam { get; set; }
  12:      public IntPtr LParam { get; set; }
  13:      public IntPtr Result { get; set; }
  14:      public object GetLParam(Type cls);
  15:      public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);
  16:      public override bool Equals(object o);
  17:      public static bool operator !=(Message a, Message b);
  18:      public static bool operator ==(Message a, Message b);
  19:      public override int GetHashCode();
  20:      public override string ToString();
  21:  }
  22:   

其中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的输出窗口监视到的调试输出:
tmp180
P.S.脑子里一直想着好像还有种定义消息处理过程的方式,而且是可以直接指定处理哪个消息,好像使用的关键字是“message”。。。
在MSDN上搜索N久后反应过来,哦,好象是Delphi中的方法;-)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值