应用程序框架设计(2):SW系统的窗口类

win32-注册窗口类、创建窗口 阅读详情

解释了消息分派机制后,接下来我们开始介绍WINX的窗口类。为了产生比较的效果,我决定从之前我写的“SW系统”的窗口类讲起。在你理解了SW系统的窗口类后,我们再来看6年后WINX中的窗口类在设计上发生了什么样的变化。——这自然也是我个人在窗口类观念上的改变。

1、SW系统的“Hello,World!”程序

#define Uses_SApp
#include <sw.h>
// SW系统中,你需要记住头文件只有 ,它是SW系统总控文件。
// 你只需要告诉它,你用了什么,它可以为你检测需要的头文件并包含它们。
// 例如,这里我们用了SApp类,故有 #define Uses_SApp 一句。

class SHelloApp : public SApp
{
public:
 void OnDraw(SHDC dc);
};

void SHelloApp::OnDraw(SHDC dc)
{
 dc.TextOut(1, 1, _T(“Hello, World!”));
}

// SW系统没有封装WinMain函数,这应该是一个好消息吧?
// 你不必对主函数是main()而非是WinMain太过关心。它只是为了与DOS
// 习惯兼容以及书写的方便而实现的宏而已。
int main()
{
 return Desktop.Execute(new SHelloApp);
 // Desktop就是Windows系统桌面,它是一个特殊的窗口对象。
 // 特别注意,SW系统中App类是一个窗口!
}

2、SW系统的类体系图

 


类说明:
 SHWnd:对Windows窗口句柄的简单包装
 SObject:类库的根
 SString:字符串类
 SArchive:文档类,用于存盘
 SHxxxCtrl:各种控件
 SWnd:窗口类基本类
 SHDC:对DC句柄的简单包装
 SMsg:消息类
 SSubclass:派生子类
 SDlg:对话框类
 SApp:应用程序类(SDI)
 SMDIChild:MDI程序文档窗口
 SMDIApp:应用程序类(MDI)

注:SW系统中对话框不分模态/非模态,只有在调用时才有区别。

如果希望是模态的:
 hWndParent.ExecDlg(new SxxxDlg); // 调用DialogBoxParam
或:
 hWndParent.Execute(new SxxxDlg, SW_SHOW);
  // 使用SW系统的消息循环

如果希望是非模态的:
 hWndParent.Insert(new SxxxDlg);
  // 像普通窗口一样插入到父窗口中即可

3、独特的窗口(视图)模型

注: 请注意术语上的变化,由于SW系统在Windows下实现,使用了Windows中的一些术语。在这里视图被称为窗口,事件被称为消息。

窗口应该具备哪些行为才合理?象显示/隐藏、选择(激活)、移动、改变大小、关闭等这些基本行为是很容易想到的。SW系统与经典应用程序框架如MFC不同的是,它引入了另两个非常有用的方法:

1)Insert操作:

SW系统中,窗口都通过Insert操作插入到父窗口中。

2)Execute操作:

SW系统中,可以在一个窗口A中运行(Execute)另一个窗口B。此时,窗口A作为窗口B的父窗口,已经不再接受消息,即窗口B是模态视图。不过,在窗口A是Desktop(这里Desktop是Windows系统桌面)时稍微有点不同。这是由于我们使用得是多任务系统所致。

在SW系统中,应用程序是一个窗口,由SWnd类派生;这不同于MFC,在那里应用程序是一个线程,由CWinThread类派生。这一点也许MFC的设计可能严谨一些,但是SW系统的做法的优点是,程序的逻辑变得相当简单。SW系统如何运行程序?非常清楚,只要一句话:
  Desktop.Execute(new SxxxApp);
这个我们在例子程序中已经看到了。

Execute操作很复杂吗?打开SW系统的源代码,你可能很失望,代码只有几句话:

UINT SHWnd::Execute(SWnd *pWnd, int nCmdShow)
{
 if (this->Insert(pWnd))
 {
  pWnd->Show(nCmdShow);
  return pWnd->MsgLoop();
 }
 return –1;
}

很显然,Desktop.Execute(new SxxxApp)的含义是:

1)将应用程序插入(Insert)到Desktop;
2)显示应用程序;
3)进入消息循环;

Insert操作又做了什么?它的代码就相对复杂,且与Windows系统密切相关,这里不具体写出它的代码。它完成的工作主要是:

1)检测窗口类是否已经注册;如果没有,注册窗口类;
2)调用窗口创建函数创建窗口对象;
3)由事件发送器将WM_CREATE发送给窗口对象;

消息循环(MsgLoop)的实现流程我们一开始就已经给出,即“事件驱动”应用中的消息流动过程。我们也提到其中事件发送器Windows只实现了一部分。现在问题的关键在于,如何将消息由窗口过程发送给具体的窗口对象?

MFC采用一张Hash表将窗口句柄与窗口实例指针关联起来。在这一点上SW系统采取了一个取巧的办法,避免去维护一张Hash表。它利用窗口句柄的USERDATA字节保存对象指针。具体代码如下:

// 为了突出问题的关键,本文的代码一般比较简略,省略了没有大多数的出错
// 处理。作为实际的程序,其风格当然不应该这样。
LONG CALLBACK WndProc(
 HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 SWnd* pWnd = (SWnd*)GetWindowLong(hwnd, GWL_USERDATA);
 if (pWnd)
 {
  SMsg ev;
  ev.uMsg = uMsg;
  ev.lParam = lParam;
  ev.wParam = wParam;
  return pWnd->HandleMsg(ev);
 }
 if (uMsg == WM_CREATE)
 {
  pWnd = (SWnd*)((CREATESTRUCT*)lParam)->lpCreateParams;
  // 这是从CreateWindow函数传进来的窗口实例指针!
  pWnd->m_hWnd = hwnd;
  SetWindowLong(hwnd, GWL_USERDATA, (LONG)pWnd);
  return pWnd->OnCreate() ? 0 : -1;
 }
 return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}

在这个技巧中存在的问题是,所有在WM_CREATE之前的消息被“遗失”了。解决这个限制的方案是简单的。核心思想是用全局变量传递窗口实例指针,而不是在CreateWindow函数的参数中传递。见下:

创建窗口代码:
 LOCK();
 x_pThisWnd = 窗口实例指针;
 调用CreateWindow创建窗口;
 UNLOCK();

窗口过程代码:
 LONG CALLBACK WndProc(
   HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
  SWnd* pWnd = (SWnd*)GetWindowLong(hwnd, GWL_USERDATA);
  if (!pWnd)
  {
   pWnd = x_pThisWnd;
   pWnd->m_hWnd = hwnd;
   SetWindowLong(hwnd, GWL_USERDATA, (LONG)pWnd);
  }
  SMsg ev;
  ev.uMsg = uMsg;
  ev.lParam = lParam;
  ev.wParam = wParam;
  return pWnd->HandleMsg(ev);
 }

这里LOCK()/UNLOCK()是多线程互斥代码。由于使用了全局变量,线程互斥是必要的。

可以看到,所有的消息都是经由HandleMsg处理的。这是一个进步。但是这中间还有一个不妥当的地方。事实上我们已经习惯于在WM_CREATE消息中对窗口实例进行初始化。因此在WM_CREATE之前的消息处理时,对象其实还没有初始化完毕。这一点如果被遗忘就会犯错。而且,从概念上说,初始化总应该是对象接收到第一个消息。正是由于这一点,SW系统决定仍然使用原先的方案。 

更改标题栏背景并重写JFrame窗口 对JFrame窗口进行了重写 最大最小化按钮可以改变(我用的是qq上的最大最小化按钮图标) 对JFrame进行更改 得到了自己想要的窗口 本窗口是用来做局域聊天窗口特别设计的 详细 内容 代码都有详细注释谢谢下载 立即下载

相关推荐

solidworks弹窗宏

solidworks弹窗宏适用于solidworks弹簧拉伸、弹簧制作

Solidworks窗口程序

所谓Solidworks窗口程序,即区别于插件,独立运行的程序,当然这只是我自己的命名。相较于插件程序的种种问题,窗体程序对于入门者来说,要友善很多,也容易很多,我常用来做调试,不用像插件程序每次启动Solidworks。当然窗口程序也有一些缺陷,比如因为是跨线程操作,速度慢要一点。 基本步骤如下: 建立一个WinForm项目: 1、添加如下引用: ...

长风破浪会有时 2487

java中JFrame布局

java中JFrame的几种布局,流布局,网格布局,卡片布局,边框布局

Android display架构分析二-SW架构分析

下面简单介绍一下上图中的各个Layer: *蓝色部分-用户空间应用程序 应用程序层,其中包括Android应用程序以及框架和系统运行库,和底层相关的是系统运行库,而其中和显示相关的就是Android的Surface Manager, 它负责对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。 *黑色部分-HAL层,在2.2.1部分会有介绍 *红色部分-L

fengyuxing168的专栏 1630

继承自JFrame类的窗口应用程序

编写继承自JFrame类的窗口应用程序,其内容面板的布局为边框布局,界面显示效果如图所示。窗口的南边放置一个按钮。窗口中间使用JSplitPane类实现水平分割效果:左边是一个面板(流式布局),其中放置5个按钮;右边是一个面板(边框布局),面板东、南、西、北各放置1个按钮,面板中间放一个文本区。

IDM更改下载路径的详细指南

Internet Download Manager(IDM)是一款功能全面的下载加速工具,它支持多种浏览器和协议,能够显著提升下载速度,同时提供断点续传、多线程下载等实用功能。IDM的用户界面简洁明了,操作便捷,使得用户能够轻松管理下载任务。此外,IDM还支持自动分类下载文件、定时下载等高级功能,进一步提升了用户的下载体验。通过本文的介绍,相信用户已经掌握了如何在IDM中更改下载路径的方法。这一功能不仅提升了下载效率和文件管理便捷性,还满足了用户的个性化需求。

nntxthml的博客 1763

应用程序框架设计(1):SW系统简介

《应用程序框架设计》是我大学毕业时(2000年)写的毕业论文。在我给公司内部作“应用程序架构”方面的讲座时,曾经作为入门级的参考资料附上。后来不知如何就流传到Internet上,不过是不完整的版本(可尝试在Google中搜索“应用程序框架设计:SW系统”)。回头看这篇文字,最大的感受觉得自己的文字功底是越来越退步了:-) 由于与界面库有关,大家不妨看看。应用程序框架设计许式伟2000年6月一、摘要

QBox/Golang/CERL/StdExt/TPL/WinxGui - 许式伟的专栏 6184

基础矩阵、本质矩阵、单应矩阵

基础矩阵是一个 3×3 的秩为2的矩阵,用于描述两个不同视图(或称为图像)之间对应点的几何约束。更具体地说,如果你有两幅图像,分别从两个不同的相机或者相机位置拍摄,那么基础矩阵可以用于找到一个图像中的点在另一个图像中的对应点。本质矩阵也是一个 3×3 的秩为2的矩阵,与基础矩阵非常类似。当两个视图(图像)之间的场景是一个平面或者两个视图之间仅有旋转和平移时,可以使用单应矩阵来关联这两个视图。这三个矩阵都是在计算机视觉中广泛应用的基础工具,但它们适用于不同类型的视图和场景。给定两个图像中的一对对应点。

Gloamirror的博客 1330

应用程序框架设计(一)

应用程序框架设计许式伟一、摘要    随着面向对象技术的发展成熟,已经出现了许多著名的应用程序框架,如在Windows平台下有MFC、VCL、OWL等;在旧的DOS系统下有Turbo Vision。在这里我希望通过我设计的“SW系统”来阐述我对设计应用程序框架一些想法。其中涉及的内容主要有:1、   应用程序框架设计的基本内容这一部分主要是讨论应用程序框架的必要性、可行性,以及

湘程国际集团——技术日记 6076

告别Keil?在Mac上配置STM32CubeIDE + CMSIS-DAP调试器全攻略(含常见报错解决)

本文详细介绍了在Mac平台上配置STM32CubeIDE与CMSIS-DAP调试器的全流程,包括硬件准备、软件安装、工程创建及调试优化。通过实战指南,帮助开发者高效完成STM32开发,解决常见报错问题,提升开发效率。

weixin_30485379的博客 434

linux 内核vxlan收发包流程

1.vxlan口收包处理流程 前面几章详细介绍了vxlan接口的使用,而且已经知道vxlan是 MAC IN UDP中的封装,因此,在解封装之前,一切按照原有流程走,在此复习一下内核收发包流程(驱动层的数据处理这次不再解析,直接从__netif_receive_skb_core开始) #物理网卡处理中断,触发softirq i40e_intr └─napi_schedule_irqoff #i40e softirq把包放到per cpu backlog,触发softirq i40e_napi_p

魏言华的博客 3826

SolidWorks插件SW2URDF使用

总结了一下个人在使用solidworks插件SW2URDF过程中的步骤和要注意的问题

chengt0312的博客 1万+

海康摄像头RTSP流延迟优化实战:从VLC到OpenCV的5种方案对比与性能调优

本文针对海康摄像头RTSP流的高延迟问题,深入剖析了延迟根源,并系统对比了从VLC、OpenCV到海康官方SDK、FFmpeg及硬件加速等五种主流方案的性能表现。重点分享了缓冲区设置、协议优化、多线程架构与硬件解码等实战调优技巧,旨在帮助开发者将视频流延迟从秒级优化至毫秒级,满足工业检测、安防监控等高实时性场景需求。

hh234的博客 809
上一篇: 应用程序框架设计(3):RuntimeClass与序列化
下一篇: 剖析WINX的Hello程序
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值