MFC基于消息,使用事件驱动(Message Based,Event Driven)机制

        MFC程序基于消息,而使用事件驱动(Message Based,Event Driven)。也就是说MFC就是一个死循环,里面有很多的条件,每个条件对应一个方法。这些条件就是有消息类定义,当用户触发事件时,将发送消息到响应的窗口。当程序收到消息时进行解析,判断如果符合条件,将运行当前事件的处理方法。

MSG msg;
while(GetMessage(&msg,NULL,NULL,NULL))
 {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
 }
         每一个程序都存在上述的循环,而MSG是一个结构,是Windows内设的一种数据格式,可以在WinUser.h中找到,代码如下:
/*
  * Message structure
  */
 typedef struct tagMSG {
     HWND        hwnd;
     UINT        message;
     WPARAM      wParam;
     LPARAM      lParam;
     DWORD       time;
     POINT       pt;
 #ifdef _MAC
     DWORD       lPrivate;
#endif
 } MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
        接受并处理消息的主角是窗口,每一个窗口都必须要有能够处理消息的方法,称为“窗口函数”(Window Procedure/Function)。当窗口获得消息后,必须判断消息的类别,将消息转换(TranslateMessage(&msg)转换键盘消息),然后将消息传递到(DispatchMessage(&msg))窗口函数去处理。
        窗口函数是一个回调函数(用户定义的函数用于Windows操作系统调用的函数),它的形式如下所示。
LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
        其中wParam和lParam的意义因消息的不同而不同,但可以知道的是wParam的位数是随着操作系统的位数而定的,在32位的操作系统中为32位,当然64位的就为64位。知道了这个函数后,如果要将每一个消息对应到响应的处理函数中就需要如switch/case结构来判断,为了让程序更好的模块化,需要了解Message Map(消息映射)的原理。

一、消息映射机制

        消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。消息可以由系统或者应用程序产生。系统在发生输入事件时产生消息。举个例子, 当用户敲键, 移动鼠标或者单击控件。系统也产生消息以响应由应用程序带来的变化, 比如应用程序改变系统字体改变窗体大小。应用程序可以产生消息使窗体执行任务,或者与其他应用程序中的窗口通讯。 


  在菜单中选择View-->Class Wizard,也可以用单击鼠标右键,选择Class Wizard,同样可以激活Class Wizard。选择Message Map标签,从Class name组合框中选取我们想要添加消息的类。在Object IDs列表框中,选取类的名称。此时, Messages列表框显示该类的大多数(若不是全部的话)可重载成员函数和窗口消息。类重载显示在列表的上部,以实际虚构成员函数的大小写字母来表示。其他为窗口消息,以大写字母出现,描述了实际窗口所能响应的消息ID。选中我们想添加的消息,单击Add Function按钮,Class Wizard自动将该消息添加进来。


          1、一个MFC消息响应函数在程序中有三处相关信息:函数原型,函数实现,关联消息和消息响应函数的宏。

函数原型——头文件CDrawView——两个AFX_MSG注释宏之间——消息响应函数原型的声明 —— afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

函数实现——源文件CDrawView()——OnLButtonDown(UINT nFlags, CPoint point)

关联消息和消息响应函数的宏——源文件CDrawView()——BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP()之间


           2、、 MFC消息映射机制的具体实现方法是:在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即:消息映射表。在消息映射表中,消息与对应消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。

           3、 MFC消息映射机制的实际实现过程:MFC在后台维护了一个窗口句柄与对应的C++对象指针的对照表。

具体请参考:http://blog.csdn.net/ocean2006/article/details/5498265

           4、MFC建立消息的步骤如下:消息的声明、消息的映射、消息的实现

声明:

//{{AFX_MSG
afx_msg void OnTimer(UINT nIDEvent);
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

映射:

BEGIN_MESSAGE_MAP(CTestDialog, CDialog)
//{{AFX_MSG_MAP(CTestDialog)
ON_WM_TIMER()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
实现:

void CTestDialog::OnPaint() 
{
}

void CTestDialog::OnTimer(UINT nIDEvent) 
{	
    CDialog::OnTimer(nIDEvent);
}


二、事件驱动机制

         EVENT有两种状态:发信号,不发信号。 SetEvent/ResetEvent分别将EVENT置为这两种状态分别是发信号与不发信号。WaitForSingleObject()等待,直到参数所指定的OBJECT成为发信号状态时才返回,OBJECT可以是EVENT,也可以是其它内核对象。当你创建一个线程时,其实那个线程是一个循环,不像上面那样只运行一次的。这样就带来了一个问题,在那个死循环里要找到合适的条件退出那个死循环,那么是怎么样实现它的呢?在Windows里往往是采用事件的方式,当然还可以采用其它的方式。在这里先介绍采用事件的方式来通知从线程运行函数退出来,它的实现原理是这样,在那个死循环里不断地使用 WaitForSingleObject函数来检查事件是否满足,如果满足就退出线程,不满足就继续运行。当在线程里运行阻塞的函数时,就需要在退出线程时,先要把阻塞状态变成非阻塞状态,比如使用一个线程去接收网络数据,同时使用阻塞的SOCKET时,那么要先关闭SOCKET,再发送事件信号,才可以退出线程的。CreateEvent、SetEvent、WaitForSingleObj

       事件驱动过程需要使用以下三个函数:

      1、CreateEvent

            函功能描述:创建或打开一个命名的或无名的事件对象.

HANDLE CreateEvent(

  
  
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
BOOL bManualReset, // 复位方法
BOOL bInitialState, // 初始状态
LPCTSTR lpName // 对象名称
);

        2. WaitForSingleObject 

        功能描述:用来检测 hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。

DWORD WaitForSingleObject(


  HANDLE hHandle,


  DWORD dwMilliseconds


);
        参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT。

        hHandle可以是下列对象的句柄:
Change notification 、Console input 、Event 、Job 、Memory resource notification、 Mutex 、Process、 Semaphore、 Thread 、Waitable timer等等。

         3、SetEvent

         功能描述:设置事件的状态为有标记,释放任意等待线程。如果事件是手工的,此事件将保持有标记直到调用ResetEvent。这种情况下将释放多个线程,如果事件是自动的,此事件将保持有标记,直到一个线程被释放,系统将设置事件的状态为无标记。如果没有线程在等待,则此事件将保持有标记,直到一个线程被释放。

         4、ResetEvent

         功能描述:这个函数把指定的事件对象设置为无信号状态。

BOOL ResetEvent(

  
  
HANDLE hEvent );
        参数说明: 
  hEvent
  [in] 指向事件对象的句柄.由 CreateEvent or OpenEvent 函数返回。 这个句柄需要拥有EVENT_MODIFY_STATE 访问权限.
  函数成功,返回非0值,否则返回0值,可以调用GetLastError得到错误的详细信息。
  注意:
  一个事件对象一直都保持在无信号状态,直到显式调用 SetEvent or PulseEvent 函数把它设置到有信号状态。 这些无信号的事件对象会阻塞任何在内部调用wait函数的线程。 这个函数用于手动重置的事件对象。手动重置的对象在线程释放后必须手动置为无信号状态。 自动重置的事件对象在一个等待它成功的线程释放后会自动变为无信号状态。

       5、CloseHandle

       功能描述:使用CloseHandle函数关闭句柄。当进程停止时,系统将自动关闭句柄。当最后一个句柄被关闭后,事件对象将被销毁。

       6、应用:主要应用是锁定功能,实现PV操作(PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思,解决进程同步与互斥问题)

       在调用的过程中,所有线程都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态被置为有信号状态时,单对象等待函数将返回。 

       对于多对象等待函数,可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时,等待线程将被释放去继续运行。 
    初始状态在bInitialState参数中进行设置。使用SetEvent函数将事件对象的状态置为有信号状态。使用ResetEvent函数将事件对象的状态置为无信号状态。 
    当一个手动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。 
    当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。 
    当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态。 
    多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。下面的对象共享机制是可行的: 
    在CreateEvent函数中,lpEventAttributes参数指定句柄可被继承时,通过CreateProcess函数创建的子进程继承的事件对象句柄。 
    一个进程可以在DuplicateHandle函数中指定事件对象句柄,从而获得一个复制的句柄,此句柄可以被其它进程使用。 
    一个进程可以在OpenEvent或CreateEvent函数中指定一个名字,从而获得一个有名的事件对象句柄。 
    使用CloseHandle函数关闭句柄。当进程停止时,系统将自动关闭句柄。当最后一个句柄被关闭后,事件对象将被销毁。

         7、以下是简单的事件驱动实例:

#include <WINDOWS.H>//需要访问windows API 函数,因此许加入此头文件
#include <IOSTREAM.H>

DWORD WINAPI Fun1Pro(LPVOID lpParameter);
DWORD WINAPI Fun2Pro(LPVOID lpParameter);

int nTickets = 100;
HANDLE g_hEvent;

void main()//主线程
{
	HANDLE hThread1;
	HANDLE hThread2;

//创建人工重置事件对象
// 	g_hEvent = CreateEvent(NULL,FALSE,TRUE,NULL);
// 	SetEvent(g_hEvent);
	g_hEvent = CreateEvent(NULL,FALSE,TRUE,"Tickets");//创建命名事件对象
	if (g_hEvent)
	{
		if (ERROR_ALREADY_EXISTS == GetLastError())
		{
			cout<<"Only one thread can run..."<<endl;
			return;
		}
	}

	hThread1 = CreateThread(NULL,0,Fun1Pro,NULL,0,NULL);
	hThread2 = CreateThread(NULL,0,Fun2Pro,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	Sleep(4000);
	CloseHandle(g_hEvent);
}

DWORD WINAPI Fun1Pro(LPVOID lpParameter)
{
	while (TRUE)
	{
		WaitForSingleObject(g_hEvent,INFINITE);
// 		ResetEvent(g_hEvent);
		if (nTickets>0)
		{
			Sleep(1);
			cout<<"Thread1 sells tickets : "<<nTickets--<<endl;
			SetEvent(g_hEvent);
		}
		else
		{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}

DWORD WINAPI Fun2Pro(LPVOID lpParameter)
{
	while (TRUE)
	{
		WaitForSingleObject(g_hEvent,INFINITE);
// 		ResetEvent(g_hEvent);
		if (nTickets>0)
		{
			Sleep(1);
			cout<<"Thread2 sells tickets : "<<nTickets--<<endl;
			SetEvent(g_hEvent);
		}
		else
		{
			SetEvent(g_hEvent);
			break;
		}
	}
	return 0;
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《深入浅出MFC》2/e 简体版 目录 第0章 你一定要知道(导读) 这本书适合谁 你需要什么技术基础 你需要什么软硬件环境 让我们使用同一种语言 本书符号习惯 本书例程的取得 范例程序说明 与前版本之差异 如何联络作者 第一篇 勿在浮砂筑高台 第1章 Win32程序基本概念 Win32程序开发流程 需要什么函数库(.LIB) 需要什么头文件(.H) 以消息为基础,以事件驱动之(message basedevent driven) 一个具体而微的Win32程序 程序进入点WinMain 窗口类之注册与窗口之诞生 消息循环 窗口的生命中枢:窗口函数 消息映射(Message Map)的雏形 对话框的运行 模块定义文件(.DEF) 资源描述档(.RC) Widnows程序的生与死 空闲时间的处理:OnIdle Console程序 Console程序与DOS程序的差别 Console程序的编译链接 JBACKUP:Win32 Console程序设计 MFCCON:MFC Console程序设计 行程与线程(Process and Thread) 核心对象 一个行程的诞生与死亡 产生子行程 一个线程的诞生与死亡 以_beginthreadex取代CreateThread 线程优先级(Priority) 多线程程序设计实例 第2章 C++的重要性质 类及其成员——谈封装(encapsulation) 基类与派生类:谈继承(Inheritance) this指针 虚拟函数与多态(Polymorphism) 类与对象大解剖 Object slicing与虚拟函数 静态成员(变量与函数) C++程序的生与死:兼谈构造函数与解构函数 四种不同的对象生存方式(in stack、in heap、global、local static) 执行期类型信息(RTTI) 动态生成(Dynamic Creation) 异常处理(Exception Handling) Template Template Functions Template Classes Template的编译与链接 第3章 MFC六大关键技术之仿真 MFC类层次结构 Frame 1范例程序 MFC程序的初始化过程 Frame 2范例程序 RTTI(执行期类型识别) 类别型录网与CRuntimeClass DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏 Frame 3范例程序 IsKindOf(类型识别) Frame 4范例程序 Dynamic Creation(动态生成) DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE宏 Frame 6范例程序 Persistence(永久保存)机制 Serialize(数据读写) …… 第二篇 欲善工事先利其器 第4章 Visual C++集成开发环境 第三篇 浅出MFC程序设计 第5章 总观Application Framework 第6章 MFC程序的生死因果 第7章 简单而完整:MFC骨干程序 第四篇 深入MFC程序设计 第8章 Document-View深入探讨 第9章 消息映射与命令传递 第10章 MFC与对话盒 第11章 View功能的加强与重绘效率的提高 第12章 打印与预览 第13章 多重文件与多重显示 第14章 MFC多线程程序设计 第15章 定制一个AppWizard 第16章 站上众人的肩膀——使用Components&activeX; Controls

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值