修改UCGUI源代码以支持多个独立窗体的说明

发 布 时 间 : 2008-11-17 来 源 : 来自网络 作 者 : 匿名 浏 览 :

问题的提出:

-----[求助]关于对话框对话框处理程序中,ok按钮按下后想出现一个消息框,该怎么做?直接加在程序中好像不行,请版主帮帮忙.

[独立窗体]------指该窗体的父窗体不是用户创建窗体,在UCGUI中其父窗体为句柄为1.

一,消息处理的流程.

ucgui是采用的消息驱动的. 它专门有对外的一套采集消息的接口, 我在模似器中, 就是通过LCD窗口的MOUSE消息,将MOUSE移动, 点击消息传入到这个接口中, 以驱动UCGUI中的事件的..

UCGUI中的消息驱动, 其实与WINDOWS的是类似的, 几种基本的消息与WINDOWS是一样的, 但UCGUI的更简单, 消息更少, 没有WINDOWS那么多的消息种类. 在WINDOWS中, 我们最简单处理按钮事件的是在WM_COMMAND消息中, 通过按钮的标志ID来处理不同的按钮, 所以, 我们的按钮的标志ID都是不同的. 要不然无*区别开的.

要处理点击OK这个按钮的事件, UCGUI的处理方*有些不同, 是在WM_NOTIFY_PARENT消息中处理:

case WM_NOTIFY_PARENT:
Id = WM_GetId(pMsg->hWinSrc); /* Id of widget */
NCode = pMsg->Data.v; /* Notification code */
switch (NCode) {
case WM_NOTIFICATION_RELEASED: /* React only if released */
if (Id == GUI_ID_OK) { /* OK Button */
GUI_EndDialog(hWin, 0);
}
if (Id == GUI_ID_CANCEL) { /* Cancel Button */
GUI_EndDialog(hWin, 1);
}
break;
}
break;

ucgui中的消息非常的少, 只有差不多不到二十种, 其实这对于嵌入式系统来说, 已经完全足够了, 用户可以自定义消息, WM_NOTIFY_PARENT这个消息是由你窗体传送过来的, 是由函数WM_NotifyParent(hObj, Notification)实现的.

void WM_NotifyParent(WM_HWIN hWin, int Notification) {
WM_MESSAGE Msg;
Msg.MsgId = WM_NOTIFY_PARENT;
Msg.Data.v = Notification;
WM_SendToParent(hWin, &Msg);
}

这个函数相当简单, 其主要还是WM_SendToParent这个函数的调用, 这个函数又调用void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg), 这个函数是最基本的一个消息处理函数, 它的第一个参数指定了接受这个要处理的消息的句柄, 第二个指定了是什么消息.这个函数的主要作是, 是调用相就窗口的消息处理函数来处理消息.

比如说, 在这里,我们这个消息的处理过程是这样的, 首先, 你左键点击OK按钮, 那么在GUI_Exec()这个窗体消息LOOP处理当中, 首先将此消息(WM_TOUCH)传给OK按钮(注意这也是一个窗体), 然后, OK按钮的消息处理函数将此消息再以WM_NOTIFY_PARENT传送到你窗体---对话框当中, 这样, 对话框就可以处理到点击OK按扭这个事件了...

其实这个消息处理的流程与WINDOWS也是类似的. 原理一样..

理解了这个过程, 那么, 我们就可以这个消息中处理很多东西了, 只要是在对话框中的子控件, 一般的消息都可以如此处理.


二,发现存在的问题----两个独立的窗口并立时, 当关闭其中一个, 另外一个不再有响应.

在如下代码中处理弹出消息框, 点击对话框的OK后弹出消息框, 会出现当按对话框的Cancel关闭对话框后, 弹出的消息框就没有任何响应的情况. 或者是关闭掉弹出的消息框, 对话框就没有任何响应的情形.

case WM_NOTIFY_PARENT:
Id = WM_GetId(pMsg->hWinSrc); /* Id of widget */
NCode = pMsg->Data.v; /* Notification code */
switch (NCode) {
case WM_NOTIFICATION_RELEASED: /* React only if released */
if (Id == GUI_ID_OK) { /* OK Button */
/弹出消息框...
GUI_MessageBox("This text is shown\nin a message box",
"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的源码, 可以知道, 对话框的消息处理函数是在如下的函数中调用的.

int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,
int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,
int x0, int y0)
{
_cb = cb;
GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog, hParent, x0, y0);
while (_cb) {
if (!GUI_Exec())
GUI_X_ExecIdle();
}
return _r;
}


在如上的代码中我们可以看到, 当UCGUI中有一个独立的窗体退出后, _cb会被清为0, 此时退出GUI窗口LOOP. 即结束了UCGUI窗口消息处理.

其实, GUI_MessageBox弹出的消息框其实也是一种对话框, 在这里我们就可以发觉UCGUI的窗口支持系统还不是非常的完备, 我们期待的结果是, 点击对话框的OK, 弹出消息框, GUI_EndDialog关闭掉对话框, 消息框继续有反应, 如果没有在OK按钮中调用GUI_EndDialog关闭对话框, 则对话框与消息框都要能够正常反应.

关于这个问题的解决, 我想还要进一步了解UCGUI窗口处理的细节.

三.寻找问题的解决办*.

在我们发现这个问题, 我们已经粗步分析了, 问题不是出在我们编写程序上, 而上UCGUI的内部, 那么要解决这个问题, 我们就要进一步了解UCGUI的窗口体系.其实换一句话说, 在嵌入式应用中, 窗口的强大直接决定到GUI系统的体积大小, 并不是所有的情况都要有这种支持, 也不一定说是UCGUI的BUG, 当然我们希望在下一版本不再有这个问题.

下面是详细分析:

1. 对话框

void MainTask(void) {
GUI_Init();
WM_SetDesktopColor(GUI_RED); /* Automacally update desktop window */
WM_SetCreateFlags(WM_CF_MEMDEV); /* Use memory devices on all windows to **oid flicker */
GUI_ExecDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0);
}

上面是我们创建对话框的程序, 是我们编写的代码, 具体看GUI_ExecDialogBox()这个函数代码如下, 它主要用以下两个作用:

[1].创建对话框中的的所有子窗体.

[2].进入消息LOOP, 收集并转发消息到对应窗体进行处理. 消息如WM_TOUCH及WM_KEY, 这些消息被分发到对话框中各子窗体中去处理, 要理解如下的尽, 父窗体(对话框)之所以能够处理其上子控件消息, 全都是因为子控件在传递, 父窗体中对子窗体的消息进行处理, 也可以不处理, 我们编程者要处理时, 则要清楚哪些消息是可以处理的, 不然无*编写程序, 所以子窗体中的消息一直都在往父窗体中发送, 与父窗体有无处理无关系.

int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,
int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,
int x0, int y0)
{
_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消息中弹出消息框:
case WM_NOTIFY_PARENT:
Id = WM_GetId(pMsg->hWinSrc); /* Id of widget */
NCode = pMsg->Data.v; /* Notification code */
switch (NCode) {
case WM_NOTIFICATION_RELEASED: /* React only if released */
if (Id == GUI_ID_OK) { /* OK Button */
GUI_EndDialog(hWin, 0);
}
if (Id == GUI_ID_CANCEL) { /* Cancel Button */
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中的定义如下:

static WM_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()--------基层的分派消息的函数, 决定谁应该被调来来处理消息.


这里面有些人会提出一个问题, 为什么消息框与对话框的消息都在后来创建的消息处理函数里面处理而不会引发什么问题呢?

是的, 表面上看去, 消息框与对话框都好好的, 没什么异常, 但其实如果你在对话框中进行过消息的处理, 那么你就可以很清楚的看到这些消息是从来不会发生的, 即使你在它的消息处理函数里下断点它也不会有任何中断.

至于其它的一些常用的消息的处理, 对话框与消息没有什么区别, 最重要的消息就是重画, 重画的时候, 比如消息框与对话框有重叠的话, 当进行窗口前后切换将其中一个切换到前面来时, 会产生重画消息. 这样就会重新计算一次该画到屏慕上的东西, 不光要发关重画消息到各窗口, 还会将整个屏慕的东西都重新计算一次, 并画出来. 这一点还是很复杂的, 须要进一步学习.是GUI图形的一个难点和重点. 以后再详细分析.


进行到这里, 我们就可以比较明白的知道, 在点击OK后, 无论是消息框还是对话框, 哪一个先被关掉, 都会掉用下面的GUI_EndDialog. 这里, _cb被清为零, 也就意味着消息LOOP到此结束了. 所以后面另外一个未被关掉的当然不会再有任何响应了.

void GUI_EndDialog(WM_HWIN hWin, int r) {
_cb = NULL;
_r = r;
WM_DeleteWindow(hWin);
}


原因我们分析出来了. 现在我们要解决这个问题, 那还差一大步, 还有很多问题要进一步弄清, 为什么UCGUI是这样的机制.要如何解决. 将会在下一步仔细学习分析.

楼主再仔细读一下我的分析, 结合实际自己也来分析一下, 问题虽然小, 但其实隐藏着的问题不小. 你的所言其实还是没有理解清楚问题, 因为你所说的HIDE对话框. 其实没有点着问题实际, 我们现在所讲的是UCGUI对于多个独立窗体的消息处理支持. UCGUI不完善.对这个目前没有支持. 你要深入到这一点来理解问题.

再仔细看下我的分析. 有什么问题再提, 结合实际, 进行源码调试, 对于问题会有更多的了解....

四. 进一步分析找到解决办*.

点击对话框的OK按钮后, 其实这个时候, 由于消息框是对话框后弹出来的,那么这个消息框的消息处理函数_MESSAGEBOX_cbCallback则成为_cb的值了, 那么以后对话框与消息框的消息处理都是在消息框的_MESSAGEBOX_cbCallback里面处理的, 如果你是在点击对话框的OK按扭之后弹出消息框.如下:

case WM_NOTIFY_PARENT:
Id = WM_GetId(pMsg->hWinSrc); /* Id of widget */
NCode = pMsg->Data.v; /* Notification code */
switch (NCode) {
case WM_NOTIFICATION_RELEASED: /* React only if released */
if (Id == GUI_ID_OK) { /* OK Button */
GUI_MessageBox("This text is shown\nin a message box",
"Caption/Title", GUI_MESSAGEBOX_CF_MOVEABLE);
GUI_EndDialog(hWin, 0);
}
if (Id == GUI_ID_CANCEL) {
GUI_EndDialog(hWin, 1);
}


那么, 程序执行到弹出消息框之前, 消息 WM_NOTIFY_PARENT 都是由对话框的消息处理函数_cbCallback处理的, 在弹出消息框之后, 如我们在(二)上面的分析, 就知道以后的消息都是由 消息框的_MESSAGEBOX_cbCallback来处理的, 此时, 在消息LOOP中马上又会发现上一次的MOUSE消息, 因为这个点击OK产生的消息还在, 并没有产生新的消息, [说来其实UCGUI中的消息是没有什么队列的, 是接收一个处理一个, 即时处理那种, 这样就简单了很多,] 那么又一次进行WM_NOTIFY_PARENT的GUI_ID_OK按钮的处理, 只不过这个处理是在消息框的_MESSAGEBOX_cbCallback函数中进行的, 在这个函数里面默认的对于GUI_ID_OK的处理就是调用GUI_EndDialog(hWin, 1); 一旦调用这个函数, 那么产生的结果就如同先前分析的, 会导致WM_DeleteWindow的调用, 而将_cb的值清为空, 并清除hWin及其所有了窗体. 从而结束了LOOP消息处理.

所以, 我们可以看到, 在点击对话框的OK后, 弹出消息框, 之后即使我们不调用清除对话框的函数, 什么也不做. 也会导到消息LOOP结束, 退出所有窗口消息的处理.

其实, 须要进一步说明的是, 在UCGUI中, 似乎在设计时只是支持单窗口的消息处理, 如果要多窗口的支持, 可以如同示例中一样, 启用多任务支持, 不然, 在单任务下, 一个MainTask中只能支持一个独立窗体, 但是, 显然我们如果只是为了要弹出一个消息框而启动一个任务, 这未免太不实际.

解决的办*, 还是有的, 那就是了解UCGUI, 自己来修改. 可以如下做-------

[如上创建一对话框及消息框, 对话框与消息框即为如下所讲的外部独立窗体]

1. 经过详细的分析, 认识到在消息处理中, 创建一个独立窗体, 则其窗口消息处理函数就会成为整个UCGUI中的消息的外部接收口, 用户所能处理到所有消息均在此处接收到.

2. 如果在后来在创建独立窗体, 则后来的窗口的消息处理函数代替了先前创建的窗口的消息处理. 那么, 如果不使它替代, 而是用一个数组将所有消息处理函数都存放起来, 然后在内部将消息往外传的地方(即外部独立窗口消息函数被调用处)修改成调用所有非空的外部独立窗口消息处量函数(而不是单个).这样就可以达到每个独立窗口消息处理函数都被调用, 各自独立运行. 但要注意和解决的问题是, 消息该如何分配到各独立窗口.

3.在清除独立窗体时, 只是将此独立窗体对应的消息处理函数清零, 并清除该窗体及其的所有子窗体. 使其不再处理消息.


那么. 我们在点击OK关闭对话框, 也就是清除对话框并让其退出消息接收. 对于弹出的消息框, 我们希望它作为一个新的独立窗体而进入窗体消息接收与处理. 就达到了我们的目的.


这是理论上的分析. 我将把实际修改后的东东也发上来, 做一示例.


五. 对UCGUI源码做出部分修改以实现多独立窗口支持.
[2005-6-30 2:30]

在第三节当中, 我们通过进一步的分析源码, 大致找到了解决问题的办*, 但那只是理论上的指导, 实际上的修改其实还会带来其它的很多问题, 因为在UCGUI体系中, 对其源码作出改动, 一定都会影响到其它的地方, 现在我们就实际的源码修改说明几点要注意的问题:


1、将原来的_cb一个变量, 修改成一个结构为new_cb的结构数组,有10个元素,

/static WM_CALLBACK* _cb;

typedef struct win_cb{
WM_CALLBACK* _cb;
WM_HWIN hwin; /_cb消息函数对应的窗口..
WM_HWIN hclient;/_cb消息函数对应的窗体窗户区...
}new_cb,*lpnew_cb;

static int dialog_pos = 0;/在数组中当前可用作存窗体消息函数的元素.
static int MAX_DIALOG = 10;/最多可支持打开的独立窗口数, 其实可以改成支持无数个,但这里作简单处理

int checkHasDialog(); /检查是否还有独立窗体存在, 以决定是否退出消息LOOP...
int getDialogIndex(lpnew_cb lp_cb);/获取当前可用于存放独立窗体的位置索引, 创建新独立窗体时调用.

static new_cb _cb[10]; /独立窗口数组创建一个新的独立窗体, 将此窗体加入到独立窗体数组当/中时, 必须注意几个问题,在如下的新旧代码对比中说明:

/新修改后的创建对话框的函数...
int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,
int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,
int x0, int y0)
{
/ _cb = cb;
dialog_pos = getDialogIndex(_cb);
if(dialog_pos != -1) _cb[dialog_pos]._cb = cb;
else return _r;
GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog, hParent, x0, y0);
/ while (_cb) {
/* 2005-6-30 0:20:16
while (checkHasDialog()) {
if (!GUI_Exec())
GUI_X_ExecIdle();
}*/
return _r;
}

/旧的创建对话框的函数...
int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,
int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,
int x0, int y0)
{
_cb = cb;
GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog, hParent, x0, y0);
while (_cb) {
if (!GUI_Exec())
GUI_X_ExecIdle();
}
return _r;
}
WM_HWIN GUI_CreateDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget, int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,
int x0, int y0)
{
WM_HWIN hDialog = paWidget->pfCreateIndirect(paWidget, hParent, x0, y0, cb); /* Create parent window */
WM_HWIN hDialogClient = WM_GetClientWindow(hDialog);
_cb[dialog_pos].hwin = hDialog;
_cb[dialog_pos ].hclient = hDialogClient; /加到GUI_CreateDialogBox中的,其余不变...
WIDGET_OrState(hDialog, paWidget->Flags);
........
}

[1]、getDialogIndex(_cb),创建新的独立窗体前,首先就要在独立窗体数组中查找空位置,如果独立窗体已达最大数,则不可再加入独立窗体,这里我做的是简单处理,没有用到动态内存分配,将独立窗体数组扩大,主要是因为是演示,读者自己可以试度支持无限独立窗体。

[2]、_cb[dialog_pos]._cb = cb 独立窗口的窗口消息处理函数必须在GUI_CreateDialogBox之前赋值,因为在GUI_CreateDialogBox中就会用到这个窗口消息处理函数。

[3]、_cb[dialog_pos]中的hwin等窗口句柄的处理加到创建对话框当中,千万不要在创建完对话框后再根据返回的对话框句柄初始化,因为在创建对话的子窗体时就会调用到对话框消息处理函数,如果hwin此时未初始化,则在_cDialog()中就无*分发消息,这样对话框中的子窗体都无*正确显示的。

[4]、GUI_ExecDialogBox中的窗口消息LOOP被注掉了,改为放到MainTask中调用,这一点与我们WIN下面就很类似了,而且事实证明,这个改进是非常有用的,因为当我们把窗体都创建了之后,再来执行消息LOOP的话,可以避免前面创建独立窗体的消息LOOP被后面创建的消息LOOP中断的作用,只有后面的窗体关闭掉后,才会返回到先前的窗体消息LOOP当中,在我们一直谈论的话题当中,点OK按钮弹出一个消息框,这个流程其实是这样的:

------点击OK的消息处理流程: 在没有弹出消息框之前,是在一直进行着对话框的窗口消息LOOP,当点击对话框上的OK按扭时(消息LOOP中的点击处理在WM_HandlePID()进行),WM_HandlePID()调用WM_SendMessage向OK按扭窗体发送WM_TOUCH消息,再到BUTTON的消息处理函数_BUTTON_Callback中处理WM_TOUCH(_OnTouch)消息,然后OK按钮中还要调用WM_NotifyParent转发WM_NOTIFY_PARENT消息到父窗体,此时父窗体才开如执行弹出消息框的代码,所以此时我们可以了解到,如果窗口消息LOOP在GUI_ExecDialogBox当中进行的话,那么表明原来的对话框中进行的窗口消息LOOP就会被新创建的消息框挂起来了,一直要等到消息框中的窗口消息LOOP结束返回为止,这当中会造成在对话框窗口消息LOOP中的WM_LOCK调用后,WM_UNLOCK的调用一直要等到消息框的窗口消息LOOP结束为止。

void MainTask(void) {
GUI_Init();
WM_SetDesktopColor(GUI_RED); /* Automacally update desktop window */
WM_SetCreateFlags(WM_CF_MEMDEV); /* Use memory devices on all windows to **oid flicker */
GUI_ExecDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0);
/ while (_cb) {
while (checkHasDialog()) {
if (!GUI_Exec())
GUI_X_ExecIdle();
}
}

在上面的流程说明中,认识到是有必要将窗口消息LOOP放到创建对话框这外的,因为我们是要创建多个独立窗体,那么LOOP当中要分发消息的对象也就是多个窗体,而不是其中的一个,所以要从创建对话框函数中拿出, 由用户来写,如同WIN下面一样。

2、在第三节中第2点还说到,要分别调用各个独立窗体的窗口消息函数,必须注意消息的分发,下面我们看一下具体如何分发:

static void _cbDialog(WM_MESSAGE* pMsg)
{
char buf[100];
/* 2005-6-28 0:53:23
if (_cb) {
sprintf(buf, "the hwin = %d, msgid = %d", pMsg->hWin, pMsg->MsgId);
SIM_Log(buf);
(*_cb[0]._cb)(pMsg);
}*/

int i = 0;
WM_LOCK();
for(i = 0; i < MAX_DIALOG; i ){
if(_cb[i]._cb != (WM_CALLBACK*)0){
if (WM__IsWindow(pMsg->hWin))
{
if(pMsg->hWin == _cb[i].hwin || _cb[i].hclient == pMsg->hWin)
{
sprintf(buf, "has call the i = %d hwin = %d, msgid = %d _cb[i].hwin = %d _cb[i].hclient = %d \n", i, pMsg->hWin, pMsg->MsgId, _cb[i].hwin, _cb[i].hclient);
(*(_cb[i]._cb))(pMsg);
}else
{
sprintf(buf, "not call the i = %d hwin = %d, msgid = %d _cb[i].hwin = %d _cb[i].hclient = %d \n", i, pMsg->hWin, pMsg->MsgId, _cb[i].hwin, _cb[i].hclient);
}
SIM_Log(buf);
}
}

}
WM_UNLOCK();
}

_cbDialog是在对话框的FRAMEWIN__cbClient中调用的,如下形式:

if (cb) {
pMsg->hWin = hParent;
(*cb)(pMsg);
}

每个独立对话框窗体均是这样,通过其FRAMEWIN来调用用户指定的窗口消息处理函数,在分发消息时,其实只须要根据消息中的窗体句柄来分发,因为我们对于每个独立窗体,均记载了它的窗体句柄及客户句柄。所有创建的独立窗体的消息均是在_cbDialog中顺序进行处理的,如果任何一个窗体的消息处理函数有问题,那么就会导致知所有窗体均没有反应。


3、第三节中所说的第3点,独立窗体退出的处理:
void GUI_EndDialog(WM_HWIN hWin, int r) {
/ _cb = NULL;
int i = 0;
char buf[255];
if (!hWin)
return;
WM_LOCK();
if (WM__IsWindow(hWin))
{
for(i = 0; i < MAX_DIALOG; i ){
if(hWin == _cb[i].hwin || _cb[i].hclient == hWin){
_cb[i]._cb = NULL;
_cb[i].hwin = 0;
_cb[i].hclient = 0;
sprintf(buf, "not call the i = %d hWin = %d, _cb[i].hwin = %d _cb[i].hclient = %d \n", i, hWin, _cb[i].hwin, _cb[i].hclient);
SIM_Log(buf);
}
}

}
WM_UNLOCK();
_r = r;
WM_DeleteWindow(hWin);
}

同样也是根据窗口句柄来找到要清除的独立窗体。

4、关于MOUSE点击消息处理的WM_HandlePID()函数

这个函数是专门负责处理MOUSE消息的,即WM_TOUCH消息,当你点击或是在触摸屏上点击上,产生此消息;它当中有两个变量,一个静态的旧消息变量,一个是局部新消息变量,每次均从MOUSE的接口(GUI_PID_GetState())中取新的,每次处理一次最新消息就将最新消息更新到旧消息变量上,下一次处理消息时会比较新旧消息,以避免对相同消息的处理。

说明上面的问题,我们是为了说明这样一个问题,如果我们没有将窗口消息LOOP放到GUI_ExecDialogBox函数之外,那么存在一个问题就是在点击OK一次后会重复创建N多的消息框,原因其实是相同的消息进行了重复处理,不是每处理完一次消息就更新了旧消息吗? 为什么还会重复处理?这里面根本的问题就是旧消息还未更新,是因为在一创建消息框后,每一个消息框都进行自己的消息LOOP,而将上面一次的消息LOOP挂起来了,那么一直要等到结束返回到上一次消息LOOP,才会更新旧消息。如果将消息LOOP放出GUI_ExecDialogBox之处,即MainTask当中,即不存在此问题,如果硬是不把消息LOOP放出来,也可以在WM_SendMessage之前就更新旧消息,这样也可以避免由于旧消息未更新而重复处理同一消息。




附言:

 

一般情况下, 小型的GUI体系, 都很少会有打个多个独立窗口的要求, UCGUI还是在发展中, 而且并不成熟, 所以有很多的问题, 不完善是肯定的.

我们要学习UCGUI, 首先一定要动手写UCGUI的程序, 阅读源码, 这样才以发现更多的问题, 才能更了解UCGUI...

我本人就是经常有问题就读源码, 在实际编程中, 发现的问题是最多的, 而且现在模拟器已经还原成源码. 底下不再有任何的秘密, 所有的问题都摆在源码之下, 所以只要花心思研究. 应该可以解决很多问题. 学习到很多图形处理的深层知识.



上一篇:如何在uC/GUI中实现窗体半透明效果(一) 下一篇:基于ARM(S3C44B0X)的嵌入式MPEG-4远程网络监控系统