2501,三个子类化窗口函数

MFC(VC6.0)的CWnd其子类中,有如下三个函数:

//从`VS`安装路径`VC98MFCIncludeAFXWIN.H`
  class  CWnd :  public  CCmdTarget
 {
    ...
public:
    ...
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual void PreSubclassWindow();
    BOOL SubclassWindow(HWND hWnd);
    ...
} ;

让人很不容易区分,不知道它们究竟干了些什么,在何时要重写哪个函数?

想知道重写函数?
根据有无关键字,在排除了SubclassWindow后,也就知道可重写预创建窗口预子类化窗口函数.

先看看对这三个函数,MSDN给的解释:
预创建窗口:
在创建窗口附加指针所指的CWnd对象前,由框架调用.
预子类化窗口:
子类化窗口框架调用它,用来允许其它必要子类化.

要理解附加,必须要知道一个C++CWnd对象和窗口(窗口)的区别:窗口就是实在的窗口,而CWnd就是MFC用类对窗口C++封装.

附加,就是在CWnd对象上附加窗口.附加(附加)完成后,CWnd对象才和窗口有了关联.窗口的子类化是指修改窗口过程,而不是面向对象中的继承子类.

创建窗口前,框架调用预创建窗口,框架子类化窗口前,调用预子类化窗口.
来看看MFC中的这三个函数的实现!

    //从`VSInstallPathVC98MFCSRCWINCORE.CPP`
 BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
 LPCTSTR lpszWindowName, DWORD dwStyle,
  int  x,  int  y,  int  nWidth,  int  nHeight,
 HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
     {
    //允许修改几个常见的`创建`参数
     CREATESTRUCT cs;
     cs.dwExStyle = dwExStyle;
     cs.lpszClass = lpszClassName;
     cs.lpszName = lpszWindowName;
     cs.style = dwStyle;
     cs.x = x;
     cs.y = y;
     cs.cx = nWidth;
     cs.cy = nHeight;
     cs.hwndParent = hWndParent;
     cs.hMenu = nIDorHMenu;
     cs.hInstance = AfxGetInstanceHandle();
     cs.lpCreateParams = lpParam;
     if (!PreCreateWindow(cs))
     {
      PostNcDestroy();
      return FALSE;
     }
     AfxHookWindowCreate(this);
     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
       cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
       cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
        ...
     return TRUE;
    }
    //对子窗口
     BOOL CWnd::PreCreateWindow(CREATESTRUCT &  cs)
     {
     if (cs.lpszClass == NULL)
     {
    //确保已注册默认`窗口`类
      VERIFY(AfxDeferRegisterClass(AFX_WND_REG));
    //未提供`窗口类`,使用子窗口默认值
      ASSERT(cs.style & WS_CHILD);
      cs.lpszClass = _afxWnd;
     }
     return TRUE;
    }

CWnd::CreateEx先设置cs(CREATESTRUCT),在调用真正的创建窗口函数::CreateWindowEx前,并按引用传递cs参数,来调用了CWnd::PreCreateWindow函数.

CWnd预创建窗口函数,也只是给cs.lpszClass赋值而已.毕竟,创建窗口函数CWnd::CreateEx诸多参数中,并没有哪个指定要创建窗口的窗口类.

所以修改窗口的大小,风格,窗口所属的窗口类cs成员变量时,要重写预创建窗口函数.

//从`VSInstallPathVC98MFCSRCWINCORE.CPP`
 BOOL CWnd::SubclassWindow(HWND hWnd)
 {
 if (!Attach(hWnd))
  return FALSE;
//允许其他子类化
 PreSubclassWindow();
//现在勾挂到`AFXWndProc`
 WNDPROC* lplpfn = GetSuperWndProcAddr();
 WNDPROC oldWndProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)AfxGetAfxWndProc());
 ASSERT(oldWndProc != (WNDPROC)AfxGetAfxWndProc());
 if (*lplpfn == NULL)
  *lplpfn = oldWndProc;
//创建该类型的第一个控件
#ifdef _DEBUG
 else if (*lplpfn != oldWndProc)
 {
  ...
  ::SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
 }
#endif
 return TRUE;
}
 void  CWnd::PreSubclassWindow()
 {
//无默认处理
}

CWnd::SubclassWindow先调用Attach(hWnd)函数,来关联CWnd对象和窗柄所指的窗口.接着再用::SetWindowLong修改窗口过程(子类化)前,调用了PreSubclassWindow.
CWnd::PreSubclassWindow则是闲着.

在实现CWnd中,除了CWnd::SubclassWindow会调用预子类化窗口外,还有一处.上面所列CreateEx函数的代码,其中调用了一个AfxHookWindowCreate函数,见下面代码:

//从`VSInstallPathVC98MFCSRCWINCORE.CPP`
 BOOL CWnd::CreateEx(...)
 {
//允许修改几个常见的`创建`参数
    ...
 if (!PreCreateWindow(cs))
 {
  PostNcDestroy();
  return FALSE;
 }
 AfxHookWindowCreate(this);
 HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
   cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
   cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
    ...
 return TRUE;
}

接着查看AfxHookWindowCreate的代码:

//从`VSInstallPathVC98MFCSRCWINCORE.CPP`
  void  AFXAPI AfxHookWindowCreate(CWnd *  pWnd)
 {
 ...
 if (pThreadState->m_hHookOldCbtFilter == NULL)
 {
  pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
   _AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
  if (pThreadState->m_hHookOldCbtFilter == NULL)
   AfxThrowMemoryException();
 }
 ...
}

起主要作用的::SetWindowsHookEx函数,用来设置一个勾挂函数(勾挂函数)_AfxCbtFilterHook,每当窗口产生一个窗口时,就会调用你设置的勾挂函数.

这样设置完成后,回到CWnd::CreateEx函数中,执行::CreateWindowEx创建窗口,窗口一产生,就会调用上面设置的勾挂函数_AfxCbtFilterHook.
而正是在_AfxCbtFilterHook中,第二次调用预子类化窗口函数.见如下代码:

//来自`VSInstallPathVC98MFCSRCWINCORE.CPP/Window`创建勾挂
LRESULT CALLBACK
_AfxCbtFilterHook( int  code, WPARAM wParam, LPARAM lParam)
 {
        ...
             ...
//连接`窗柄`到`pWndInit...`
  pWndInit->Attach(hWnd);
//允许首先其他子类化
    pWndInit->PreSubclassWindow();
            ...
    {
        //用标准`AfxWndProc`,子类化窗口
        oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)afxWndProc);
        ASSERT(oldWndProc != NULL);
        *pOldWndProc = oldWndProc;
    }
       ...
}

也在调用SetWindowLong函数,子类化窗口,前调用了预子类化窗口.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958601.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

校验收货地址是否超出配送范围实战3(day09)

优化用户下单功能,加入校验逻辑,如果用户的收货地址距离商家门店超出配送范围(配送范围为5公里内),则下单失败。 提示: ​ 1. 基于百度地图开放平台实现(https://lbsyun.baidu.com/&#xff09…

Linux应用编程(五)USB应用开发-libusb库

一、基础知识 1. USB接口是什么? USB接口(Universal Serial Bus)是一种通用串行总线,广泛使用的接口标准,主要用于连接计算机与外围设备(如键盘、鼠标、打印机、存储设备等)之间的数据传输和电…

【优选算法】7----三数之和

来了来了,他来了,又是学习算法的一天~ 今天的嘉宾是中等难度的算法题----三数之和! ------------------------------------------begin------------------------------------ 题目解析: 哇趣!又是给了一个数组&#…

深度学习|表示学习|卷积神经网络|参数共享是什么?|07

如是我闻: Parameter Sharing(参数共享)是卷积神经网络(CNN)的一个重要特性,帮助它高效地处理数据。参数共享的本质就是参数“本来也没有变过”。换句话说,在卷积层中,卷积核的参数&…

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展 在人工智能领域,大模型的竞争一直备受关注。最近,DeepSeek 团队发布了 DeepSeek-R1 模型,并开源了模型权重,这一举动无疑为 AI 领域带来了新的活力。今…

递归的本质

字节面试题叠罗汉,很遗憾没想出来,看了答案挺巧妙的,但是居然是个案例题。。。 复习一下递归的本质 正面解决问题 利用子问题来解决 可以通过规约推导的,基本可以用递归解决! 在写这道算法题时,我想规…

【力扣:新动计划,编程入门 —— 题解 ②】

—— 25.1.23 1512. 好数对的数目 给你一个整数数组 nums 。 如果一组数字 (i,j) 满足 nums[i] nums[j] 且 i < j &#xff0c;就可以认为这是一组 好数对 。 返回好数对的数目。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,1,1,3] 输出&#xff1a;4 解释&#xff…

K8S 快速实战

K8S 核心架构原理: 我们已经知道了 K8S 的核心功能:自动化运维管理多个容器化程序。那么 K8S 怎么做到的呢?这里,我们从宏观架构上来学习 K8S 的设计思想。首先看下图: K8S 是属于主从设备模型(Master-Slave 架构),即有 Master 节点负责核心的调度、管理和运维,Slave…

Redis 集群模式入门

Redis 集群模式入门 一、简介 Redis 有三种集群模式&#xff1a;主从模式、Sentinel 哨兵模式、cluster 分片模式 主从复制&#xff08;Master-Slave Replication&#xff09;: 在这种模式下&#xff0c;数据可以从一个 Redis 实例&#xff08;主节点 Master&#xff09;复…

Vue2 项目二次封装Axios

引言 在现代前端开发中&#xff0c;HTTP请求管理是构建健壮应用的核心能力之一。Axios作为目前最流行的HTTP客户端库&#xff0c;其灵活性和可扩展性为开发者提供了强大的基础能力。 1. 为什么要二次封装Axios&#xff1f; 1.1 统一项目管理需求 API路径标准化&#xff1a;…

Ceisum无人机巡检直播视频投射

接上次的视频投影&#xff0c;Leader告诉我这个视频投影要用在两个地方&#xff0c;一个是我原先写的轨迹回放那里&#xff0c;另一个在无人机起飞后的地图回显&#xff0c;要实时播放无人机拍摄的视频&#xff0c;还要能转镜头&#xff0c;让我把这个也接一下。 我的天&#x…

AI Agent:深度解析与未来展望

一、AI Agent的前世&#xff1a;从概念到萌芽 &#xff08;一&#xff09;早期探索 AI Agent的概念可以追溯到20世纪50年代&#xff0c;早期的AI研究主要集中在简单的规则系统上&#xff0c;这些系统的行为是确定性的&#xff0c;输出由输入决定。随着时间的推移&#xff0c;…

Spring MVC:HTTP 请求的参数传递2.0

本篇博客接上文: Spring MVC&#xff1a;Spring 前置知识 & HTTP 请求的参数传递1.0-CSDN博客 目录 1. 传递 json - RequestBody 1.1 json 1.1.1 什么是 json 1.1.2 json 的语法 1.1.3 json 和 Java 中对象的转换 1.1.4 json 优点 1.2 传递 json 2. 获取路径参数 -…

电子应用设计方案103:智能家庭AI浴缸系统设计

智能家庭 AI 浴缸系统设计 一、引言 智能家庭 AI 浴缸系统旨在为用户提供更加舒适、便捷和个性化的沐浴体验&#xff0c;融合了人工智能技术和先进的水疗功能。 二、系统概述 1. 系统目标 - 实现水温、水位和水流的精确控制。 - 提供多种按摩模式和水疗功能。 - 具备智能清洁…

设计模式的艺术-外观模式

结构性模式的名称、定义、学习难度和使用频率如下表所示&#xff1a; 1.如何理解外观模式 外观类充当了软件系统中的“服务员”&#xff0c;它为多个业务类的调用提供了一个统一的入口&#xff0c;简化了类与类之间的交互。 外观模式&#xff08;Facade Pattern&#xff09;&a…

“““【运用 R 语言里的“predict”函数针对 Cox 模型展开新数据的预测以及推理。】“““

主题与背景 本文主要介绍了如何在R语言中使用predict函数对已拟合的Cox比例风险模型进行新数据的预测和推理。Cox模型是一种常用的生存分析方法&#xff0c;用于评估多个因素对事件发生时间的影响。文章通过具体的代码示例展示了如何使用predict函数的不同参数来获取生存概率和…

戴尔电脑设置u盘启动_戴尔电脑设置u盘启动多种方法

最近有很多网友问&#xff0c;戴尔台式机怎么设置u盘启动&#xff0c;特别是近两年的戴尔台式机比较复杂&#xff0c;有些网友不知道怎么设置&#xff0c;其实设置u盘启动有两种方法&#xff0c;下面小编教大家戴尔电脑设置u盘启动方法。 戴尔电脑设置u盘启动方法一、戴尔进入b…

【React】 react路由

这一篇文章的重点在于将React关于路由的问题都给搞清楚。 一个路由就是一个映射关系&#xff0c;key:value。key是路径&#xff0c;value 可能是function或者component。 安装react-router-dom包使用路由服务&#xff0c;我这里想要用的是6版本的包&#xff0c;因此后面加”6&q…

隐私保护+性能优化,RyTuneX 让你的电脑更快更安全

RyTuneX 是一款专为 Windows 10 和 11 用户量身打造的系统优化工具&#xff0c;采用先进的 WinUI 3 框架开发&#xff0c;以其现代化的设计风格和强大的功能集合脱颖而出。这款工具不仅界面简洁美观&#xff0c;还提供了多样化的系统优化选项&#xff0c;旨在帮助用户最大化设备…

HTML新春烟花

系列文章 序号目录1HTML满屏跳动的爱心&#xff08;可写字&#xff09;2HTML五彩缤纷的爱心3HTML满屏漂浮爱心4HTML情人节快乐5HTML蓝色爱心射线6HTML跳动的爱心&#xff08;简易版&#xff09;7HTML粒子爱心8HTML蓝色动态爱心9HTML跳动的爱心&#xff08;双心版&#xff09;1…