纯C++制作的Splash Screen类(打造专业的启动画面)

纯C++打造的Splash Screen类(打造专业的启动画面)

Introduction

每一个应用程序可能都需要一个“about box”和程序启动时一个“splash screen”(启动画面),大多数开发者都使用自带的““about dialog””。我创建了一个类CSplashScreen,都可以处理这两者,使开发变的容易,有趣!从小的对话框组件程序到需要加在几分复杂程序,我都会使用这个类。


这个类有两个文件组成,SplashScreen.hSplashScreen.cpp。这个类不需要MFC和.NET的支持!


这个类可以从资源编辑器中获取很多信息包括位图,版本字符串等等,显示在“splash screen”上。因此,在程序版本改变的时候,你不需要修改splash screen。


当程序启动时splash screen就会显示,有键盘按下,或者设定时间已过,splash screen就会消失。


Splash Screen Example




Using the code

  • 包含SplashScreen.hSplashScreen.cpp.
  • 在资源编辑器中添加版本字符串
  • 在资源编辑器中添加位图IDB_SPLASH
  • 在连接库添加version.lib


ShowSplashScreen(HWND pParentWnd, LPCTSTR statusMessage, int millisecondsToDisplay) 函数有三个参数。

  • pParentWnd - splash screen的父窗口指针
  • statusMessage - 显示在splash screen状态栏里的字符串
  • millisecondsToDisplay - splash screen持续时间


为了显示splash screen,需要在程序初始化时,添加CSplashScreen::ShowSplashScreen();

#include "SplashScreen.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    MSG msg;
    
    // display splash screen and have it turn off after 10 seconds
    CSplashScreen::ShowSplashScreen( hWnd, "http://applehome.com/", 10000);
  .
  .
  .
    while (GetMessage(&msg, NULL, 0, 0))  {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}


为了显示about box,在WndProc(…)中添加CSplashScreen::ShowSplashScreen()

#include "SplashScreen.h"

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_COMMAND:
        // Parse the menu selections:
        switch (LOWORD(wParam)) {
        case IDM_ABOUT:
            // display about box
            CSplashScreen::ShowSplashScreen( hWnd );
            break;
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


Under the hood

因为我是个顾问,为每一个客户需要创建指定的位图。一般我会特别放置公司的logo和应用程序的icon在IDB_SPLASH位图上。能否创造一个专业的splash screen.取决于你们天才的设计师。版本字符串时写在位图之上的。

有三组字符串:Product Name,Body,和Status。Body是由company name, version, copyright, 和 comment strings一个或多个组成。


每一组 都有指定字符串在该组如何显示的静态变量:

  • m_xxxVerticalOffset - empty space between the top of the bitmap and the first string
  • m_xxxVerticalHeight - the maximum height of a group of strings
  • m_xxxLeftMargin - the distance from the left side to the place strings
  • m_xxxRightMargin - the distance from the right side to strings
  • m_xxxFontName - the name of the font for strings
  • m_xxxPointSize - the point size used for strings, (-1,-1) ==> Calculate point size
  • m_xxxTextColor - the color used for strings
Body有可以有0-4个字符串,设置其是否显示的变量:

  • m_displayCompanyName - true if displaying company name
  • m_displayVersion - true if displaying version
  • m_displayCopyright - true if displaying copyright
  • m_displayComments - true if displaying comments

CSplashScreen类是在CSplashScreen::ShowSplashScreen()被调用时实例化,键盘按下或者时间超过时,被删除。

原文参考地址:http://www.codeproject.com/Articles/35760/Splash-Screen-C-Class-without-MFC-or-NET

CSplashScreen源代码:

// SplashScreen.h: interface for the CSplashScreen class.
//说明:为程序定制启动画面,如果你有完美的图片和设计,你通过它能打造专业的
//      启动画面
//版本:1.0.0.1
//作者:Chris Apple
//时间:21 Apr 2009

//版本:1.0.0.2
//作者:chen_jint
//时间:11 Jul 2014
//修改:
//     1.解决了软件版本信息在VsiualC++2012上不能正确取得问题
//     2.解决了用户设置了显示ProductName,CompanyName等等产品信息,
//       没有设定相关内容,出现异常问题。
//博客:http://blog.csdn.net/chen_jint
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_SPLASHSCREEN_H__44FBD1FF_4158_40A5_BB5C_8BB71EDD2EE7__INCLUDED_)
#define AFX_SPLASHSCREEN_H__44FBD1FF_4158_40A5_BB5C_8BB71EDD2EE7__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CSplashScreen  
{
public:
	CSplashScreen( HWND parentWnd );
	virtual ~CSplashScreen();

	static void ShowSplashScreen( HWND pParentWnd = NULL, LPCTSTR statusMessage = NULL, int millisecondsToDisplay=0 );
	static void HideSplashScreen();

protected:
	BOOL Create( HWND pParentWnd=NULL);
	static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
	void GetVersionStrings();
	void ReportError( LPCTSTR format, ...  );
	BOOL RegisterClass( LPCTSTR szWindowClassName );
	void ClearMessageQueue();

	HFONT CreatePointFont( int pointSize, LPCTSTR fontName, HDC dc);
	SIZE FindFontPointSize( HDC paintDC, LPCTSTR fontName, char **stringsToCheck, int numberOfStringsToCheck, SIZE maximumSize );
	void OnPaint( HWND hWnd );
	void DisplayProductName( HDC paintDC, int windowWidth, int windowHeight );
	void DisplayBody( HDC paintDC, int windowWidth, int windowHeight );
	void DisplayStatusLine( HDC paintDC, int windowWidth, int windowHeight );

private:
	HWND m_hDlg;
	HWND m_hParentWnd;
	HWND m_hWnd;
	HINSTANCE m_instance;
	static CSplashScreen* m_pSplashWnd;
	static ATOM m_szWindowClass ;
	static BOOL m_useStderr;

	HBITMAP m_bitmap;

	static LPTSTR m_productNameString;
	static LPTSTR m_companyNameString;
	static LPTSTR m_versionNumberString;
	static LPTSTR m_versionString;
	static LPTSTR m_copyrightString;
	static LPTSTR m_commentsString;
	static LPTSTR m_statusMessage;

	static int m_millisecondsToDisplay;				// 0 ==> until mouse click or keystroke
	
	// create rectangle that product name has to fit in
	static const int m_productNameVerticalOffset;	// empty space between top 3-D border and product name
	static const int m_productNameVerticalHeight;	// empty space between bottom 3-D border and bottom of product name
	static const int m_productNameLeftMargin;		// distance from left side to place name, company, copyright and version
	static const int m_productNameRightMargin;		// distance from right side to place name, company, copyright and version
	static const LPCTSTR m_productNameFontName;		// name of font for application name
	static SIZE m_productNamePointSize;				// point size used for the application name	
	static COLORREF m_productNameTextColor;			// color used for text

	static const BOOL m_displayCompanyName;			// true if displaying companyName
	static const BOOL m_displayVersion;				// true if displaying version
	static const BOOL m_displayCopyright;			// true if displaying copyright
	static const BOOL m_displayComments;			// true if displaying comments

	// create rectangle that strings in body have to fit in
	static const int m_bodyVerticalOffset;			// empty space between top 3-D border and top of body
	static const int m_bodyVerticalHeight;			// empty space between bottom 3-D border and bottom of body
	static const int m_bodyLeftMargin;				// distance from left side to place company name, copyright, version and comment
	static const int m_bodyRightMargin;				// distance from right side to place company name, copyright, version and comment
	static const LPCTSTR m_bodyFontName;			// name of font for company name, copyright, version and comment	
	static SIZE m_bodyPointSize;					// point size used for company name, copyright, version and comment	
	static COLORREF m_bodyTextColor;				// color used for company name, copyright, version and comment

	static const int m_statusVerticalOffset;		// empty space between top 3-D border and top of status line
	static const int m_statusVerticalHeight;		// empty space between bottom 3-D border and bottom of status line
	static const int m_statusLeftMargin;			// distance from left side to status line
	static const int m_statusRightMargin;			// distance from right side to place status line
	static const LPCTSTR m_statusMessageFontName;	// name of font for status message while starting	
	static SIZE m_statusMessagePointSize;			// point size used for status message while starting	
	static COLORREF m_statusMessageTextColor;		// color used for status message while starting

};

#endif // !defined(AFX_SPLASHSCREEN_H__44FBD1FF_4158_40A5_BB5C_8BB71EDD2EE7__INCLUDED_)

// SplashScreen.cpp: implementation of the CSplashScreen class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "resource.h"
#include "SplashScreen.h"
#include <crtdbg.h>	// for _ASSERT()
#include <stdio.h>	// for vsprintf
#include <stdarg.h>	// for vsprintf

#ifndef ASSERT
#define ASSERT(x) _ASSERT(x)
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
//static CSplashScreen *gDlg;
CSplashScreen* CSplashScreen::m_pSplashWnd = NULL;
ATOM CSplashScreen::m_szWindowClass = 0;
BOOL CSplashScreen::m_useStderr = FALSE;

LPTSTR CSplashScreen::m_productNameString = NULL;
LPTSTR CSplashScreen::m_companyNameString = NULL;
LPTSTR CSplashScreen::m_versionNumberString = NULL;
LPTSTR CSplashScreen::m_versionString = NULL;
LPTSTR CSplashScreen::m_copyrightString = NULL;
LPTSTR CSplashScreen::m_commentsString = NULL;
LPTSTR CSplashScreen::m_statusMessage = NULL;

int CSplashScreen::m_millisecondsToDisplay = 0;					// 0 ==> until mouse click or keystroke

// create rectangle that product name has to fit in
const int CSplashScreen::m_productNameVerticalOffset = 160;		// empty space between top border and Product Name
const int CSplashScreen::m_productNameVerticalHeight = 70;		// maximum height of Product Name
const int CSplashScreen::m_productNameLeftMargin = 20;			// distance from left side to place Product Name
const int CSplashScreen::m_productNameRightMargin = 20;			// distance from right side to place Product Name
const LPCTSTR CSplashScreen::m_productNameFontName = "Arial";	// name of font for Product Name
SIZE CSplashScreen::m_productNamePointSize = {-1,-1};			// point size used for the Product Name, (-1,-1) ==> Calculate point size
COLORREF CSplashScreen::m_productNameTextColor = RGB(109,140,44); // color used for Product Name

const BOOL CSplashScreen::m_displayCompanyName = FALSE;			// true if displaying companyName
const BOOL CSplashScreen::m_displayVersion = TRUE;				// true if displaying version
const BOOL CSplashScreen::m_displayCopyright = TRUE;			// true if displaying copyright
const BOOL CSplashScreen::m_displayComments = TRUE;				// true if displaying comments


// create rectangle that strings in body have to fit in
const int CSplashScreen::m_bodyVerticalOffset = 280;			// empty space between top border and top of body
const int CSplashScreen::m_bodyVerticalHeight = 100;			// maximum height of body
const int CSplashScreen::m_bodyLeftMargin = 120;				// distance from left side to place company name, copyright, version and comment
const int CSplashScreen::m_bodyRightMargin = 10;				// distance from right side to place company name, copyright, version and comment
const LPCTSTR CSplashScreen::m_bodyFontName = "Arial";			// name of font for company name, copyright and version	
SIZE CSplashScreen::m_bodyPointSize = {-1,-1};					// point size used for company name, copyright and version, (-1,-1) ==> Calculate point size	
COLORREF CSplashScreen::m_bodyTextColor = RGB(109,140,44);		// color used for company name, copyright and version (-1 ==> use Product Name color)

// create rectangle for status line string
const int CSplashScreen::m_statusVerticalOffset = 390;			// empty space between top border and top of status string
const int CSplashScreen::m_statusVerticalHeight = 50;			// maximum height of status string
const int CSplashScreen::m_statusLeftMargin = 50;				// distance from left side to place status string
const int CSplashScreen::m_statusRightMargin = 10;				// distance from right side to place status string
const LPCTSTR CSplashScreen::m_statusMessageFontName = "Arial";	// name of font for status string	
SIZE CSplashScreen::m_statusMessagePointSize = {-1,-1};			// point size used for status string, (-1,-1) ==> Calculate point size	
COLORREF CSplashScreen::m_statusMessageTextColor = RGB(0,0,255);// color used for status string (-1 ==> use Product Name color)


CSplashScreen::CSplashScreen( HWND parentWnd )
{
	m_hParentWnd = parentWnd;

	GetVersionStrings();
}

CSplashScreen::~CSplashScreen()
{
	if ( m_statusMessage ) {
		delete m_statusMessage;
		m_statusMessage = NULL;
	}
}
void CSplashScreen::HideSplashScreen()
{

	// Destroy the window, and update the mainframe.
	if ( m_pSplashWnd != NULL ) {
		HWND hParentWnd = m_pSplashWnd->m_hParentWnd;
		::DestroyWindow( m_pSplashWnd->m_hWnd );
		if ( hParentWnd && ::IsWindow( hParentWnd ) )
			::UpdateWindow( hParentWnd );
	}
}

void CSplashScreen::ShowSplashScreen(  HWND pParentWnd /*= NULL*/, LPCTSTR statusMessage /*= NULL*/, int millisecondsToDisplay /*= 0*/ )
{
	if ( m_statusMessage ) {
		delete m_statusMessage;
		m_statusMessage = NULL;
	}

	if ( statusMessage ) {
		m_statusMessage = new char[strlen(statusMessage) + 1];
		strcpy ( m_statusMessage, statusMessage );
	}

	m_millisecondsToDisplay = millisecondsToDisplay;

	// Allocate a new splash screen, and create the window.
	if ( m_pSplashWnd == NULL ) {
		m_pSplashWnd = new CSplashScreen ( pParentWnd );
		if (!m_pSplashWnd->Create(pParentWnd)) {
			delete m_pSplashWnd;
			m_pSplashWnd = NULL;
		}
	}
	if ( pParentWnd ) {
		UpdateWindow( pParentWnd);
	}

	// Set a timer to destroy the splash screen.
	if ( m_millisecondsToDisplay ) {
		SetTimer(m_pSplashWnd->m_hWnd, 1, m_millisecondsToDisplay, NULL);
	}

	ShowWindow ( m_pSplashWnd->m_hWnd, SW_SHOW );
	UpdateWindow( m_pSplashWnd->m_hWnd );
	InvalidateRect( m_pSplashWnd->m_hWnd, NULL, FALSE );
	
	// make sure paint message happens
	m_pSplashWnd->ClearMessageQueue();
}
void CSplashScreen::ReportError( LPCTSTR format, ... )
{
	TCHAR buffer[4096];
	va_list argp;
	va_start(argp, format);
	vsprintf(buffer, format, argp);
	va_end(argp);
	if (m_useStderr) {
		fprintf(stderr, buffer);
	} else {
		// OutputDebugString(buffer);
		MessageBox ( m_hWnd, buffer, "Error", MB_ICONERROR );
	}
}
BOOL CSplashScreen::RegisterClass( LPCTSTR szWindowClassName )
{
	m_instance = GetModuleHandle ( NULL );

	// register class
	DWORD lastError;
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= m_instance;
	wcex.hIcon			= NULL;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClassName;
	wcex.hIconSm		= NULL;

	m_szWindowClass = RegisterClassEx(&wcex);
	if ( m_szWindowClass == 0 ) {
		lastError = ::GetLastError();
		char errorBuffer[_MAX_PATH];
		sprintf( errorBuffer, "Failed to register class - error %d", lastError );
		ReportError ( errorBuffer );
		return FALSE;
	}
	return TRUE;
}

BOOL CSplashScreen::Create( HWND pParentWnd /*= NULL*/)
{
	m_instance = GetModuleHandle ( NULL );

	m_bitmap = LoadBitmap ( m_instance, MAKEINTRESOURCE (IDB_SPLASH) );

	HWND hwndDesktop = GetDesktopWindow(); 
    HDC hdcDesktop = GetDC(hwndDesktop); 

	BITMAPINFO bitmapInfo;
	memset ( &bitmapInfo, 0, sizeof(BITMAPINFOHEADER) );
	bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	int scanLines = GetDIBits(hdcDesktop,		// handle to DC
							  m_bitmap,			// handle to bitmap
							  0,				// first scan line to set
							  0,				// number of scan lines to copy
							  NULL,				// array for bitmap bits
							  &bitmapInfo,		// bitmap data buffer
							  DIB_RGB_COLORS );	// RGB or palette index

	LPCTSTR szTitle = "";
	LPCTSTR szWindowClassName = "SplashScreen";

	// register splash window class if not already registered
	if ( m_szWindowClass == 0 ) {
		BOOL result = RegisterClass( szWindowClassName );
		if ( !result )
			return FALSE;
	}

	DWORD exStyle = 0;
	int xPos = 0;
	int yPos = 0;
	int width = bitmapInfo.bmiHeader.biWidth;
	int height = bitmapInfo.bmiHeader.biHeight;

	// if parent window, center it on the parent window. otherwise center it on the screen
	RECT parentRect;
	if ( pParentWnd == NULL ) {
		::GetWindowRect ( GetDesktopWindow(), &parentRect );
	} else {
		::GetWindowRect ( pParentWnd, &parentRect );
	}
	HWND hwnd = GetDesktopWindow();

	xPos = parentRect.left + (parentRect.right - parentRect.left)/2 - (width/2);
	yPos = parentRect.top + (parentRect.bottom - parentRect.top)/2 - (height/2);

	HMENU menu = NULL;
	m_hWnd = CreateWindowEx( exStyle, szWindowClassName, szTitle, WS_POPUP | WS_VISIBLE,
      xPos, yPos, width, height, pParentWnd, menu, m_instance, this);
	
	if ( m_hWnd == NULL ) {
		DWORD lastError = ::GetLastError();
		char errorBuffer[_MAX_PATH];
		sprintf( errorBuffer, "Failed to create window- error %d", lastError );
		ReportError ( errorBuffer );
		return FALSE;
	}

	// if no parent window, make it a topmost, so eventual application window will appear under it
	if ( pParentWnd == NULL ) {
		::SetWindowPos( m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
	}


	return TRUE;
}
void CSplashScreen::ClearMessageQueue()
{
	MSG msg;
    while (PeekMessage(&msg, m_hWnd,  0, 0, PM_REMOVE)) { 
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

LRESULT CALLBACK CSplashScreen::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) {

	case WM_PAINT:
		m_pSplashWnd->OnPaint ( hWnd );
		break;

	case WM_NCDESTROY:
		delete m_pSplashWnd;
		m_pSplashWnd = NULL;
		break;

	case WM_TIMER:
		m_pSplashWnd->HideSplashScreen();
		break;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_NCLBUTTONDOWN:
	case WM_NCRBUTTONDOWN:
	case WM_NCMBUTTONDOWN:
		m_pSplashWnd->HideSplashScreen();
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}
HFONT CSplashScreen::CreatePointFont( int pointSize, LPCTSTR fontName, HDC dc)
{
	HFONT font;

	LOGFONT logicalFont;
	memset ( &logicalFont, 0, sizeof(LOGFONT) );
	strcpy ( logicalFont.lfFaceName, fontName );
	
	logicalFont.lfHeight = -MulDiv(pointSize, GetDeviceCaps(dc, LOGPIXELSY), 72); //pointSize * 10;
	font = CreateFontIndirect( &logicalFont );
	return font;
}

SIZE CSplashScreen::FindFontPointSize( HDC paintDC, LPCTSTR fontName, char **stringsToCheck, int numberOfStringsToCheck, SIZE maximumSize )
{
	HFONT font;
	int pointSize = 8;
	SIZE previousLargest;
	SIZE largest;
	previousLargest.cx = 0;
	previousLargest.cy = 0;
	largest.cx = 0;
	largest.cy = 0;

	LOGFONT logicalFont;
	memset ( &logicalFont, 0, sizeof(LOGFONT) );
	strcpy ( logicalFont.lfFaceName, fontName );

	maximumSize.cy /= numberOfStringsToCheck;
	while ( 1 ) {
	
		logicalFont.lfHeight = -MulDiv(pointSize, GetDeviceCaps(paintDC, LOGPIXELSY), 72); //pointSize * 10;
		font = CreateFontIndirect( &logicalFont );
		HFONT originalFont = (HFONT)SelectObject( paintDC, font );

		char **stringsPtr = stringsToCheck;
		for ( int i=0; i<numberOfStringsToCheck; i++ ) { 
			LPCTSTR string = *stringsPtr++;
			int stringLength = strlen ( string );
			SIZE szString;
			GetTextExtentPoint32( paintDC, string, stringLength, &szString );
			if (  largest.cx < szString.cx ) {
				previousLargest = largest;
				largest.cx = szString.cx;
				largest.cy = szString.cy;
			}
		}

		SelectObject( paintDC, originalFont );
		DeleteObject( font );

		if ( largest.cx > maximumSize.cx )
			break;
		if ( largest.cy > maximumSize.cy )
			break;

		pointSize += 2;

	}
	pointSize -= 2;
	SIZE ret;
	ret.cx = previousLargest.cy;		// in cx, return actual height of font in device units
	ret.cy = pointSize;
	return ret;
}

void CSplashScreen::DisplayProductName( HDC paintDC, int windowWidth, int windowHeight )
{
	SIZE sectionSize;

	int productNameLeftMargin = m_productNameLeftMargin;
	int productNameRightMargin = windowWidth - m_productNameRightMargin;
	int widthOfProductName = productNameRightMargin - productNameLeftMargin;
	sectionSize.cx = widthOfProductName;
	sectionSize.cy = m_productNameVerticalHeight;

	if ( (m_productNamePointSize.cx == -1) && (m_productNamePointSize.cy == -1) ) {
		ASSERT ( m_productNameString );	// Check Resources: version : ProductName
		m_productNamePointSize = FindFontPointSize( paintDC, m_productNameFontName, &m_productNameString, 1, sectionSize );
	}

	int topOfText = m_productNameVerticalOffset; 
	int bottomOfText = topOfText + m_productNameVerticalHeight; 
	RECT productNameRect;
	productNameRect.left = productNameLeftMargin;
	productNameRect.top = topOfText;
	productNameRect.right = productNameRightMargin;
	productNameRect.bottom = bottomOfText;

	HFONT productNameFont = CreatePointFont( m_productNamePointSize.cy, m_productNameFontName, paintDC );
	HFONT originalFont = (HFONT)SelectObject( paintDC, productNameFont );

	SetTextColor( paintDC, m_productNameTextColor );
	SetBkMode( paintDC, TRANSPARENT);
	DrawText( paintDC, m_productNameString, strlen(m_productNameString), &productNameRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );

	SelectObject( paintDC, originalFont);
	DeleteObject( productNameFont );
}


void CSplashScreen::DisplayBody( HDC paintDC, int windowWidth, int windowHeight )
{
	SIZE sectionSize;
	int bodyLeftMargin = m_bodyLeftMargin;
	int bodyRightMargin = windowWidth - m_bodyRightMargin;
	int widthOfBody = bodyRightMargin - bodyLeftMargin;
	sectionSize.cx = widthOfBody;
	sectionSize.cy = m_bodyVerticalHeight;


	char * stringsToCheck[6];
	char ** pStringsToCheck = &stringsToCheck[0];
	//int stringsInBody = (int)m_displayCompanyName + (int)m_displayVersion + (int)m_displayCopyright + (int)m_displayComments;
	if ( (m_bodyPointSize.cx == -1) && (m_bodyPointSize.cy == -1) ) {
		if ( m_displayCompanyName && m_companyNameString ) //by chen
			*pStringsToCheck++ = m_companyNameString;
		if ( m_displayVersion && m_versionString ) //by chen
			*pStringsToCheck++ = m_versionString;
		if ( m_displayCopyright && m_copyrightString ) //by chen
			*pStringsToCheck++ = m_copyrightString;
		if ( m_displayComments && m_commentsString ) //by chen
			*pStringsToCheck++ = m_commentsString;
		int stringsInBody = pStringsToCheck - &stringsToCheck[0];
		m_bodyPointSize= FindFontPointSize( paintDC, m_bodyFontName, stringsToCheck, stringsInBody, sectionSize );
	}

	// check that strings will fit vertically
	int singleStringOfBodyHeight = m_bodyPointSize.cx;

	int topOfText = m_bodyVerticalOffset; 
	RECT companyNameRect;
	if ( m_displayCompanyName && m_companyNameString) {
		companyNameRect.left = bodyLeftMargin;
		companyNameRect.top = topOfText;
		companyNameRect.right = bodyRightMargin;
		companyNameRect.bottom = topOfText+singleStringOfBodyHeight;
		topOfText += singleStringOfBodyHeight;
	}

	RECT versionRect;
	if ( m_displayVersion && m_versionString ) {
		versionRect.left = bodyLeftMargin;
		versionRect.top = topOfText;
		versionRect.right = bodyRightMargin;
		versionRect.bottom = topOfText+singleStringOfBodyHeight;
		topOfText += singleStringOfBodyHeight;
	}

	RECT copyrightRect;
	if ( m_displayCopyright && m_copyrightString ) {
		copyrightRect.left = bodyLeftMargin;
		copyrightRect.top = topOfText;
		copyrightRect.right = bodyRightMargin;
		copyrightRect.bottom = topOfText+singleStringOfBodyHeight;
		topOfText += singleStringOfBodyHeight;
	}

	RECT commentsRect;
	if ( m_displayComments && m_commentsString ) {
		commentsRect.left = bodyLeftMargin;
		commentsRect.top = topOfText;
		commentsRect.right = bodyRightMargin;
		commentsRect.bottom = topOfText+singleStringOfBodyHeight;
		topOfText += singleStringOfBodyHeight;
	}


	// display body (Company Name, Version, Copyright and Comments)
	HFONT bodyFont = CreatePointFont( m_bodyPointSize.cy, m_bodyFontName, paintDC );
	HFONT originalFont = (HFONT)SelectObject( paintDC, bodyFont );
	SetTextColor( paintDC, (m_bodyTextColor == -1) ? m_productNameTextColor : m_bodyTextColor );
	SetBkMode(paintDC, TRANSPARENT);
	if ( m_displayCompanyName && m_companyNameString )
		DrawText( paintDC, m_companyNameString, strlen(m_companyNameString), &companyNameRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );
	if ( m_displayVersion && m_versionString)
		DrawText( paintDC, m_versionString, strlen(m_versionString), &versionRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );
	if ( m_displayCopyright && m_copyrightString)
		DrawText( paintDC, m_copyrightString, strlen(m_copyrightString), &copyrightRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );
	if ( m_displayComments && m_commentsString )
		DrawText( paintDC, m_commentsString, strlen(m_commentsString), &commentsRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );

	SelectObject( paintDC, originalFont);
	DeleteObject( bodyFont );
}

void CSplashScreen::DisplayStatusLine( HDC paintDC, int windowWidth, int windowHeight )
{
	SIZE sectionSize;
	int statusLeftMargin = m_statusLeftMargin;
	int statusRightMargin = windowWidth - m_statusRightMargin;
	int widthOfStatus = statusRightMargin - statusLeftMargin;
	sectionSize.cx = widthOfStatus;
	sectionSize.cy = m_statusVerticalHeight;

	int topOfText = m_statusVerticalOffset; 
	int bottomOfText = topOfText + m_statusVerticalHeight; 
	RECT statusRect;
	statusRect.left = 0;
	statusRect.top = topOfText;
	statusRect.right = windowWidth;
	statusRect.bottom = topOfText + m_statusVerticalHeight;

	if ( (m_statusMessagePointSize.cx == -1) && (m_statusMessagePointSize.cy == -1) ) {
		m_statusMessagePointSize = FindFontPointSize( paintDC, m_statusMessageFontName, &m_statusMessage, 1, sectionSize );
	}

	HFONT statusFont = CreatePointFont( m_statusMessagePointSize.cy, m_statusMessageFontName, paintDC );
	HFONT originalFont = (HFONT)SelectObject( paintDC, statusFont );

	SetTextColor( paintDC, (m_bodyTextColor == -1) ? m_productNameTextColor : m_statusMessageTextColor );
	SetBkMode( paintDC, TRANSPARENT );
	DrawText( paintDC, m_statusMessage, strlen(m_statusMessage), &statusRect, DT_VCENTER|DT_CENTER|DT_SINGLELINE );
	
	SelectObject( paintDC, originalFont);
	DeleteObject( statusFont );
}
void CSplashScreen::OnPaint( HWND hWnd )
{
	PAINTSTRUCT ps;
	HDC paintDC = BeginPaint(hWnd, &ps);


	HDC imageDC = ::CreateCompatibleDC( paintDC );

	BITMAPINFO bitmapInfo;
	memset ( &bitmapInfo, 0, sizeof(BITMAPINFOHEADER) );
	bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	int scanLines = GetDIBits(imageDC,		// handle to DC
							  m_bitmap,			// handle to bitmap
							  0,				// first scan line to set
							  0,				// number of scan lines to copy
							  NULL,				// array for bitmap bits
							  &bitmapInfo,		// bitmap data buffer
							  DIB_RGB_COLORS );	// RGB or palette index

	// Paint the bitmap image.
	HBITMAP pOldBitmap = (HBITMAP)SelectObject( imageDC, m_bitmap );
	int width = bitmapInfo.bmiHeader.biWidth;
	int height = bitmapInfo.bmiHeader.biHeight;
	BitBlt( paintDC, 0, 0, width, height, imageDC, 0, 0, SRCCOPY );
	SelectObject( imageDC, pOldBitmap );

	// calculate height of strings
	int heightOfDialogFace = height;
	int widthOfDialogFace = width;

	DisplayProductName( paintDC, width, height );
	DisplayBody( paintDC, width, height );

	if ( m_statusMessage ) {
		DisplayStatusLine( paintDC, width, height );
	}

	EndPaint(hWnd, &ps);
}

void CSplashScreen::GetVersionStrings()
{	
	DWORD dwHandle;			// ignored 
	char *buf;				// pointer to buffer to receive file-version info.

	// get name of executable
	char moduleName[_MAX_PATH];
	::GetModuleFileName ( NULL, moduleName, sizeof(moduleName) );

	// Get the size of the version information.
	DWORD verSize = GetFileVersionInfoSize(
		moduleName,	// pointer to filename string
		&dwHandle		// pointer to variable to receive zero
	);

	if (verSize != 0)
	{
		buf = new char[verSize + 1];

		BOOL res = GetFileVersionInfo(
				moduleName,	// pointer to filename string
				NULL,			// ignored 
				verSize,		// size of buffer
				buf 			// pointer to buffer to receive file-version info.
		);
		ASSERT(res);

		UINT ver = (UINT)verSize;

		LPVOID lplpBuffer;
		BOOL doThis = VerQueryValue (buf, TEXT("\\StringFileInfo\\041104B0\\ProductName"), &lplpBuffer, &ver);
		if ( doThis ) {
			m_productNameString = new TCHAR[ver+1];
			strcpy(m_productNameString, (char *)lplpBuffer);
		}

		doThis = VerQueryValue (buf, TEXT("\\StringFileInfo\\041104B0\\CompanyName"), &lplpBuffer, &ver);
		if ( doThis ) {
			m_companyNameString = new TCHAR[ver+1];
			strcpy(m_companyNameString, (char *)lplpBuffer);
		}

		doThis = VerQueryValue (buf, TEXT("\\StringFileInfo\\041104B0\\LegalCopyright"), &lplpBuffer, &ver);
		if ( doThis ) {
			m_copyrightString = new TCHAR[ver+1];
			strcpy(m_copyrightString, (char *)lplpBuffer);
		}

		doThis = VerQueryValue (buf, TEXT("\\StringFileInfo\\041104B0\\Comments"), &lplpBuffer, &ver);
		if ( doThis ) {
			m_commentsString = new TCHAR[ver+1];
			strcpy(m_commentsString, (char *)lplpBuffer);
		}

		doThis = VerQueryValue (buf, TEXT("\\StringFileInfo\\041104B0\\ProductVersion"), &lplpBuffer, &ver);
		if ( doThis ) {
			/*int versionNumbers[4];
			int numberOfValues = sscanf ( (char *)lplpBuffer, "%d,%d,%d,%d", &versionNumbers[0],&versionNumbers[1], &versionNumbers[2], &versionNumbers[3] );
			char numberString[12*4+1];
			if ( versionNumbers[3] != 0 ) {
				sprintf ( numberString, "%d.%d.%d.%d", versionNumbers[3],versionNumbers[2], versionNumbers[1], versionNumbers[0] );
			} else if ( versionNumbers[2] != 0 ) {
				sprintf ( numberString, "%d.%d.%d", versionNumbers[2], versionNumbers[1], versionNumbers[0] );
			} else if ( versionNumbers[1] != 0 ) {
				sprintf ( numberString, "%d.%d",  versionNumbers[1], versionNumbers[0] );
			} else {
				sprintf ( numberString, "%d", versionNumbers[0] );
			}
			int numberLength = strlen ( numberString );
			m_versionNumberString = new TCHAR[numberLength+1];
			strcpy ( m_versionNumberString, numberString );*/
			m_versionNumberString = new TCHAR[ver];
			strcpy ( m_versionNumberString, (char *)lplpBuffer);

			LPCTSTR versionPrefix = "Version ";
			int versionNumberStringLength = strlen( versionPrefix ) + strlen ( m_versionNumberString );
			m_versionString = new char [versionNumberStringLength+1];

			strcpy ( m_versionString, "Version " );
			strcat ( m_versionString, m_versionNumberString );
		}
		delete buf;
	}
}