新浪博客

C#的三大难点之三:消息与事件

2016-02-13 20:47阅读:

相关文章:

C#的三大难点之前传:什么时候应该使用C#?
C#的三大难点之一:byte与char,string与StringBuilder
C#的三大难点之二:托管与非托管
C#的三大难点之三:消息与事件

一、进程间的同步与通信

注:本文中不区分进程与线程在同步与通信上的具体区别。
进程/线程间的同步,是指两个或多个进程/线程访问公共资源的一种机制 ,具体的方式包括:临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event)四种。
而进程/线程的通信,是指在不同的进程/线程间传播信息,包括管道、系统IPC(包括消息队列、信号量、共享存储)、SOCKET等方式。
具体内容请参见《操作系统》。

二、Windows中的的消息与事件

在Windows中,你的每一个交互动作,如键盘输入,鼠标单击按钮,改变窗体大小等,都会产生一个相对应的事件。
在MFC中,你可以通过右键快捷菜单中的“添加事件处理程序”对某个事件产生后程序的具体行为进行定义,如下图所示:
enter description here

例如,我们可以添加一个单击OK按钮后执行的函数:
void CtestDlg::OnBnClickedOk() { // TODO: 在此添加控件通知处理程序代码 CDialogEx::OnOK(); }
但是,这样的函数是在哪里被调用的呢?这就涉及到Windows的消息机制了。实际上,程序使用一个message map来进行消息的维护和传递,消息在这个巨大的网中流动,最后到达可以响应的地点。具体可以查看《深入浅出MFC》中的第九章:消息映射与命令绕行,我由于看了很久了,具体细节也想不起来了,需要的话以后再补充吧。

C#中的线程通信

在C#中,由于做了高度的封装,我们是看不到消息具体的流动方式的。
处理事件时,直接填写如下函数即可。
private void button1_Click(object sender, EventArgs e) { }
其中,button1_Click这个函数被添加到类似消息队列的一个EventHandler中
this.button1.Click += new System.EventHandler(this.button1_Click);
EventHandler本质上是一个委托(delegate),定义为:
public delegate void EventHandler(object sender, EventArgs e)
那么,在C#中如何自定义一个事件呢?同样,使用委托的方式即可。
具体的例子可以在网上找下,本文不再赘述。接下来讲述另一种通信的方式。
在前面我们讲述过,Windows是通过消息的机制实现进程间信息的传递的。在C#中,我们同样可以利用这个机制实现通信。具体方式如下:
首先,定义消息,并从win32 API中导入发送消息和寻找窗体的函数。
public const int USER = 0x0400;//用户自定义消息的开始数值 [DllImport('user32.dll')] public static extern void PostMessage(IntPtr hWnd, int msg, int wParam, int lParam); [DllImport('user32.dll')] static extern IntPtr FindWindow(string strClass, string strWindow);
然后,在需要发送消息的地方加入:
IntPtr ptrWnd = FindWindow(null, 'test'); PostMessage(ptrWnd, USER + 1, 168, 51898);
最后,在接收的地方重写DefWndProc()函数。
protected override void DefWndProc(ref System.Windows.Forms.Message m) { switch (m.Msg) { case USER + 1: //输入执行的内容 this.Invalidate(true);//调用重绘命令 break; default: base.DefWndProc(ref m);//一定要调用基类函数,以便系统处理其它消息。 break; } }
当然,这种写法比较奇怪,特别是168,51898这种数字,看起来真是让人不明所以。但这的确是一种窗体间通信的方法,而且和MFC的思路比较类似。

后记

《C#的三大难点》系列文章,到这里就结束了。三篇文章不可能涵盖C#开发中遇到的所有难点,我也只是对我过去三年的C#开发经历中,涉及到比较常见的三个部分:数据格式转换、dll库导入、窗体间通信三个问题进行了探讨,以后如果有机会的话,会对文中不清楚的地方继续进行完善和扩展。

我的更多文章

下载客户端阅读体验更佳

APP专享