事件驱动编程【0】

事件驱动流程

  • 每个应用程序属于一个事件泵或者称为消息循环 是每个事件驱动中最重要的部分。
  • 调用获取消息窗口API框以非阻塞获取一个输入事件消息。
  • 当任何这类事件发生时,等待来自鼠标或着键盘或者触摸屏的输入事件消息,并将其记录为一个消息对象并将其放置在此应用程序的消息队类当中。
  • 获取消息并且解除阻塞,将消息从队列复制出来到MSG对象,这就表示接收到一个事件。
  • 最后是处理消息事件。

事件循环的关键属性


三个重要关键属性

特殊消息

事件循环将使用特殊消息对象来记录所有可能对此应用程序感兴趣的事件,此事件对象仅用于通信并且可以方便的存储在事件队列中占用小小的队列内存(意味着在驱动中事件对象解析出来的输入事件才是最后的结果),保证可以在任何时候传递,系统将之记录 事件作为消息放置在消息队列当中但不等待消息的处理(这意味着这是一个异步机制)。

DISPATCH消息

消息的调用在下一事件之前完成并且回到事件循环,这意味着事件处理在运行中进行RTC(run-to-completion)步骤,不能被任何其他事件处理中断(在裸机中可以考虑全局中断的关闭)。

应用程序代码

在实时操作系统中,应用程序会调用某个艺术家的服务函数,事件驱动将调用应用程序,这将导致一个IOC(inversion of control)控制反转,这意味着引用事件和驱动应用程序取消引用不是相反的。

最后再谈谈顺序编程与事件驱动编程

举个例子:

事件驱动的钩子函数中将处理所有的时间逻辑,并且根据事件的类型快速的定位用户的事件响应。

一个简单的输入事件驱动:

点击查看代码
LRESULT CALLBACK WINPROC(HWND me, UINT sig, WPARAM wparam, LPARAM lparam)
{
	static int wm_keydown_ctr		=0;
	static int wm_mousemove_ctr		=0;
	
	LRESULT status;
	
	switch(sig)
	{
		case WM_CREATE:{
			status = WIN_HANDLED;
			break;
		}
		case WM_DESTROY:{
			postquitmessage(0);
			status = WIN_HANDLED;
			break;
		}
		case WM_PAINT:{
			PAINTSTRUCT ps;
			HDC hdc;
			RECT rect;
			char cBuffer[100]
			wsprintf(cBuffer,"KEYBORD = %3d,MOUSE = %3d",(wm_keydown_ctr%1000),(wm_keydown_ctr%1000));
			
			hdc = BeginPaint(me,&ps);
			GetClientRect(me,&rect);
			DrawText(hdc,cBuffer, -1, &rect,DT_SIGNALLINE | DT_CENTER | DT_VCENTER);
			EndPaint(me,&ps);
			
			status = WIN_HANDLED;
			break;
		}
		case WM_KEYDOWN:{
			++wm_keydown_ctr;
			InvalidateRect(me, NULL, false);
			
			status = WIN_HANDLED;
			break;
		}
		case WM_MOUSEMOVE:{
			++wm_mousemove_ctr;
			InvalidateRect(me, NULL, false);
			
			status = WIN_HANDLED;
			break;
		}
		default:{
			/*这意味着自己指定的特殊方式块 将被转向更高层次的编程解决*/
			status = DefwindowProc(me, sig, wparam, lparam);
		}
	}
	//根据返回值表示如何处理
	return status;
}
当产生输入事件时,我们假设LED即将闪烁:
case WM_KEYDOWN:{
		++wm_keydown_ctr;
		LED_ON;
		InvalidateRect(me, NULL, false);//将窗口设置为无效
		
		sleep(200);
		
		LED_OFF;
		InvalidateRect(me, NULL, false);
		
		status = WIN_HANDLED;
		break;
	}

那么结果是什么呢:
当产生输入事件时,程序将被冻结,并且不会立即更新键盘计数,假如输入多个按键这将变得更加糟糕。

所以这意味着事件发布与排队遭到了难以忽略的阻塞,这将导致你的程序开始睡眠,延迟会阻止你的窗口过程。

事实上我们需要对事件驱动进行一个简单的了解:我们称这个事件泵"THIS IS A PIG",所以如果不希望你的程序变成一只PIG,我们就需要通过事件驱动的方式来解决事件驱动的问题。

case WM_TIMER:{
	++wm_keydown_ctr;
	LED_OFF;
	InvalidateRect(me, NULL, false);	//将窗口设置为无效
	KillTimer(me ,wparam);			//杀死定时器事件
	
	status = WIN_HANDLED;
	break;
}
case WM_KEYDOWN:{
	++wm_keydown_ctr;
	LED_ON;
	InvalidateRect(me, NULL, false);	//将窗口设置为无效
	SetTimer(me, 1, 200, NELL);		//设置200Ms timer
	status = WIN_HANDLED;
	break;
}

当我们遇到需要超过100Ms的事件时,最好是将其分割呈不断大小的片时间,进行处理。

写在末尾

上述为本人浅短的认知与学习,望交流指正学习,感谢。