📄 基于 linux 和 minigui 的嵌入式系统软件开发指南(二).htm
字号:
MiniGUI-Threads
当中,如果发送消息的线程和接收消息的线程不是同一个线程,发送消息的线程将阻塞并等待另一个线程的处理结果,然后继续运行;否则,SendMessage
函数将直接调用接收消息窗口的窗口过程函数。MiniGUI-Lite
则和上面的第二种情况一样,直接调用接收消息窗口的窗口过程函数。 </P>
<P> <B>SendNotifyMessage:</B>该函数和 PostMessage
消息类似,也是不等待消息被处理即返回。但和 PostMessage
消息不同,通过该函数发送的消息不会因为缓冲区满而丢失,因为系统采用链表的形式处理这种消息。通过该函数发送的消息一般称为"通知消息",一般用来从控件向其父窗口发送通知消息。 </P>
<P> <B>PostQuitMessage:</B>该消息在消息队列中设置一个 QS_QUIT
标志。GetMessage 在从指定消息队列中获取消息时,会检查该标志,如果有 QS_QUIT 标志,GetMessage 消息将返回
FALSE,从而可以利用该返回值终止消息循环。 </P>
<P><B>4 MiniGUI-Threads 和 MiniGUI-Lite
在消息处理上的不同 <BR></B> 表 1 总结了 MiniGUI-Threads 和
MiniGUI-Lite 在消息处理上的不同</P>
<P> 表 1 MiniGUI-Threads 和 MiniGUI-Lite
在消息处理上的不同 </P>
<TABLE>
<TBODY>
<TR>
<TD></TD>
<TD>MiniGUI-Threads</TD>
<TD>MiniGUI-Lite</TD></TR>
<TR>
<TD>多消息队列</TD>
<TD>每个创建窗口的线程拥有独立的消息队列</TD>
<TD>只有一个消息队列。所有窗口共享一个消息队列。除非嵌套消息循环,否则一个程序中只有一个消息循环。</TD></TR>
<TR>
<TD>内建多线程处理</TD>
<TD>是。可以自动处理跨线程的消息传递</TD>
<TD>不能。从一个线程向另外一个线程发送或者邮寄消息时,必须通过互斥处理保护消息队列。</TD></TR>
<TR>
<TD>其他</TD>
<TD>可以利用 PostSyncMessage 函数跨线程发送消息,并等待消息的处理结果</TD>
<TD>不能使用 PostSyncMessage、SendAsynMessage</TD></TR></TBODY></TABLE>
<P><B>5 窗口的建立和销毁</B></P>
<P><B>5.1 窗口的建立<BR></B> 我们知道,MiniGUI 的 API 类似
Win32 的 API。因此,窗口的建立过程和 Windows 程序基本类似。不过也有一些差别。首先我们回顾一下 Windows
应用程序的框架: </P>
<P> 在 WinMain ()
中创建窗口,使用以下步骤:创建窗口类、登记窗口类、创建并显示窗口、启动消息循环。 </P>
<P> 在 WndProc () 中,负责对发到窗口中的各种消息进行响应。<BR>在 MiniGUI
中也同样要有这两个函数。不过稍微有点不同。程序的入口函数名字叫MiniGUIMain
(),它负责创建程序的主窗口。在建立主窗口之后,程序进入消息循环。</P>
<P> 在 Win32
程序中,在建立一个主窗口之前,程序首先要注册一个窗口类,然后创建一个属于该窗口类的主窗口。MiniGUI
却没有在主窗口中使用窗口类的概念。在 MiniGUI 程序中,首先初始化一个 MAINWINCREATE
结构,该结构中元素的含义是:</P>
<P> CreateInfo.dwStyle: 窗口风格 <BR>CreateInfo.spCaption:
窗口的标题 <BR>CreateInfo.dwExStyle :
窗口的附加风格 <BR>CreateInfo.hMenu:
附加在窗口上的菜单句柄 <BR>CreateInfo.hCursor:
在窗口中所使用的鼠标光标句柄 <BR>CreateInfo.hIcon:
程序的图标 <BR>CreateInfo.MainWindowProc:
该窗口的消息处理函数指针 <BR>CreateInfo.lx:
窗口左上角相对屏幕的绝对横坐标,以象素点表示 <BR>CreateInfo.ty:
窗口左上角相对屏幕的绝对纵坐标,以象素点表示 <BR>CreateInfo.rx:
窗口的长,以象素点表示 <BR>CreateInfo.by:
窗口的高,以象素点表示 <BR>CreateInfo.iBkColor:
窗口背景颜色 <BR>CreateInfo.dwAddData: 附带给窗口的一个 32
位值 <BR>CreateInfo.hHosting: 窗口消息队列所属 </P>
<P>其中有如下几点要特别说明: </P>
<OL>
<LI>CreateInfo.dwAddData:在程序编制过程中,应该尽量减少静态变量,但是如何不使用静态变量而给窗口传递参数呢?这时可以使用这个域。该域是一个
32 位的值,因此可以把所有需要传递给窗口的参数编制成一个结构,而将结构的指针赋予该域。在窗口过程中,可以使用
GetWindowAdditionalData 函数获取该指针,从而获得所需要传递的参数。
<LI>CreateInfo.hHosting:该域表示的是将要建立的主窗口使用哪个主窗口的消息队列。使用其他主窗口消息队列的主窗口,我们称为"被托管"的主窗口。当然,这只在
MiniGUI-Threads 版本中有效。
<LI>MainWinProc
函数负责处理窗口消息。这个函数就是主窗口的"窗口过程"。窗口过程一般有四个入口参数,第一个是窗口句柄,第二个是消息类型,第三个和第四个是消息的两个参数。
</LI></OL>
<P> 在准备好MAINWINCREATE 结构之后,就可以调用 CreateMainWindow
函数建立主窗口了。在建立主窗口之后,典型的程序将进入消息循环。如下所示: </P>
<DIV align=center>
<TABLE cellSpacing=1 width="90%" border=0>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0>int MiniGUIMain (int args,
const char* arg[])<BR>{<BR>MSG Msg;<BR>MAINWINCREATE
CreateInfo;<BR>HWND hWnd;<BR><BR>// 初始化 MAINWINCREATE
结构<BR>CreateInfo.dwStyle = WS_VISIBLE | WS_VSCROLL |
WS_HSCROLL | WS_CAPTION;<BR>CreateInfo.spCaption= "MiniGUI
step three";<BR>CreateInfo.dwExStyle =
WS_EX_NONE;<BR>CreateInfo.hMenu =
createmenu();<BR>CreateInfo.hCursor =
GetSystemCursor(0);<BR>CreateInfo.hIcon =
0;<BR>CreateInfo.MainWindowProc =
MainWinProc;<BR>CreateInfo.lx = 0;<BR>CreateInfo.ty =
0;<BR>CreateInfo.rx = 640;<BR>CreateInfo.by =
480;<BR>CreateInfo.iBkColor =
COLOR_lightwhite;<BR>CreateInfo.dwAddData =
0;<BR>CreateInfo.hHosting = HWND_DESKTOP;<BR><BR>//
建立主窗口<BR>hWnd = CreateMainWindow(&CreateInfo);<BR>if (hWnd
== HWND_INVALID)<BR>return 0;<BR><BR>// 显示主窗口<BR>ShowWindow
(hWnd, SW_SHOWNORMAL);<BR><BR>// 进入消息循环<BR>while
(GetMessage(&Msg, hWnd)) {<BR>TranslateMessage
(&Msg);<BR>DispatchMessage(&Msg);<BR>}<BR><BR>MainWindowThreadCleanup
(hWnd);<BR>return 0;<BR>}</TD></TR></TBODY></TABLE></DIV>
<P> 注意,和 Windows 程序不同的是,在退出消息循环之后,还要调用一个函数,即
MainWindowThreadCleaup 函数。该函数的工作是销毁主窗口的消息队列,一般在线程或者进程的最后调用。</P>
<P><B>5.2 窗口的销毁 <BR></B> 要销毁一个主窗口,可以利用
DestroyMainWindow (hWnd)
函数。该函数将销毁主窗口,但不会销毁主窗口所使用的消息队列,而要使用MainWindowThreadCleaup
最终清除主窗口所使用的消息队列。</P>
<P> 一般而言,一个主窗口过程在接收到 MSG_CLOSE 消息之后会销毁主窗口,并调用
PostQuitMessage 消息终止消息循环。如下所示: </P>
<DIV align=center>
<TABLE cellSpacing=1 width="90%" border=0>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0>case MSG_CLOSE:<BR>//
销毁窗口使用的资源<BR>DestroyLogFont (logfont1);<BR>DestroyLogFont
(logfont2);<BR>DestroyLogFont (logfont3);<BR><BR>//
销毁子窗口<BR>DestroyWindow(hWndButton);<BR>DestroyWindow(hWndEdit);<BR>//
销毁主窗口<BR>DestroyMainWindow (hWnd);<BR>// 发送 MSG_QUIT
消息<BR>PostQuitMessage(hWnd);<BR>return
0;</TD></TR></TBODY></TABLE></DIV>
<P><B>6 几个重要消息 <BR></B>
在窗口(包括主窗口和子窗口在内)的生存周期当中,有几个重要的消息需要仔细处理。下面描述这些消息的概念和典型处理。<BR><BR><B>6.1
MSG_NCCREATE <BR></B> 该消息在 MiniGUI
建立主窗口的过程中发送到窗口过程。lParam 中包含了由 CreateMainWindow 传递进入的 pCreateInfo
结构指针。您可以在该消息的处理过程中修改 pCreateInfo 结构中的某些值。</P>
<P><B>6.2 MSG_SIZECHANGING <BR></B>
该消息窗口尺寸发生变化时,或者建立窗口时发送到窗口过程,用来确定窗口大小。wParam 包含预期的窗口尺寸值,而 lParam
用来保存结果值。MiniGUI 的默认处理是, </P>
<DIV align=center>
<TABLE cellSpacing=1 width="90%" border=0>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0>case
MSG_SIZECHANGING:<BR>memcpy ((PRECT)lParam, (PRECT)wParam,
sizeof (RECT));<BR>return 0;</TD></TR></TBODY></TABLE></DIV>
<P> 你可以截获该消息的处理,从而让即将创建的窗口位于指定的位置,或者具有固定的大小,比如在
SPINBOX 控件中,就处理了该消息,使之具有固定的大小: </P>
<DIV align=center>
<TABLE cellSpacing=1 width="90%" border=0>
<TBODY>
<TR>
<TD width="100%" bgColor=#c0c0c0>case
MSG_SIZECHANGING:<BR>{<BR>const RECT* rcExpect = (const RECT*)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -