全部博文(263)
分类: WINDOWS
2009-11-14 11:52:54
VC++工程中加入 SplashScreen 原理释解
作者:
摘要
本文旨在剖析VC++工程中加入 SplashScreen 的原理,并给出在VC++ MFC(exe)工程中加入 SplashScreen 的步骤。
关键字 SplashScreen,原理
环境:Windows 98SE/2000,VC++ 6.0
SplashScreen - 我们使用 Word
之类的软件在启动的短暂时间里就会看到它的身影。它通常用以在程序启动时显示程序及用户名称,版权信息等。我也不知道它准确的名称是什么(是闪屏吗?),
就这样称呼吧。也许你也想在自己的工程里加入这样的特性,本文将以创建实际工程的方式逐步剖析其实现原理。
注意:为避免实际所使用工程名给类或对象名带来的干扰,除非特别说明,在本文中将使用基类名如CWinApp、CMainFrame、CDialog来代替实际工程中的相应派生类名进行描述。
Visual C++是一个相当强大的C++开发工具,它内嵌了对SplashScreen的支持。但是在MFC
EXE类型工程中只是对带有主框架类的SDI或MDI工程提供了这一支持,基于对话框类的工程则被排除在外。现在让我们开始吧。第一步是在SDI工程中加
入SplashScreen。
首先利用AppWizard生成一个SDI工程,除了其中Docking
ToolBar必须选择外(我认为这是MFC的一个Bug,当然这与本文讨论的SplashScreen没有关系),其他的文档-视图支持、状态条之类的
都可以不要,这样可以尽量减少无用的代码。
通过IDE中的菜单Project->Add to Project->Components and Controls,我们就可以从Visual
C++ Components中选择Splash Screen这个组件插入工程。
在点击了"Insert"后会弹出一个如下图所示的对话框,这是设置插入该工程中的SplashScreen的类名、显示用位图的ID及文件名,采用缺省值即可。
通过以上几步的操作,就会在工程目录下生成Splash.CPP和Splash.H文件,这便是CSplashWnd类的实现文件与头文件。同时工程
中CWinApp与CMainFrame类中的部分代码也会被修改,以实现CSplashWnd窗口的消息处理。
接着我们来看看 CSplashWnd 类的声明与主要的代码(已经过删减):
//类的声明
class CSplashWnd : public CWnd//是否使用SplashScreen
{
CSplashWnd();
~CSplashWnd();
virtual void PostNcDestroy();
static void EnableSplashScreen(BOOL bEnable = TRUE);
static void ShowSplashScreen(CWnd* pParentWnd = NULL);
static BOOL PreTranslateAppMessage(MSG* pMsg);
BOOL Create(CWnd* pParentWnd = NULL);
void HideSplashScreen();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
CBitmap m_bitmap; //SplashScreen窗口显示用的位图对象
static BOOL c_bShowSplashWnd; //是否要显示SplashScreen的标志量
static CSplashWnd* c_pSplashWnd;
};
void CSplashWnd::EnableSplashScreen(BOOL bEnable)//创建CsplashWnd对象,并调用Create()创建窗口
{
c_bShowSplashWnd = bEnable;
}
void CSplashWnd::ShowSplashScreen(CWnd* pParentWnd)//装入SplashScreen欲显示位图,通过CreateEx()激发OnCreate()完成窗口创建与设置
{
//如果不要显示SplashScreen或SplashWnd对象已经被创建则返回
if (!c_bShowSplashWnd || c_pSplashWnd != NULL)
return;
c_pSplashWnd = new CSplashWnd;
if (!c_pSplashWnd->Create(pParentWnd))
delete c_pSplashWnd;
else
c_pSplashWnd->UpdateWindow();
}
BOOL CSplashWnd::Create(CWnd* pParentWnd)//销毁窗口,刷新框架
{
if (!m_bitmap.LoadBitmap(IDB_SPLASH))
return FALSE;
BITMAP bm;
m_bitmap.GetBitmap(&bm);
return CreateEx(0,
AfxRegisterWndClass(0,AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
NULL,
WS_POPUP | WS_VISIBLE,
0,
0,
bm.bmWidth,
bm.bmHeight,
pParentWnd->GetSafeHwnd(),
NULL);
}
void CSplashWnd::HideSplashScreen()//利用窗口创建结构创建窗口,并设置定时器在750ms后触发OnTimer()
{
DestroyWindow();
AfxGetMainWnd()->UpdateWindow();
}
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)//将键盘和鼠标消息传递给CSplashWnd对象,以销毁窗口
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
CenterWindow(); //窗口居中显示
SetTimer(1, 750, NULL); //设置定时器
return 0;
}
BOOL CSplashWnd::PreTranslateAppMessage(MSG* pMsg)再看看CWinApp和CMainFrame类中发生了什么样的改变:
{
if (c_pSplashWnd == NULL)
return FALSE;
if (pMsg->message == WM_KEYDOWN ||
pMsg->message == WM_SYSKEYDOWN ||
pMsg->message == WM_LBUTTONDOWN ||
pMsg->message == WM_RBUTTONDOWN ||
pMsg->message == WM_MBUTTONDOWN ||
pMsg->message == WM_NCLBUTTONDOWN ||
pMsg->message == WM_NCRBUTTONDOWN ||
pMsg->message == WM_NCMBUTTONDOWN)
{
c_pSplashWnd->HideSplashScreen();
return TRUE;
}
return FALSE;
}
void CSplashWnd::OnTimer(UINT nIDEvent)
{
HideSplashScreen();
}
int CMyDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)(2)利用ClassWizard为CWinApp添加消息转发处理函数PreTranslateMessage();
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
CSplashWnd::ShowSplashScreen(this);
return 0;
}
BOOL CWinApp::PreTranslateMessage(MSG* pMsg)(3)CWinApp::InitInstance()中加入如下调用: CSplashWnd::EnableSplashScreen(TRUE);
{
if (CSplashWnd::PreTranslateAppMessage(pMsg))
return TRUE;
return CWinApp::PreTranslateMessage(pMsg);
}