网站首页 > 博客文章 正文
2.1.3 窗口
Windows窗口是用来和用户进行交互信息的,可以在窗口中输入信息,也可以在窗口中输出信息。这些信息包括文本、图形、动画等等。对比控制台黑窗口,Windows窗口可以输入和输出的信息要丰富的多。
■自定义窗口
窗口就是对象,作为程序员来说,就是一块内存。这块内存中通常包含一个标题栏、一个菜单栏、一个工具栏和一个滚动条等其他小的对象(更小的一块内存)。通常我们会使用窗口对象的句柄对窗口进行操作。
如果我们要创建一个窗口,需要告诉Windows系统一些明确的信息:
typedef struct tagWNDCLASSA {
UINT style; //创建一个什么样风格的窗口
WNDPROC lpfnWndProc; //由谁来负责处理窗口的消息—窗口过程
int cbClsExtra; //要根据窗口类结构分配的额外字节数
int cbWndExtra; //在窗口实例之后分配的额外字节数
HINSTANCE hInstance; //窗口所属的进程
HICON hIcon; //窗口的图标
HCURSOR hCursor; //窗口的鼠标
HBRUSH hbrBackground; //窗口的背景颜色
LPCSTR lpszMenuName; //窗口所包含的菜单
LPCSTR lpszClassName; //窗口类名(等同于进程名)
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;
显然,将创建窗口的所有信息组合到一起就是一个结构体。细心的读者可能会发现,创建窗口的信息还应该包含窗口的尺寸。这是一定需要的,我们将在下一节中使用示例代码来说明。
■预定义对话框窗口
Windows还预定义另一种窗口,可以不带标题栏,而是包含各种各样的控件,例如按钮、列表框、滚动条和文本框等。
对话框窗口可以分为:
●模态对话框:当程序显示一个模态对话框时,用户不能在对话框和该程序的其他窗口之间进行切换。用户必须先明确地终止该对话框。这通常由单击OK或Cancel按钮来实现。但是当对话框正在显示时,用户可以切换到其他的程序。有些对话框(所谓“系统模态”)则连这种切换都不允许。在Windows 中,用户必须先结束系统模态对话框才可以进行其他操作。典型的默认对话框就是MessageBox框了。
●非模态对话框:非模态对话框允许用户在对话框和窗口之间,以及在对话框和其他程序之间进行切换。非模态对话框因此更接近于程序自定义的正常弹出窗口。如果在一段时间内,一直要显示某个对话框,用户更倾向于使用非模态对话框,因为它更方便。典型的非模态对话框如查找和替换对话框。
●公用对话框:为了方便用户快速熟悉和使用Windows应用程序,从Windows 3.1开始,提供了统一标准的“公用对话框库”(common dialogue box library)。这个库包括了一些函数,可以用来激活打开和存储文件、查找和替换、选择颜色、选择字体(所有这些将在本书第十一章中演示)以及打印(将在第十三章中演示)的对话框。
■预定义子窗口控件
子窗口控件也是一种窗口,也是Windows预定义的窗口类型。虽然我们也可以自定义子窗口控件,但是直接使用Windows提供的标准子窗口控件更为方便快捷。Windows预定义了子窗口控件的窗口类和窗口过程,并且提供多种不同风格的子窗口样试供我们选择。常用的子窗口控件包括按钮类的BUTTON控件、静态文本控件、滚动条控件、文本编辑控件和列表框控件。我们将在第八章详细讲解子窗口控件。
2.1.4 消息
Windows操作系统是一种消息驱动的系统,这句话应该被每一位从事Windows程序设计的开发人员所熟知。那么什么是消息呢?也许有很多已经从事多年Windows程序开发的程序员都无法说清楚。这是因为他们并不清楚消息传递的实现过程。
■消息的本质
消息其实非常简单,就是函数调用时传递的参数,仅此而已。只不过我们并不能从代码中真实的感受到消息传递的整个过程,所以会觉得不可琢磨。Windows操作系统中有很多很多消息,这些消息可以是键盘消息、鼠标消息、滚动条消息、字符消息、绘图消息、控件消息、菜单消息或者是系统消息等等。键盘消息和鼠标消息肯定是由用户操作键盘和鼠标产生的,这是人机交互的需要。其他消息又是如何产生的呢?如果我们把消息看作是参数,那么这些消息肯定是在调用函数调用传递参数时产生的,消息本身就是参数。例如绘图时,我们将绘图消息WM_PAINT作为实参传递给GDI绘图函数。不论什么样的消息都是由操作系统捕捉并分发的。API函数的调用和执行也是由Windows操作系统实现的,这些都是潜藏在水面以下的冰山。正是因为我们看不见,所以才会感到困惑。
既然消息就是一个参数,我们来了解一下消息本身:
typedef struct tagMSG {
HWND hwnd; //接收消息的窗口的句柄
UINT message; //指定消息类型-消息ID,例如 WM_PAINT、WM_CLOSE等。
WPARAM wParam; //针对特定消息的附加信息
LPARAM lParam; //详细的附加信息,通常与wParam一起配合使用
DWORD time; //消息发布的时间
POINT pt; //鼠标在发布消息的时候在屏幕上的位置(鼠标消息特性)
DWORD lPrivate; //仅在某些消息类型中使用,其他情况下是保留项
} MSG, *PMSG, *NPMSG, *LPMSG;
我们用一个结构体来描述消息,显然消息是一个对象(Windows系统处处是对象)。消息结构体中包含7个成员,我们只需要关系前面四个成员,后面三个成员仅提供给操作系统使用。
hwnd:接收消息的窗口的句柄,所有的消息都是发送给指定窗口的。
message:指定消息类型-消息ID,Windows系统使用一个32位无符号整数标识消息。
wParam 和lParam:针对特定消息的附加信息。仅有一个消息ID远远不够的,还需要借助于32位的wParam 和32位的lParam传递具体的信息。
我们在Windows程序中通常会将上述四个成员作为参数传递。
举例
例如发送一个消息:
LRESULT SendMessage(
[in] HWND hWnd, //接收消息的窗口句柄
[in] UINT Msg, //消息ID
[in] WPARAM wParam, //附加参数,其他的消息特定信息
[in] LPARAM lParam //附加参数,其他的消息特定信息
);
很显然,这里的消息就是参数。在Windows系统内部产生和传递消息也是如此。
Windows系统中的消息虽然非常多,但是绝大多数消息都是由Windows系统按照默认方式处理的,不需要我们劳心费力,目的当然是不想为难我们这些开发者了。
■窗口过程
在Winodws程序中,我们需要写一个函数来处理各种消息,这个函数称为窗口过程。它是一个回调函数。正常情况下,在Windows程序中是由我们来调用操作系统的API函数,回调的意思是由操作系统反过来调用我们写的窗口过程来处理消息。窗口过程只需要处理我们关心的一小部分消息,绝大多数消息都是交给默认的Windows系统的窗口过程来处理。本书的学习过程也就是学习各种各样消息处理的过程。
■队列消息
Windows操作系统的消息可以分为队列消息和非队列消息。Windows操作系统中有一个总消息队列,所有进程的队列消息都会被送入总消息队列中。然后再根据消息所属的窗口,将消息分发到各个窗口队列。还记得消息结构的第一个成员就是接收消息的窗口。以此判断该消息属于哪个窗口。
什么样的消息属于队列消息呢?鼠标消息、键盘消息肯定是属于队列消息,一定会被送入消息队列。这是由于用户通过鼠标、键盘与窗口进行交互产生的消息,而用户的操作是有先后顺序的,因此需要使用队列进行同步操作。因此可以认为,凡是与窗口进行交互或需要保持同步操作的消息都是队列消息,一定会被送上消息队列。队列消息都是调用PostMessage函数将消息送入指定窗口的消息队列。
BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
消息队列存在于线程中,理论上每个线程都可以创建一个消息队列。
假如一个Windows进程只有一个主线程,那么非常简单,所有的窗口消息都是送入主线程的消息队列。
假如一个Windows进程同时拥有多个线程,其中只有一个主线程负责处理窗口消息,其它线程执行其他任务。意思是在多线程的Windows程序中,同样只有一个消息队列,所有消息都交给主线程处理,不存在多个线程间的冲突问题。因此,虽然窗口是不包含消息队列的,但是我们将线程消息队列称为窗口消息队列也没什么错。
在Windows程序设计中,需要遵循“十分之一秒原则”。“十分之一秒原则”是指用户在做出操作后,至少应在0.1秒之内得到某种形式的反馈,这样才能让用户感觉到系统在对他的操作给予响应。如果一个任务执行时间超过0.1秒,我们为了良好的用户体验,就需要考虑新建一个线程来单独处理这个任务。我们将在本书的第二十章详细讲解进程与线程。
windows应用程序中一般都包含一小段称为“消息循环”的代码,该段代码用于从消息队列中检索消息,并将其分发给相应的窗口过程。其他非队列消息则不经过消息队列直接发送给窗口过程。
■非队列消息
除了队列消息之外,剩下的消息就都属于非队列消息了。不需要被送入消息队列的消息可以通过SendMessage函数将消息直接发送给负责处理消息的窗口过程。
在Windows程序设计中,SendMessage函数被用于将一个消息发送给指定窗口或线程,它会等待消息处理完毕才会返回,也就是说它是一个同步或者说阻塞的函数。这是一个非常重要的用来在窗口间或线程间通信的函数。SendMessage函数可以在同一个线程内、同一个进程内的线程间或者不同进程的线程间通过消息进行通信。
下面是SendMessage函数的定义:
LRESULT SendMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
SendMessage函数应该是使用最频繁的一个函数了,在后面的章节中我们将深有体会。
猜你喜欢
- 2024-12-02 学习Windows消息机制
- 2024-12-02 iframe嵌入页面实现免登录
- 2024-12-02 简单易懂的大文件上传教程:Vue3与.NET 6完美结合
- 2024-12-02 高频面试题:JavaScript事件循环机制解析
- 2024-12-02 大文件上传实践分享
- 2024-12-02 如何解决iframe跨域问题?这些解决方案可以有效满足你的业务场景
- 2024-12-02 win32编程 -- 消息机制(一)
- 2024-12-02 HttpClient使用指南——POST请求
- 2024-12-02 最新HTML BroadcastChannel API简介
- 2024-12-02 在C#中使用 SendMessage 实现在操作外部其他程序上的控件教程
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)