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; 
}; 

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;
}

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

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

的意思是查看(而不移除)消息队列中是否还有等待处理的消息,如果有,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__

将其实现代码放在名为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)

此框架规定,每个应用程序必须有一个全局的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;
}

其中负责框架内部初始化的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;
}

使用类库框架之后,不需要再与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();
};

下面是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;
}

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳光开朗男孩

你的鼓励是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值