October 1999
Figure 3   Splash

Splash.h

////////////////////////////////////////////////////////////////
// Microsoft Systems Journal -- October 1999
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
//
#pragma once
#ifndef NODIB
#include "dib.h"
#endif

//////////////////////////////////////////////////////////////////
// Splash screen. To use it, write:
//
// CSplash *pSplash = new CSplash(
//    IDB_MYBITMAP,        // resource ID of bitmap
//    duration,            // min time to display, in msec
//    flags,               // see below
//    &pSplash);           // address of back pointer
//
// If you want to kill the screen, you can call
//
// if (pSplash)
//    pSplash->Kill();
//
// but this is usually unnecessary. You don't have to call delete either;
// CSplash will delete itself. When it does, it sets your pointer to NULL so
// you won't try to call Kill on a bad pointer.
//
class CSplash : public CWinThread {
public:
   CSplash(UINT nIDRes,          // resource ID of bitmap
      UINT duration,             // how long to show (minimum)
      WORD flags=0,              // see below
      CSplash** ppBackPtr=NULL); // pointer to NULL when destroyed
   ~CSplash();

   enum { // flags
      KillOnClick = 0x0001,      // any key/mouse dismisses splash
      IgnoreCmdLine = 0x0002,    // need I say more?
   };

   // override to create a different kind of splash window
   virtual CWnd* OnCreateSplashWnd(UINT nIDRes, UINT duration, WORD flags);
   void Kill();                  // kill the splash screen

protected:
   CSplash**   m_ppBackPtr;      // caller's back pointer to me
   UINT        m_nIDRes;         // bitmap resource ID
   UINT        m_duration;       // how long to display
   WORD        m_flags;          // CSplashWnd creation flags

   virtual BOOL InitInstance();
   DECLARE_DYNAMIC(CSplash)
};

//////////////////
// Splash window. This class is private to CSplash--Don't use it unless
// you are doing some hairy stuff to override the splash window, like
// create animated effects, etc.
// 
class CSplashWnd : public CWnd {
protected:
   friend CSplash;
   CSplashWnd();
   ~CSplashWnd();

#ifdef NODIB
   CBitmap  m_bitmap;      // ordinary MFC bitmap
#else
   CDib     m_dib;         // Device independent bitmap
#endif
   UINT     m_duration;    // duration (msec)
   WORD     m_flags;       // see below

   // override to do weird stuff
   virtual BOOL Create(UINT nIDRes, UINT duration, WORD flags);

   virtual BOOL PreTranslateMessage(MSG* pMsg);
   virtual void PostNcDestroy();

   afx_msg int  OnCreate(LPCREATESTRUCT lpCreateStruct);
   afx_msg void OnClose();
   afx_msg void OnPaint();
   afx_msg void OnTimer(UINT nIDEvent);
   DECLARE_MESSAGE_MAP()
   DECLARE_DYNAMIC(CSplashWnd)
};
Splash.cpp
/////////////////////////////////////////////////////////////////
// Microsoft Systems Journal -- October 1999
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
//
#include "stdafx.h"
#include "Splash.h"
#include "cmdline.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC(CSplashWnd, CWnd)
BEGIN_MESSAGE_MAP(CSplashWnd, CWnd)
   ON_WM_CREATE()
   ON_WM_CLOSE()
   ON_WM_PAINT()
   ON_WM_TIMER()
END_MESSAGE_MAP()

////////////////////////////////////////////////////////////////
// CSplashWnd : the splash window

CSplashWnd::CSplashWnd()
{
}

CSplashWnd::~CSplashWnd()
{
}

//////////////////
// Create splash window: load bitmap and create the window
//
BOOL CSplashWnd::Create(UINT nIDRes, UINT duration, WORD flags)
{
#ifdef NODIB
   // Code for ordinary bitmap (assumes m_bitmap is a CBitmap)
   if (!m_bitmap.LoadBitmap(nIDRes))
      return FALSE;
   BITMAP bm;
   m_bitmap.GetBitmap(&bm);
   CSize sz(bm.bmWidth, bm.bmHeight);
#else
   if (!m_dib.Load(nIDRes))
      return FALSE;
   CSize sz = m_dib.GetSize();
#endif

   m_duration = duration;
   m_flags = flags;
   return CreateEx(0,
      AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_ARROW)),
      NULL,
      WS_POPUP | WS_VISIBLE,
      0, 0, sz.cx, sz.cy,
      NULL, // parent wnd
      NULL);
}

//////////////////
// Handle close message: quit
//
void CSplashWnd::OnClose()
{
   DestroyWindow();
}

//////////////////
// Splash window created: center it, move to foreground and set a timer
//
int CSplashWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
   if (CWnd::OnCreate(lpCreateStruct) == -1)
      return -1;
   CenterWindow();
   UpdateWindow();
   SetForegroundWindow();
   if (m_duration!=-1)
      SetTimer(1, m_duration, NULL);
   return 0;
}

//////////////////
// The window has been destroyed: put main app in foreground
// and post a message to quit this thread.
//
void CSplashWnd::PostNcDestroy()
{
   CWinApp* pApp = AfxGetApp();
   CWnd* pMainWnd = pApp->m_pMainWnd;
   if (pMainWnd && IsWindow(pMainWnd->m_hWnd))
      ::SetForegroundWindow(pMainWnd->m_hWnd);
   delete this;
}

//////////////////
// Draw the bitmap.
//
void CSplashWnd::OnPaint()
{
   CPaintDC dc(this);
#ifdef NODIB
   // Code for ordinary bitmap:
   CDC dcImage;
   if (!dcImage.CreateCompatibleDC(&dc))
      return;
   BITMAP bm;
   m_bitmap.GetBitmap(&bm);

   // Paint the image.
   CBitmap* pOldBitmap = dcImage.SelectObject(&m_bitmap);
   dc.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcImage, 0, 0, SRCCOPY);
   dcImage.SelectObject(pOldBitmap);
#else
   m_dib.Draw(dc);
#endif
}

//////////////////
// Timer expired: kill myself--unless the app has
// not created a main window yet.
//
void CSplashWnd::OnTimer(UINT nIDEvent)
{
   CWinApp* pApp = AfxGetApp();
   CWnd* pMainWnd = pApp->m_pMainWnd;
   if ((m_flags & CSplash::NoWaitForMainWnd) ||
                  IsWindow(pMainWnd->GetSafeHwnd()))
                  // have main window: OK to die
                  SendMessage(WM_CLOSE)
   else
      // no main window: keep splashing
      SetTimer(1,100,NULL);
}

//////////////////
// Before translating keystroke or mouse: die
//
BOOL CSplashWnd::PreTranslateMessage(MSG* pMsg)
{
   if (m_flags & CSplash::KillOnClick) {
      UINT msg = pMsg->message;
      if (msg == WM_KEYDOWN ||
          msg == WM_SYSKEYDOWN ||
          msg == WM_LBUTTONDOWN ||
          msg == WM_RBUTTONDOWN ||
          msg == WM_MBUTTONDOWN)
      {
         PostMessage(WM_CLOSE); // post don't send, to let current msg process
         return TRUE; // eat it
      }
   }
   return CWnd::PreTranslateMessage(pMsg);
}

////////////////////////////////////////////////////////////////
// CSplash, a thread object

IMPLEMENT_DYNAMIC(CSplash, CWinThread)

//////////////////
// Create a new splash thread
//
CSplash::CSplash(UINT nIDRes, UINT duration, WORD flags, CSplash** ppBackPtr)
{
   m_ppBackPtr = ppBackPtr;
   m_nIDRes = nIDRes;
   m_duration = duration;
   m_flags = flags;
   CreateThread();
}

/////////////////
// Destruction: Set caller's pointer to NULL, so he knows I'm gone.
//
CSplash::~CSplash()
{
   if (m_ppBackPtr)
      *m_ppBackPtr = NULL;
}

//////////////////
// Thread initialization.
// Returns TRUE to keep running, otherwise FALSE
// if I determine I'm not supposed to run the splash
//
BOOL CSplash::InitInstance()
{
   // Check for -nologo switch
   CWinApp* pApp = AfxGetApp();
   ASSERT(pApp);

   // Look for -nologo switch, or any others that MFC thinks should
   // prohibit a splash screen (such as OLE embedding, etc.)
   //
   if (!(m_flags & IgnoreCmdLine)) {
      CCommandLineInfoEx cmdinfo;
      pApp->ParseCommandLine(cmdinfo);
      if (!cmdinfo.m_bShowSplash || cmdinfo.GetOption(_T("nologo")))
         return FALSE;
   }
   if (!AfxOleGetUserCtrl())  // running without UI: to be safe
      return FALSE;

   // Create the splash window
   m_pMainWnd = OnCreateSplashWnd(m_nIDRes, m_duration, m_flags);
   return m_pMainWnd != NULL;
}

//////////////////
// Create the splash window. This is virtual so you can override to create
// some other kind of window if you like. 
//
CWnd* CSplash::OnCreateSplashWnd(UINT nIDRes, UINT duration, WORD flags)
{
   CSplashWnd *pSplashWnd = new CSplashWnd;
   if (pSplashWnd)
      pSplashWnd->Create(nIDRes, duration, flags);
   return pSplashWnd;
}

//////////////////
// Kill the splash window. Could set a CEvent to terminate thread,
// but easier to simply post a close message to the window.
//
void CSplash::Kill()
{
   if (m_pMainWnd)
      m_pMainWnd->PostMessage(WM_CLOSE);
}

Figure 5   Standard Command-Line Switches

Switch
Function
-pt
Print to specific printer,driver,port
-p
Print
-Unregister
Unregister the app/server
-Unregserver
Same as -Unregister
-dde
DDE command
-Embedding
Run app as embedded OLE object
-Automation
Run app as automation server


Figure 6   CmdLine

CmdLine.h


////////////////////////////////////////////////////////////////
// Microsoft Systems Journal -- October 1999
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT too.
//
#pragma once

//////////////////
// Improved CCommandLineInfo parses arbitrary switches.
// Use instead of CCommandLineInfo:
//
//    CCommandLineInfoEx cmdinfo;
//    ParseCommandLine(cmdinfo); // (from app object)
//
// After parsing, you can call GetOption to get the value of any switch. Eg:
//
//    if (cmdinfo.GetOption("nologo")) {
//       // handle it
//    }
//
// to get the value of a string option, type
//
//    CString filename;
//    if (cmdinfo.GetOption("f")) {
//       // now filename is string following -f option
//    }
//
class CCommandLineInfoEx : public CCommandLineInfo {
public:
   BOOL GetOption(LPCTSTR option, CString& val);
   BOOL GetOption(LPCTSTR option) {
      return GetOption(option, CString());
   }

protected:
   CMapStringToString m_options; // hash of options
   CString  m_sLastOption;       // last option encountered
   virtual void ParseParam(const TCHAR* pszParam, BOOL bFlag, BOOL bLast);
};
CmdLine.cpp

////////////////////////////////////////////////////////////////
// Microsoft Systems Journal -- October 1999
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Compiles with Visual C++ 6.0, runs on Windows 98 and probably Windows NT 
//
#include "stdafx.h"
#include "cmdline.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//////////////////
// Parse a command line parameter/token. Just add it to the table.
// 
void CCommandLineInfoEx::ParseParam(const TCHAR* pszParam, BOOL bFlag, 
                                    BOOL bLast)
{
   if (bFlag) {
      // this is a "flag" (begins with / or -)
      m_options[pszParam] = "TRUE";    // default value is "TRUE"
      m_sLastOption = pszParam;        // save in case other value specified

   } else if (!m_sLastOption.IsEmpty()) {
      // last token was option: set value
      m_options[m_sLastOption] = pszParam;
      m_sLastOption.Empty(); // clear
   }

   // Call base class so MFC can see this param/token.
   CCommandLineInfo::ParseParam(pszParam, bFlag, bLast);
}

BOOL CCommandLineInfoEx::GetOption(LPCTSTR option, CString& val)
{
   return m_options.Lookup(option, val);
}