Windows MFC 工程应用开发与框架原理完全剖析视频教程(中)之上部
来源:http://www.tudoupe.com时间:2022-07-18
Windows MFC 工程应用开发与框架原理完全剖析视频教程(中)之上部
- 第3章 原则2 MFC核心框架的全面实施
- 3.1MFC高级级结构设计及RTTI和CRunTimeClass设计思想的分析
- 3.2RTTI设计及核查
- 3.3动态类型识别技术:动态类型识别技术的设计和实现
- 3.4动态创造技术-宣布宏
- 3.5CWinThread的深入分析和实现(1):CWinThread的实现
- 3.6CWinThread(2)的深入分析和实现:MFC工人线性设计-CTypedSimpleList和CSimpleList的泛建模实现
- 3.7CWinThread(3)的深入分析和实现:MFC工人线性设计-NnoTrackObject的实现
- 3.8CWinThread(4)的深入分析和实现:MFC工人线性设计-MFC线性进步的覆盖基础-WindowsTLS机制的详细说明
- 3.9CWinThread(5)的深入分析和实现:MFC工人线性设计-MFC封装TLS的原理分析
- 3. 10 个 MFC 工作线的设计-- MFC 封装 TLS 头文件编码的实现
- 3.11MFC工人线性设计-MFC接口TLS1
- 3. 12 个 MFC 工人线的设计-MFC 接口 TLS2
- 3.13CThreadSlotData分析、DeltaValues编码和CThreadLocalObject函数
- 3.14MFC工人线性设计-单元测试和MFC封装TLS的总结
- 3. 15 个 MFC 工人线的设计-CWinThread全面实施
- 3.16 MFC工作线设计-CWinThread和错误删除的单元测试
- 3.17 MFC工人线程的设计-CWinThread过程运行的概述
第3章 原则2 MFC核心框架的全面实施
3.1MFC高级级结构设计及RTTI和CRunTimeClass设计思想的分析
























CObject CRuntimeClass::CreateObject(void)可以生成类变量。该函数与新函数类似,但在某些特殊情况下具有独特的函数。
在 上面例子中,CreateWnd返回的是CWnd* 其实它是一个CWndA*。你可以进行由父类到子类的强制转换而不必要担心出错。
使用CRuntimeClass可以取代一些简单的使用开关生产类的例子。请考虑一下它的使用.当你发现它的好处时,你一定会大吃一惊,M$使用宏实现类动态检测,如果您有兴趣,请查阅MFC的源代码。)
注: IMPLEMENT_DYNCREATE在类定义中使用后会生效。
MFC中经常只需要构建类,程序就可以运行了,并没有看到创建对象的过程。那么对象是如何创建的呢?
这是动态创造机制。
动态创建允许MFC自动为您写的类创建对象,因此您只需要在写代码的过程中编辑该类。
为了实现对象的自动创建,必须在每个类中创建一个结构, CRuntimeClass,主要记录类的信息:类名、几个标记和构造函数CreateObject。
指针用于程序中的所有类序列化(包括您创建的类和MFC自动创建的类),在整个程序中创建一个类的链表。需要指出,这个链是水平的和垂直的。横向是指,每个类通过m_pNextClass根据构造序列(pfirst和pnext的字符串)连接,用简单的链接表连接所有CRuntimeClass对象。纵向是指,在结构m_pBaseClass中还有一个指针,指向该类的父类。这样可以更容易查询该类的导数关系。
用于构造类对象的构造函数。CreateObject()函数是可动态创建的类中。
动态创建类CRuntimeClass结构中的m_pfnCreateObject字段最初被分配给CreateObject,如果它是NULL,则不能动态创建。
上述构造函数可参阅: https://ww.cnblogs.com/tinaluo/p/7826678.html
有了这些准备,动态创造是很容易的.例如,在主程序中,提供一个类名,创建该类的对象。只要写一个搜索函数,有了类的链表,搜索函数可以轻易地搜索相应的类,搜索成功后,调用该类内置函数,就可以产生对象。
上述搜索功能可以在 https://blog.csdn.armman/article/details/1527409 找到
3.2RTTI设计及核查






上图return pClassThis->IsDerivedFrom函数的参数错了,应由pClassThis改为pClass。

上面显示的RUNTIME_CLASS宏定义,其中CRuntimClass应变为CRuntimeClass。







3.3动态类型识别技术:动态类型识别技术的设计和实现












3.4动态创造技术-宣布宏

上面的图显示了一个错误,我们通过设置下图的输出预处理文件,在宏扩展后检查代码声明。



你可以看到,图片的末端有一个反向栏,应该被删除。












3.5CWinThread的深入分析和实现(1):CWinThread的实现



















3.6CWinThread(2)的深入分析和实现:MFC工人线性设计-CTypedSimpleList和CSimpleList的泛建模实现


上图有误,应改为:






3.7CWinThread(3)的深入分析和实现:MFC工人线性设计-NnoTrackObject的实现









如果我们的数据节点不继承CNoTrackObject:

它调用的是通用的new和delete操作符。
3.8CWinThread(4)的深入分析和实现:MFC工人线性设计-MFC线性进步的覆盖基础-WindowsTLS机制的详细说明


这是我们一开始使用的非常简单的工人线条,我们直接使用AfxBeginThread进行主要功能,根据微软的讲法,在MFC中,我们不需要去CloseHandle,而且你不需要做一系列的线圈关闭,我们可以直接使用这些参数。我们不需要删除对象,我们不需要管理手柄.

对于线程本地存储,我们需要集中注意两个概念:
(一)在一个线程中本地存储的位子集;
(二)线程和位群之间的通信。








3.9CWinThread(5)的深入分析和实现:MFC工人线性设计-MFC封装TLS的原理分析

我们希望线程和线程之间的数据也能够有逻辑访问:
MFC使用线性本地存储扩展,使得它可以无限扩展,因为我们以前有固定数量的线性本地存储。

3. 10 个 MFC 工作线的设计-- MFC 封装 TLS 头文件编码的实现




我们定义了CThreadLocalObject类,它是一个辅助数据结构,我们定义以完成分区分配,即在分区数据中提到的实际用户数据。
这实际上是我们线程本地存储中的那个空格中的对象,所以必须有一个空格数。
我们将MFC的内容复制到CThreadLocalObject模板中:

在我们的开发过程中,为什么,当MFC做这个工人的线程时,它不允许通过线程访问非MFC对象,因为一旦跨线访问被从我们的TLS管理中删除,为了使这种实现超越了我们的供应链管理,让你的记忆管理无法控制,因为您也知道它的内存不再在C++中运行,事实上,它对于操作系统至关重要,所以它不能跨线访问。
3.11MFC工人线性设计-MFC接口TLS1






3. 12 个 MFC 工人线的设计-MFC 接口 TLS2





3.13CThreadSlotData分析、DeltaValues编码和CThreadLocalObject函数








3.14MFC工人线性设计-单元测试和MFC封装TLS的总结










在我们的召唤过程中,它与MFC的CWinThread输入点函数AfxThreadEntry非常相似,换句话说,我们只需要从上面的图片中删除78到81行。转换为AfxThreadEntry函数调用是好的,但我们还没有_bgeinthreadex和WaitForMultipleObjects,以及CloseHandle来做一些事情(要把它们封装起来),我们不能自动关闭内容(程序处理、同步事件、程序本地对象等),然后我们开始编码CWinThread工人的线程。
3. 15 个 MFC 工人线的设计-CWinThread全面实施
为CWinThread class.h新定义的 _ AFXWIN头条:

这个m_hThread说,因为CWinThread是一个C++语法,它应该能够在头上保存一个手柄,这实际上是调用WindowsSDK来帮助我们创建这个线程;
重载一个括号运算符, 其实现是返回m_hThread.

这个进程中可能有多个线程,每个线程都有不同的模块(线程有私人数据),在这些模块中,这个一个线程对我们来说是私有的,例如,线程1有一个自己的模块,2号线也有自己的模块,这些模块必须作为线程保持其状态,因为这些可执行对象(模块)也会被推广,推进到哪里也会被记录,现在我们必须重新设计一个班级来记录这个状态,我们称之为状态AFX_MODULE_THREAD_STATE,很明显,这个线程的状态必须取决于TLS。

换句话说,我们的MFC内部维护每个模块独立于每个线程;
我们还必须维持它,让它能够用链条捆绑它,为了管理每个线程的状态,使用THREAD_LOCAL宏把表示线程状态的全局变量_afxModuleThreadState改写成线程局部变量(通过我们自己的线程局部存储),即CThreadLocal<AFX_MODULE_THREAD_STATE> _afxModuleThreadState。


除了AFX_MODULE_THREAD_STATE的定义之外,我们还得想一想,当我们的用户使用CWinThread创建线程时,除了设置线程的状态之外,进入消息循环,我们还必须适当地设置线程的函数地址,但我们也不能直接暴露给用户。那时我们开始想,我们需要一个设计来帮助我们设计在Windows SDK中的C++函数。

AFX_THREADPROC 这个类型的函数将用于初始化我们的线程参数,帮助我们运行我们的线程。


让我们回顾使用MFC的过程,我们需要提供一个AfxThreadEntry来帮助我们输入函数的输入点,然后我们实现了AfxThreadEntry,CWinThread类, ThreadCore.cpp的新实现文件:

换句话说,为了推进子程序,我需要为子程序(例如同步机制)创建一个特定的环境,因此我们现在还必须有一个名为AFX_THREAD_STARTUP的包。

通过这种结构,把它放在Win32中的 CreateThread函数中,可以这么讲,我们正在将CWinThread结合到CreateThread中,完成CWinThread输入点函数AfxThreadEntry.
两个线程(主线程和子线程)之间的交互是事件对象:
第一个hEvent告诉您在创建子线程后触发,告诉父亲线程我们已经创建它,告诉父亲线程您可以继续;
第二个hEvent2就是说,当我完成为这个调用线程准备的所有粒子并再次触发它时,换句话说,告诉下线程你可以继续。
_AfxThreadEntry:
这个时候呢,既然有了AFX_THREAD_STARTUP,我们就可以为CWinThread对象组合进来提供帮助了。
通过AFX_THREAD_STARTUP获得我们将要推进的CWinThread对象,即pStatup->pThread
接下来我们将设置线程的状态并查看内容:
让我们看看我们是否能从这个对象中获取该模块的当前状态,同时我们指定当前状态,指向这个CWinThread对象的当前pThread;
通过try…catch语句看看这个转换是否OK,有没有异常,因为线程当中有可能会被其他打断,这个时候我们就不能往下执行了,设置bError为TRUE,告诉父线程新创建的这个子线程初始化出错。


CommonConstruct函数它的任务是建立我们的对象。
此外,我们还为用户提供两个全球功能:


我们回到 ThreadCore.inside cpp,上图这个地方,当父线作为进入点向前走时,我们会考虑,这个pStartup指的内存空间是我们的新线程的空间,我们开始了新的线程的初始化后,如果新线程堆栈无法构造,这个线程的空间(指向pStartup的内存空间)可能被侵蚀,所以我们需要能够正常地启动CreateThread,然后才能完成。我们先保存下pStartup->hEvent2的值,确保线程初始化完成(避免子线程僵局)。



要返回当前线程CWinThread对象的指针, 我们必须取其状态.

这意味着AFX_MODULE_THREAD_STATE总是包含我们的CWinThread对象。

这是我们自己编写的线程的本地存储,因为CWinThread对象不存在,我们也将释放我们自己分配的内存空间,否则它将占用内存资源;
FALSE参数表示只有当前线程被删除当前线程的本地存储空间。

vs智能感知不到_afxThreadData变量,它是放在AFXTLS.cpp文件当中的,所以需要调整一下位置,放到_AFXSTAT.h里面:

当我们先让它挂起来时,我们创建一个线程的原因是考虑父母线程和子线程之间的相互作用,并给子线程足够的时间来载入其中资源。






如果成功创建的线程手柄存在,则我们将恢复线程的执行。
坦率地说,它告诉你,当父亲线创造了这个孩子线之后,你先把所有空间放在里面,然后推你向前。
这个时候在ResumeThread之后,我们就要开始让子线程去把相应的空间初始化完毕,所以要等待你(子线程)告诉我初始化完毕好了,告诉我什么时候能够推进,由你子线程完成告诉我。
如果用户一上来并不是要求你直接推进,你创建的是一个挂起的线程,我就先让你暂时停止推进,等什么时候需要推进的时候由你来推进。
用户可以创建悬挂线程, 这是常见的操作.
由于资源不足,创建一个线程很可能失败.

3.16 MFC工作线设计-CWinThread和错误删除的单元测试

让我们看看 MFC工人线程是如何实现的。



你看,这个AfxBeginThread非常接近MFC的工作流程,所以这是我们对CWinThread的基本工作。

注意,我们只是把它弄错了,它应该在_AFXTLS_上。


这个时候就会出现上图这种重定义的问题,这个原因是什么呢?
这是因为重复涉及造成,为了解决这种重复内容,我们将修订标题,因为在C/C++中我们又做了混合编程,请特别注意这个问题,因为C没有所谓的空间的概念,因此, 它 将 以 文件 统一,在我们的 ThreadCore.the _afxThreadData中,在cpp中的AFXTLS.the _afxThreadData,会有重复冲突的现象,所以没有办法在obj文件中有一个独特的生成。



同样, _ AFXSTAT.h 和 _ AFXWIN.h 应该在两个标题上进行同样的修正:




在选择中切开两个行并将其放在AFXTLS中。 在cpp文件中:



3.17 MFC工人线程的设计-CWinThread过程运行的概述
让我们看看工人的线条做了些什么。
让我们开始创建父线:

新线程创建,现在,让我们看看我们的操作系统中创建新的线程,它实际上给你一个堆栈的线条和一些空间,但它尚未立即实施,为什么呢,因为你知道我们可能会得到一些手柄,一些模块啊,还有一些相应的执行器可以推入Windows,同时,如果它还没有完全启动,不能执行,所以怎么办呢,它先挂起,这就是子程序做的。

左上是MFC做的,MFC帮助我们推新的线程(右),这样新的线程开始执行AfxThreadEntry。



这是我们的CWinThread过程。可以这么讲,虽然这个CWinThread对我们来说很方便,但是,MFC已经做了很多工作来推进我们的应用,每个人都必须了解这个过程,因此,我们的新轨迹不会被推向上。
现在要知道的基本上有两件事,第一个,CWinThread能够封装函数WinMain,而且因为它可以被看作是函数执行器,如线程等命令序列的聚合物向前推进,所以我们的MFC用CWinThread完成WinMain功能;
第二个,当我们的MFC正在推进这一方向时,它考虑到这个线程交互的状态,所以我们做了一系列事情,能够管理自己的记忆力,您可以在线程中管理自己的数据,同时通过技术,如TLS,使线程的相互作用更加统一和协调,在这个过程当中,可以说它是使用Windows操作系统底层的更复杂的机制。
相关新闻
- 2022-08-04 WPF的由来
- 2022-08-04 Win11勒索软件防护怎么打开?Win11安
- 2022-08-04 Windows系统jdk的配置
- 2022-08-04 Windows10 OneNote怎么重新登录?如何重
- 2022-08-04 超好用的 Windows 效率工具推荐
- 2022-08-04 Windows如何在CMD或PowerShell中配置代理
- 2022-08-04 powershell和cmd对比
- 2022-08-04 【QT】Windows下QT下载安装
- 2022-08-04 windows下 C++ 实现类属性的get和set方
- 2022-08-04 Win11快速助手在哪里?Win11打开快速
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
