最近想做个程序的启动画面,原因当然就是为了使程序更加美观、好看一些。原来记得VC5中有个Splash Screen组件,可以直接做启动画面的,结果在VC .Net中,这个组件已经找不到了。是不是取消了这个功能,本人不太清楚。于是网上搜了一些关于Splash启动画面的资料,研究了一番,结果都不太满意。有一篇流传比较广的,是基于Dialog做的,效果一样,但是如果为了做一个启动画面,就得加一个Dialog资源,感觉比较“原始”一点。另外有几篇是基于CWnd类做的,这才是制作Spalsh本来的技术嘛,不过这几篇,要么是在项目内加一个BMP资源,要么用CPicture类从文件加载图像资源,现在不是有GDI+技术嘛,于是研究了一下,完成了这个利用GDI+技术制作基于CWnd类的Splash启动画面的程序。
一、在程序中增加GDI+支持
1、在APP的头文件或者stdafx.h中引用Gdiplus头文件,并使用GDI名空间。
#include "gdiplus.h" // 使用GDI+技术
// 使用GDI+名空间
using namespace Gdiplus;
#pragma comment(lib,"gdiplus.lib") // 添加GDI+库文件
2、启动GDI系统资源初始化和注销,这个可以在APP类中实现,也可以在启动画面类CSplashWnd类中实现,根据具体情况来定。但要在类中添加一个protected型的变量m_gdiplusToken,代码如下:
protected:
// GDI+程序启动的标识
ULONG_PTR m_gdiplusToken;
// GDI+系统资源初始化
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput,NULL); // 启动GDI+资源
// 销毁GDI+系统资源
Gdiplus::GdiplusShutdown(m_gdiplusToken); // 关闭GDI+资源
二、项目上添加启动画面的CSplashWnd类
1、程序中添加基于CWnd类的CSplashWnd类,并把CSplash类的构造函数修改,增加文件名的参数。
CSplashWnd::CSplashWnd(LPCWSTR lpszFileName)
并在类中增加一些private型变量:
private:
LPCWSTR m_sImageName;
Image * m_pImage;
UINT nWidth;
UINT nHeight;
然后添加WM_CREATE和WM_PAINT消息的响应函数
afx_msg void OnPaint();
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
2、编写构造函数和析构函数
CSplashWnd::CSplashWnd(LPCWSTR lpszFileName)
{
m_sImageName = lpszFileName;
nWidth = 0;
nHeight = 0;
m_pImage = NULL;
}
CSplashWnd::~CSplashWnd(void)
{
if(m_pImage) delete m_pImage;
m_pImage = NULL;
}
3、从文件加载图像并弹出启动画面
在类中添加一个CreateWnd(CWnd* pParentWnd = NULL)函数,设为public型。
声明: BOOL CreateWnd(CWnd* pParentWnd = NULL);
定义:// 建立启动画面的弹出式窗口
BOOL CSplashWnd::CreateWnd(CWnd* pParentWnd /*= NULL*/)
{
m_pImage = new Image(m_sImageName);
if (!m_pImage)
{
TRACE0(_T("图像文件不存在"));
return FALSE;
}
nWidth = m_pImage->GetWidth();
nHeight = m_pImage->GetHeight();
LPCTSTR strSplashWndClass;
strSplashWndClass = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, AfxGetApp()->LoadStandardCursor(IDC_ARROW),
0,AfxGetApp()->LoadIcon(IDR_MAINFRAME));
return CreateEx(0,strSplashWndClass,NULL, WS_POPUP|WS_VISIBLE, 0, 0, nWidth, nHeight, pParentWnd->GetSafeHwnd(), NULL);
}
4、编写OnPaint()和OnCreate()函数
void CSplashWnd::OnPaint()
{
CPaintDC pDC(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CWnd::OnPaint()
Graphics p_Graphics(pDC.m_hDC);
p_Graphics.DrawImage(m_pImage,0,0,nWidth,nHeight);
}
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
CenterWindow();//使弹出窗口居中
return 0;
}
5、为防止内存溢出,窗口销毁后要释放CSplashWnd对象,为此,我们重载虚拟函数PostNcDestroy,此函数在窗口销毁后调用。
void CSplashWnd::PostNcDestroy()
{
// TODO: 在此添加专用代码和/或调用基类
delete this;
CWnd::PostNcDestroy();
}
6、在CSplashWnd的.Cpp文件中,添加Resource.h文件的引用
#include "Resource.h"
三、Splash画面的启动方法
Splash画面可以有2种启动方式,一种是用Sleep()阻断进程,启动画面,另一种是用定时器开设新线程,来启动画面,2种方式各有各的益处。于是本人设计通过一个参数,来实现不同的启动方式。
1、增加显示时间变量
在CSplashWnd类中添加一个整形变量,表示画面显示的时间,同样设为private型变量
private:
UINT nSplashTime;
2、添加WM_TIMER定时器消息的响应函数OnTimer(),代码如下:
void CSplashWnd::OnTimer(UINT nIDEvent)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if(IsWindowVisible())
{
SetActiveWindow(); //把启动画面设置为当前活动窗口
UpdateWindow();
::Sleep(nSplashTime); //修改此处可更改画面显示时间
SendMessage(WM_CLOSE); //关闭对话框
}
else
{
AfxGetMainWnd()->SetActiveWindow(); // 激动主框架窗口
KillTimer(nIDEvent) ; //清除WM_TIMER事件
}
//CWnd::OnTimer(nIDEvent);
}
3、增加显示画面的函数ShowSplashScreen(),这个函数应该是public型的,代码如下:
// 显示启动画面 nSeconds表示显示的毫秒,nMode表示显示的方法,0-阻断显示,1-定时器显示
void CSplashWnd::ShowSplashScreen(UINT nSeconds,UINT nMode)
{
// 显示启动画面
if (!CreateWnd(AfxGetMainWnd())) {
delete this;
return; }
nSplashTime = nSeconds;
if(nMode == 0)
{
UpdateWindow();
::Sleep(nSeconds);
DestroyWindow();
}
if(nMode == 1) SetTimer(1,100,NULL); //启动ID为1的定时器
}
四、在CMainFrame框架类中,启动CSplashWnd类
在OnCreate()函数的最后面,添加如下代码:
CSplashWnd * pSplashWnd;
pSplashWnd = new CSplashWnd(L"splash1.jpg");
pSplashWnd->ShowSplashScreen(2000,0); // 显示时间为2秒,方式为阻断模式
最后一个完美的Splash启动画面就完成了。如果想使用定时器模式,只需要将最后一句代码修改成下面。
pSplashWnd->ShowSplashScreen(2000,1); // 显示时间为2秒,方式为定时器模式