修改UCGUI源代码以支持多个独立窗体的说明发布时间:2008-11-17来源:来自网络作者:匿名浏览:2868问题的提出:关于对话框对话框处理程序中,ok按钮按下后想出现一个消息框。但直接加在程序中好像不行,该怎么办?[独立窗体]------指该窗体的父窗体不是用户创建窗体,在UCGUI中其父窗体为句柄为1.一,消息处理的流程.ucgui是采用的消息驱动的.它专门有对外的一套采集消息的接口,我在模似器中,就是通过LCD窗口的MOUSE消息,将MOUSE移动,点击消息传入到这个接口中,以驱动UCGUI中的事件的..UCGUI中的消息驱动,其实与WINDOWS的是类似的,几种基本的消息与WINDOWS是一样的,但UCGUI的更简单,消息更少,没有WINDOWS那么多的消息种类.在WINDOWS中,我们最简单处理按钮事件的是在WM_COMMAND消息中,通过按钮的标志ID来处理不同的按钮,所以,我们的按钮的标志ID都是不同的.要不然无*区别开的.要处理点击OK这个按钮的事件,UCGUI的处理方*有些不同,是在WM_NOTIFY_PARENT消息中处理:caseWM_NOTIFY_PARENT:Id=WM_GetId(pMsg-hWinSrc);/*Idofwidget*/NCode=pMsg-Data.v;/*Notificationcode*/switch(NCode){caseWM_NOTIFICATION_RELEASED:/*Reactonlyifreleased*/if(Id==GUI_ID_OK){/*OKButton*/GUI_EndDialog(hWin,0);}if(Id==GUI_ID_CANCEL){/*CancelButton*/GUI_EndDialog(hWin,1);}break;}break;ucgui中的消息非常的少,只有差不多不到二十种,其实这对于嵌入式系统来说,已经完全足够了,用户可以自定义消息,WM_NOTIFY_PARENT这个消息是由你窗体传送过来的,是由函数WM_NotifyParent(hObj,Notification)实现的.voidWM_NotifyParent(WM_HWINhWin,intNotification){WM_MESSAGEMsg;Msg.MsgId=WM_NOTIFY_PARENT;Msg.Data.v=Notification;WM_SendToParent(hWin,&Msg);}这个函数相当简单,其主要还是WM_SendToParent这个函数的调用,这个函数又调用voidWM_SendMessage(WM_HWINhWin,WM_MESSAGE*pMsg),这个函数是最基本的一个消息处理函数,它的第一个参数指定了接受这个要处理的消息的句柄,第二个指定了是什么消息.这个函数的主要作是,是调用相就窗口的消息处理函数来处理消息.比如说,在这里,我们这个消息的处理过程是这样的,首先,你左键点击OK按钮,那么在GUI_Exec()这个窗体消息LOOP处理当中,首先将此消息(WM_TOUCH)传给OK按钮(注意这也是一个窗体),然后,OK按钮的消息处理函数将此消息再以WM_NOTIFY_PARENT传送到你窗体---对话框当中,这样,对话框就可以处理到点击OK按扭这个事件了...其实这个消息处理的流程与WINDOWS也是类似的.原理一样..理解了这个过程,那么,我们就可以这个消息中处理很多东西了,只要是在对话框中的子控件,一般的消息都可以如此处理.二,发现存在的问题----两个独立的窗口并立时,当关闭其中一个,另外一个不再有响应.在如下代码中处理弹出消息框,点击对话框的OK后弹出消息框,会出现当按对话框的Cancel关闭对话框后,弹出的消息框就没有任何响应的情况.或者是关闭掉弹出的消息框,对话框就没有任何响应的情形.caseWM_NOTIFY_PARENT:Id=WM_GetId(pMsg-hWinSrc);/*Idofwidget*/NCode=pMsg-Data.v;/*Notificationcode*/switch(NCode){caseWM_NOTIFICATION_RELEASED:/*Reactonlyifreleased*/if(Id==GUI_ID_OK){/*OKButton*//弹出消息框...GUI_MessageBox(Thistextisshown\ninamessagebox,Caption/Title,GUI_MESSAGEBOX_CF_MOVEABLE);GUI_EndDialog(hWin,0);}if(Id==GUI_ID_CANCEL){GUI_EndDialog(hWin,1);}break;}经分析,粗步的原因是,调用MainTask的主线程已经退出了,这个主线程是在模拟器中开启的,它的主线程函数是Thread(),Thread函数里调用main,main再调用MainTask;所以主线程退出后,消息框再也没有任何反应了.这是从模拟器的角度来分析,现在我们分析一下,为什么MainTask的主线程会这么早退出呢?深入UCGUI的源码,可以知道,对话框的消息处理函数是在如下的函数中调用的.intGUI_ExecDialogBox(constGUI_WIDGET_CREATE_INFO*paWidget,intNumWidgets,WM_CALLBACK*cb,WM_HWINhParent,intx0,inty0)//建立阻塞式对话框{_cb=cb;GUI_CreateDialogBox(paWidget,NumWidgets,_cbDialog,hParent,x0,y0);//建立非阻塞式对话框while(_cb){if(!GUI_Exec())GUI_X_ExecIdle();//出现HardFaultException()中断的原因}return_r;}在如上的代码中我们可以看到,当UCGUI中有一个独立的窗体退出后,_cb会被清为0,此时退出GUI窗口LOOP.即结束了UCGUI窗口消息处理.其实,GUI_MessageBox弹出的消息框其实也是一种对话框,在这里我们就可以发觉UCGUI的窗口支持系统还不是非常的完备,我们期待的结果是,点击对话框的OK,弹出消息框,GUI_EndDialog关闭掉对话框,消息框继续有反应,如果没有在OK按钮中调用GUI_EndDialog关闭对话框,则对话框与消息框都要能够正常反应.关于这个问题的解决,我想还要进一步了解UCGUI窗口处理的细节.三.寻找问题的解决办法.在我们发现这个问题,我们已经粗步分析了,问题不是出在我们编写程序上,而是UCGUI的内部,那么要解决这个问题,我们就要进一步了解UCGUI的窗口体系.其实换一句话说,在嵌入式应用中,窗口的强大直接决定到GUI系统的体积大小,并不是所有的情况都要有这种支持,也不一定说是UCGUI的BUG,当然我们希望在下一版本不再有这个问题.下面是详细分析:1.对话框voidMainTask(void){GUI_Init();WM_SetDesktopColor(GUI_RED);/*Automacallyupdatedesktopwindow*/WM_SetCreateFlags(WM_CF_MEMDEV);/*Usememorydevicesonallwindowsto**oidflicker*/GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate),&_cbCallback,0,0,0);}上面是我们创建对话框的程序,是我们编写的代码,具体看GUI_ExecDialogBox()这个函数代码如下,它主要有以下两个作用:[1].创建对话框中的所有子窗体.[2].进入消息LOOP,收集并转发消息到对应窗体进行处理.消息如WM_TOUCH及WM_KEY,这些消息被分发到对话框中各子窗体中去处理,父窗体(对话框)之所以能够处理其上子控件的消息,全都是因为子控件在传递消息,父窗体中对子窗体的消息进行处理,也可以不处理,我们编程者要处理时,则要清楚哪些消息是可以处理的,不然无*编写程序,所以子窗体中的消息一直都在往父窗体中发送,与父窗体有无处理无关系.intGUI_ExecDialogBox(constGUI_WIDGET_CREATE_INFO*paWidget,intNumWidgets,WM_CALLBACK*cb,WM_HWINhParent,intx0,inty0){_cb=cb;GUI_CreateDialogBox(paWidget,NumWidgets,_cbDialog,hParent,x0,y0);while(_cb){if(!GUI_Exec())GUI_X_ExecIdle();}return_r;}2.消息框其实消息框本身就是一个只含静态文框及OK按钮的对话框,在对话框的WM_NOTIFY_PARENT消息中弹出消息框:caseWM_NOTIFY_PARENT:Id=WM_GetId(pMsg-hWinSrc);/*Idofwidget*/NCode=pMsg-Data.v;/*Notificationcode*/switch(NCode){caseWM_NOTIFICATION_RELEASED:/*Reactonlyifreleased*/if(Id==GUI_ID_OK){/*OKButton*/GUI_EndDialog(hWin,0);}if(Id==GUI_ID_CANCEL){/*CancelButton*/GUI_EndDialog(hWin,1);}break;}break;在关闭对话框之前弹出一个消息框,当我们都不点击消息框与对话框的OK按钮来结束时,他们都有反应也可以前后台切换,不存在什么问题.问题就出在关闭其中一个后,其它的任何一个也无反应.这个原因,在上面有过过粗步的分析,但没有进入到UCGUI的实现机制内,现在我们来看一下._cb=cb;while(_cb){if(!GUI_Exec())GUI_X_ExecIdle();}这个LOOP,其实它就是类似我们非常熟悉的WIN下面的消息LOOP,其原理是一致的._cb是什么东西呢?这里我们可以看到,它其实就是对话框的窗口消息处理函数,这里面有一个判断,就是_cb非空时,才进行消息LOOP,_cb在Dialog.c中的定义如下:staticWM_CALLBACK*_cb;_cb是一个全局变量!我们程序中创建对话框与弹出消息框时两次调用了GUI_ExecDialogBox,后一次的_cb将会把前面的值冲掉,我们程序中,可以知道它是消息框的窗口消息处理函数地址.由此可知道谁在后面调用,则另外一个对话框的窗口消息处理函数根本就是无用的,它的消息其实是被后来创建的对话框消息处理函数所处理的.在while中有判断,那么证明_cb是在GUI_Exec所产生的调用中有使用的.进一步看,可以看到,调用的其实是如下面的箭头方向所指,一步一步深入的,其中最明显的一点就是,窗口消息处理函数是在WM_SendMessage中通过函数指针的调用中,注意[]内部的就是真正被调用来处理消息的函数,它是将消息一步一步的传到真正要处理此消息的窗体中,在这里其实就是后面弹出的消息框窗体.GUI_Exec---GUI_Exec1--WM_Exec---WM_Exec1--WM_HandlePID--WM_SendMessage--(*pWin-cb)(pMsg)[_FRAMEWIN_Callback]--_OnTouch()---(*cb)(pMsg)[_cbDialog]--(*_cb)(pMsg)[_MESSAGEBOX_cbCallback]WM_HandlePID()----------专门处理MOUSE消息的函数.WM_SendMessage()--------