• 5.5 框架程序的执行顺序


    5.5.1 线程的生命周期

    线程被创建以后,它就成了一个执行代码的独立实体。有的线程生命周期很长,例如主线程,它的生存期跟进程的生存期一样。它在进程创建时被创建,主线程的结束也导致进程的终止。有的线程则在完成了一项任务后马上退出。不管是哪个线程,其经历的状态都可以被分为表5.1所示的3种。
    在这里插入图片描述
    线程刚开始被创建时,必须做一些初始化工作,为的是产生应用程序的工作平台——窗口。这项工作是每个线程实例都得做的,所以称之为Initialize Instance。

    初始化工作完成后,线程进入消息循环,不断地移除并处理消息队列的消息,响应用户的请求。这是线程的主要工作状态,称之为Running。

    消息循环结束后,线程就要准备终止自己的执行了,这时称它所做的工作为Exit Instance。

    在CWinThread类中这几个状态用代码表示如下。

    class CWinThread : public CObject 
    { 
    // ……其他成员 
    public:  
         // 允许重载的函数(Overridables)  
         // 执行线程实例初始化  
         virtual BOOL InitInstance();  
         // 开始处理消息  
         virtual int Run();  
         virtual BOOL PreTranslateMessage(MSG* pMsg);  
         virtual BOOL PumpMessage();  
         virtual BOOL OnIdle(LONG lCount);  
         virtual BOOL IsIdleMessage(MSG* pMsg);  
         // 线程终止时执行清除  
         virtual int ExitInstance();  
         // 当前正在处理的消息  
         MSG m_msgCur; 
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    m_msgCur成员记录着当前的CWinThread对象正在处理的消息。InitInstance、Run和ExitInstance函数是由框架程序负责调用的,用户如果想添加额外的代码只需重载它们就可以了。下面是它们的实现代码。

    int CWinThread::Run()
    {
    	BOOL bIdle = TRUE;
    	LONG lIdleCount = 0;
    	for(;;)
    	{
    		while(bIdle && !::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE))
    		{
    			if(!OnIdle(lIdleCount++))
    				bIdle = FALSE;
    		}
    
    		do
    		{
    			if(!PumpMessage())
    				return ExitInstance();
    			
    			if(IsIdleMessage(&m_msgCur))
    			{
    				bIdle = TRUE;
    				lIdleCount = 0;
    			}
    		}while(::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE));
    	}
    	ASSERT(FALSE);
    }
    // 在消息送给Windows的TranslateMessage和DispatchMessage之前进行消息过滤
    BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
    {
    	return FALSE;
    }
    
    BOOL CWinThread::PumpMessage()
    {
    	if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
    		return FALSE;
    
    	if(!PreTranslateMessage(&m_msgCur)) // 没有完成翻译
    	{
    		::TranslateMessage(&m_msgCur);
    		::DispatchMessage(&m_msgCur);
    	}
    	return TRUE;
    }
    
    BOOL CWinThread::OnIdle(LONG lCount)
    {
    	return lCount < 0;
    }
    
    BOOL CWinThread::IsIdleMessage(MSG* pMsg)
    {
    	return TRUE;
    }
    
    BOOL CWinThread::InitInstance()
    {
    	return FALSE;
    }
    
    int CWinThread::ExitInstance()
    {
    	int nResult = m_msgCur.lParam;
    	return nResult;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    在进行消息循环时CWinThread类进行了消息空闲处理。当消息队列中没有消息时,框架程序自动调用虚函数OnIdle,允许用户利用线程空闲的时间一些工作。下面这行代码。

    while(::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE)); 
    
    • 1

    的意思是查看(而不移除)消息队列中是否还有等待处理的消息,如果有,PeekMessage函数返回TRUE,Run函数继续调用PumpMessage移除并处理一个消息;如果没有就做空闲处理,试图调用OnIdle函数。

    5.5.2 程序的初始化过程

    在使用框架之前,用户必须首先创建应用程序实例。这个应用程序实例用来初始化整个类库框架,也用来维护一些全局变量信息,我们用一个CWinApp类来描述它。

    // CWinApp类
    class CWinApp : public CWinThread //_AFXWIN.H
    {
    	DECLARE_DYNCREATE(CWinApp)
    public:
    	CWinApp();
    	virtual ~CWinApp();
    
    // 属性
    	// WinMain函数的四个参数
    	HINSTANCE m_hInstance;
    	HINSTANCE m_hPrevInstance;
    	LPTSTR m_lpCmdLine;
    	int m_nCmdShow;
    
    // 帮助操作,通常在InitInstance函数中进行
    public:
    	HCURSOR LoadCursor(UINT nIDResource) const;
    	HICON LoadIcon(UINT nIDResource) const;
    
    // 虚函数
    public:
    	virtual BOOL InitApplication();
    	virtual BOOL InitInstance();
    	virtual int ExitInstance();
    	virtual int Run();
    };
    
    __inline HCURSOR CWinApp::LoadCursor(UINT nIDResource) const
    	{ return::LoadCursor(AfxGetModuleState()->m_hCurrentResourceHandle, (LPCTSTR)nIDResource); }
    __inline HICON CWinApp::LoadIcon(UINT nIDResource) const
    	{ return::LoadIcon(AfxGetModuleState()->m_hCurrentResourceHandle, (LPCTSTR)nIDResource); }
    
    CWinApp* AfxGetApp();
    
    __inline CWinApp* AfxGetApp()
    	{ return AfxGetModuleState()->m_pCurrentWinApp; }
    
    BOOL AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    				LPTSTR lpCmdLine, int nCmdShow);
    
    #endif // __AFXWIN_H__
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    将其实现代码放在名为APPCORE.CPP的文件中。

    // APPCORE.CPP文件
    
    #include "_afxwin.h"
    
    CWinApp::CWinApp()
    {
    	// 初始化CWinThread状态
    	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    	AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
    	ASSERT(AfxGetThread() == NULL);
    	pThreadState->m_pCurrentWinThread = this;
    	ASSERT(AfxGetThread() == this);
    	m_hThread = ::GetCurrentThread();
    	m_nThreadID = ::GetCurrentThreadId();
    
    	// 初始化CWinApp状态
    	ASSERT(pModuleState->m_pCurrentWinApp == NULL);
    	pModuleState->m_pCurrentWinApp = this;
    	ASSERT(AfxGetApp() == this);
    
    	// 直到进入WinMain函数之后再设置为运行状态
    	m_hInstance = NULL;
    }
    
    CWinApp::~CWinApp()
    {
    	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    	if(pModuleState->m_pCurrentWinApp == this)
    		pModuleState->m_pCurrentWinApp = NULL;
    }
    
    BOOL CWinApp::InitApplication()
    {
    	return TRUE;
    }
    
    BOOL CWinApp::InitInstance()
    {
    	return TRUE;
    }
    
    int CWinApp::Run()
    {
    	return CWinThread::Run();
    }
    
    int CWinApp::ExitInstance()
    {
    	return m_msgCur.wParam;
    }
    
    IMPLEMENT_DYNCREATE(CWinApp, CWinThread)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    此框架规定,每个应用程序必须有一个全局的CWinApp对象,这样CWinApp类的构造函数就会在WinMain函数执行之前被调用。构造函数除了初始化CWinApp对象之外,还初始化了主线程的线程状态和当前模块的模块状态。

    还剩几个虚函数需要讲述,其中InitApplication负责每个程序只做一次的动作,而InitInstance负责每个线程只做一次的动作。在使用的时候,要视情况去重载不同的虚函数。

    现在该在类库框架中实现WinMain函数的执行代码了。为这个文件命名为WINMAIN.CPP。

    // WINMAIN.CPP文件
    
    #include "_afxwin.h"
    
    int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    	LPTSTR lpCmdLine, int nCmdShow)
    {
    	ASSERT(hPrevInstance == NULL);
    
    	int nReturnCode = -1;
    	CWinThread* pThread = AfxGetThread();
    	CWinApp* pApp = AfxGetApp();
    
    	// 类库框架内部的初始化
    	if(!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
    		goto InitFailure;
    
    	// 应用程序的全局初始化
    	if(pApp != NULL && !pApp->InitApplication())
    		goto InitFailure;
    
    	// 主线程的初始化
    	if(!pThread->InitInstance())
    	{
    		nReturnCode = pThread->ExitInstance();
    		goto InitFailure;
    	}
    
    	// 开始与用户交互
    	nReturnCode = pThread->Run();
    
    InitFailure:
    	return nReturnCode;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    其中负责框架内部初始化的AfxWinInit函数的实现代码如下。

    // APPINIT.CPP文件
    
    #include "_afxwin.h"
    
    BOOL AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    	LPTSTR lpCmdLine, int nCmdShow)
    {
    	ASSERT(hPrevInstance == NULL);
    
    	// 设置实例句柄
    	AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
    	pModuleState->m_hCurrentInstanceHandle = hInstance;
    	pModuleState->m_hCurrentResourceHandle = hInstance;
    
    	// 为这个应用程序填写初始化状态
    	CWinApp* pApp = AfxGetApp();
    	if(pApp != NULL)
    	{
    		pApp->m_hInstance = hInstance;
    		pApp->m_hPrevInstance = hInstance;
    		pApp->m_lpCmdLine = lpCmdLine;
    		pApp->m_nCmdShow = nCmdShow;
    	}
    
    	return TRUE;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    使用类库框架之后,不需要再与WinMain函数见面了,只需从CWinApp类派生自己的应用程序类,重载InitApplication或者InitInstance函数。如果想让程序进入消息循环,就令InitInstance函数返回TRUE,否则返回FALSE。

    5.5.3 框架程序应用举例

    本小节的例子是为了说明使用框架程序以后程序的结构,所有它没有什么功能,运行之后仅仅弹出一个MessageBox对话框。

    首先创建一个空的Win32工程,工程名称为05UseFrame。然后按照3.1.5小节所示的方法设置工程支持多线程,再将COMMON文件夹下的文件添加到工程中(至少要添加所有的.CPP文件)。如果觉得类视图中显示的内容太多,可以在类视图中新建一个目录,把添加进来的类全部拖到这个目录下面。最后新建两个文件UseFrame.h和UseFrame.cpp,下面是UseFrame.h文件中的代码。
    05UseFrame

    //UseFrame.h
    #include "_afxwin.h"
    
    class CMyApp : public CWinApp
    {
    public:
    	virtual BOOL InitInstance();
    	virtual int ExitInstance();
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    下面是UseFrame.cpp文件中的代码。

    // UseFrame.cpp文件
    #include "UseFrame.h"
    
    CMyApp theApp;		// 应用程序实例对象
    
    // CMyApp类
    
    BOOL CMyApp::InitInstance()
    {
    	::MessageBox(NULL, "主线程开始执行!", "CMyApp::InitInstance", 0);
    
    	return FALSE;	// 不要进入消息循环
    }
    
    int CMyApp::ExitInstance()
    {
    	::MessageBox(NULL, "主线程将要退出!", "CMyApp::ExitInstance", MB_OK);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    简单来说,要使用这个类库框架,必须定义一个从CWinApp类派生的应用程序类,在这个类里重载InitInstance函数写程序初始化代码。还必须要保证程序中有惟一的应用程序实例对象,如本例中的theApp。

    这个工作流程并不是为写控制台应用程序设计的,所以只有学完了下一章,才会觉得在InitInstance函数中添加代码并不比在WinMain函数中麻烦。

  • 相关阅读:
    (っ•̀ω•́)っ 如何在PPT中为文本框添加滚动条
    基于毫米波雷达的跌倒方式实时检测方法研究
    vue + koa + 阿里云部署 + 宝塔:宝塔前后端部署
    技术栈 业务架构 插件库
    力扣226:反转二叉树
    使用Git将GitHub仓库下载到本地
    MongoDB数据库新手入门
    ES10 新特性
    描述符——设备描述符
    基于SSM的进销存管理系统设计与实现
  • 原文地址:https://blog.csdn.net/qq_36314864/article/details/125480242