Figure 5   MyPanel.cpp


 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- November 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.
 //
 // ---
 // MyPanel shows how to implement a control panel extension in MFC
 // using the control panel mini-framework (CControlPanelApp and CCPApplet).
 // The framework is implemented in cpanel.cpp and cpanel.h.
 //
 // This file implements two applets: one dialog and one property sheet.
 // 
 #include "StdAfx.h"
 #include "CPanel.h"
 #include "resource.h"
 #include "StatLink.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 //////////////////
 // My control panel app
 //
 class CMyControlPanelApp : public CControlPanelApp {
 public:
    virtual BOOL OnInit();
    DECLARE_DYNAMIC(CMyControlPanelApp)
 } theApp;
 
 // this widget turns on tracing before all static constructors are called
 struct CInitTracing {
    CInitTracing() {  CControlPanelApp::bTRACE=TRUE; }
 } foo;
 
 ////////////////////////////////////////////////////////////////
 // Dialog ("Market Meltdown")
 //
 class CMyDialog : public CDialog {
 protected:
    CStaticLink m_wndLink1;
    CStaticLink m_wndLink2;
    virtual BOOL OnInitDialog();
    virtual void PostNcDestroy() { delete this; }
 public:
    CMyDialog() : CDialog(IDR_MYAPPLET1) { }
    DECLARE_DYNCREATE(CMyDialog);
 };
 IMPLEMENT_DYNCREATE(CMyDialog, CDialog);
 
 BOOL CMyDialog::OnInitDialog()
 {
    // subclass static controls. URL is static text or 3rd arg
    m_wndLink1.SubclassDlgItem(IDC_MSJURL, this);
    m_wndLink2.SubclassDlgItem(IDC_PDURL,  this);
    return CDialog::OnInitDialog();
 }
 
 ////////////////////////////////////////////////////////////////
 // Property sheet ("intergalactic settings")
 //
 
 class CMyPropPage : public CPropertyPage {
 protected:
    CStaticLink m_wndLink1;
    CStaticLink m_wndLink2;
    virtual BOOL OnInitDialog();
    DECLARE_DYNAMIC(CMyPropPage);
 };
 IMPLEMENT_DYNAMIC(CMyPropPage, CPropertyPage);
 
 //////////////////
 // Initialize: subclass web links if on this page.
 //
 BOOL CMyPropPage::OnInitDialog()
 {
    if (GetDlgItem(IDC_MSJURL)) {
       m_wndLink1.SubclassDlgItem(IDC_MSJURL, this);
       m_wndLink2.SubclassDlgItem(IDC_PDURL,  this);
    }
    return CPropertyPage::OnInitDialog();
 }
 
 //////////////////
 // Custom prop sheet has 5 pages.
 //
 class CMyPropSheet : public CPropertySheet {
 public:
    CMyPropSheet();
 protected:
    enum {NPAGES=5};
    CMyPropPage m_mypages[NPAGES];
    virtual void PostNcDestroy() { delete this; }
    DECLARE_DYNCREATE(CMyPropSheet);
 };
 IMPLEMENT_DYNCREATE(CMyPropSheet, CPropertySheet);
 
 //////////////////
 // Construct: set icon and add all the pages.
 //
 CMyPropSheet::CMyPropSheet() : CPropertySheet(IDS_MYPROPSHEETCAPTION)
 {
    m_psh.dwFlags |= PSH_NOAPPLYNOW | PSH_USEHICON;
    m_psh.hIcon = AfxGetApp()->LoadIcon(IDR_MYAPPLET3);
    for (int i=0; i<NPAGES; i++) {
       m_mypages[i].Construct(IDD_PAGE1+i);
       AddPage(&m_mypages[i]);
    }
 }
 
 ////////////////////////////////////////////////////////////////
 // application object
 
 IMPLEMENT_DYNAMIC(CMyControlPanelApp, CControlPanelApp)
 
 //////////////////
 // Control panel initialization similar to InitInstance. The only thing
 // required is that you add at least one CCPApplet.
 //
 BOOL CMyControlPanelApp::OnInit()
 {
    CPTRACEFN(_T("CMyControlPanelApp::OnInit\n"));
 
    // NOTE: I use third arg (bDynamic) = TRUE for testing, so control panel
    // uses CPL_NEWINQUIRE instead of CPL_INQUIRE. For production code, you
    // should change to FALSE (default)
    //
    AddApplet(new CCPApplet(IDR_MYAPPLET1, RUNTIME_CLASS(CMyDialog), TRUE));
    AddApplet(new CCPApplet(IDR_MYAPPLET3, RUNTIME_CLASS(CMyPropSheet),TRUE));
    return CControlPanelApp::OnInit();
 }

Figure 6   Control Panel Messages and the Control Panel Framework

CPlApplet Message
Framework class::function
Need to override?
CPL_INIT
CControlPanelApp::OnInit
Yes. Call AddApplet to add each applet.
CPL_GETCOUNT
None
NA­-CControlPanelApp determines number of applets.
CPL_INQUIRE
CCPApplet:: OnInquire
Rarely
CPL_NEWINQUIRE
CCPApplet::OnNewInquire
Rarely
CPL_DBLCLK
CCPApplet::OnLaunch
Rarely. Only if you have a non-dialog or non-property sheet UI.
CPL_SELECT (obsolete)
CCPApplet::OnSelect
NA
CPL_STOP
CCPApplet::OnStop
Rarely. Use if you have per-appletgarbage collection, but that’s betterdone in your applet destructor.
CPL_EXIT
CControlPanelApp::OnExit
Rarely. Use ExitInstance instead.
CPL_STARTWPARAMS (Windows 98 or Windows NT 4.0)
CCPApplet::OnLaunch
Rarely. Use if you have a non-dialog or non-property sheet UI.


Figure 7   CPanel CPanel.h


 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- November 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.
 //
 #ifndef _CPANEL_H_
 #define _CPANEL_H_
 
 #include "Debug.h"
 
 class CControlPanelApp;
 
 //////////////////
 // This class represents one applet within a control panel extension.
 // Usually, there's only one. If your control panel extension implements
 // more than one applet, then you should use one of these for each applet.
 //
 class CCPApplet : public CCmdTarget {
 public:
    CCPApplet(UINT nIDRes, CRuntimeClass* pDialogClass, BOOL bDynamic=FALSE);
    virtual ~CCPApplet();
 
 protected:
    CControlPanelApp* m_pApp;        // back ptr to owning app
    CRuntimeClass* m_pDialogClass;   // dialog class
    BOOL           m_bDynamic;       // update icon every time CPL starts?
    UINT           m_nIDRes;         // resource ID for this applet
 
    friend class CControlPanelApp;   // OK for control panel to access me
 
    // To implement your applet, override these virtual functions.
    // The only really important one is OnLaunch. 
    //
    virtual LRESULT OnLaunch(CWnd* pWndCpl, LPCSTR lpCmdLine); 
    virtual LRESULT OnInquire(CPLINFO& info);       // CPL_INQUIRE
    virtual LRESULT OnNewInquire(NEWCPLINFO& info); // CPL_NEWINQUIRE
    virtual LRESULT OnSelect();                     // CPL_SELECT
    virtual LRESULT OnStop();                       // CPL_STOP
    DECLARE_DYNAMIC(CCPApplet);
 };
 
 //////////////////
 // To implement a control panel application (DLL), derive your
 // app class from this and override virtual functions if you need to.
 //
 class CControlPanelApp : public CWinApp {
    DECLARE_DYNAMIC(CControlPanelApp)
    static BOOL bTRACE;                  // show TRACE diagnostics
 
 protected:
    CObList m_lsApplets;                 // list of applets
 
    CControlPanelApp();
    virtual ~CControlPanelApp();
 
    BOOL AddApplet(CCPApplet* pApplet);
 
    // OK for control panel callback to access me
    friend LRESULT APIENTRY CPlApplet(HWND, UINT, LPARAM, LPARAM);
 
    // Control panel message handlers.
    // Override these to implement your own control panel extension.
    //
    virtual LRESULT OnCplMsg(HWND hwnd, UINT msg, LPARAM lp1, LPARAM lp2);
    virtual BOOL    OnInit();  // CPL_INIT
    virtual LRESULT OnExit();  // CPL_EXIT
 };
 
 #ifdef _DEBUG
 
 #define CPTRACEFN          \
    CTraceFn __fooble;      \
    if (CControlPanelApp::bTRACE) \
       TRACE                
 #define CPTRACE            \
    if (CControlPanelApp::bTRACE) \
       TRACE
 
 #else // not _DEBUG
 
 #define CPTRACEFN
 #define CPTRACE
 
 #endif // _DEBUG
 
 #endif // _CPANEL_H_
 
CPanel.cpp

 ////////////////////////////////////////////////////////////////
 // Microsoft Systems Journal -- November 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 "CPanel.h"
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #undef THIS_FILE
 static char THIS_FILE[] = __FILE__;
 #endif
 
 // To turn on TRACE diagnostics, set this = TRUE
 BOOL CControlPanelApp::bTRACE = FALSE;
 
 IMPLEMENT_DYNAMIC(CControlPanelApp, CWinApp)
 
 CControlPanelApp::CControlPanelApp()
 {
    CPTRACEFN(_T("CControlPanelApp::CControlPanelApp\n"));
 }
 
 CControlPanelApp::~CControlPanelApp()
 {
    CPTRACEFN(_T("CControlPanelApp::~CControlPanelApp\n"));
 
    // delete all the applets
    while (!m_lsApplets.IsEmpty())
       delete STATIC_DOWNCAST(CCPApplet, m_lsApplets.RemoveHead());
 }
 
 //////////////////
 // Add a new applet to the app. You should call this once for
 // each applet your control panel extension supports.
 //
 BOOL CControlPanelApp::AddApplet(CCPApplet* pApplet)
 {
    CPTRACEFN(_T("CControlPanelApp::AddApplet\n"));
    m_lsApplets.AddTail(pApplet);
    pApplet->m_pApp = this;
    return TRUE;
 }
 
 #define CPL_FIRST CPL_INIT
 #define CPL_LAST  CPL_STARTWPARMS
 
 ///////////////////////////////////////////////////////////////////////////
 // CPlApplet: This is the Control Panel callback function that Windows will
 // call, the function the control panel calls to do stuff.
 // The control panel framework implements most of the functions for you.
 // 
 LRESULT APIENTRY CPlApplet(HWND hwnd, UINT msg, LPARAM lp1, LPARAM lp2)
 {
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
 
 #ifdef _DEBUG
    static LPCSTR MsgNames[] = { 
       NULL,
       _T("CPL_INIT"),      
       _T("CPL_GETCOUNT"),  
       _T("CPL_INQUIRE"),   
       _T("CPL_SELECT"), 
       _T("CPL_DBLCLK"), 
       _T("CPL_STOP"),         
       _T("CPL_EXIT"),      
       _T("CPL_NEWINQUIRE"),
       _T("CPL_STARTWPARMS") };
    CPTRACEFN(_T("CPlApplet(%s,%d,%p)\n"),
       CPL_FIRST<=msg && msg<= CPL_LAST ? MsgNames[msg] : _T(
           "(Unknown)"), lp1, lp2);
 #endif
 
    CControlPanelApp* pApp = STATIC_DOWNCAST(CControlPanelApp, AfxGetApp());
    return pApp->OnCplMsg(hwnd, msg, lp1, lp2);
 }
 
 //////////////////
 // Low-level message handler for all CPL_ messages.
 // You can override this to do funky stuff, like overriding CWnd::WndProc.
 //
 LRESULT CControlPanelApp::OnCplMsg(HWND hwnd, UINT msg, LPARAM lp1, LPARAM lp2)
 {
    // Dispatch on message
    if (msg==CPL_INIT)
       return OnInit();
    else if (msg==CPL_GETCOUNT)
       return m_lsApplets.GetCount();
    else if (msg==CPL_EXIT)
       return OnExit();
    else {
       // It must be one of the applet-specific messages. Get applet.
       //
       POSITION pos = m_lsApplets.FindIndex(lp1);
       ASSERT(pos);
       CCPApplet* pApplet = STATIC_DOWNCAST(CCPApplet, m_lsApplets.GetAt(pos));
       ASSERT_VALID(pApplet);
 
       // Dispatch to appropriate CCPApplet function.
       //
       switch (msg) {
       case CPL_NEWINQUIRE:
          return pApplet->OnNewInquire(* ((NEWCPLINFO*)lp2) );
       case CPL_INQUIRE:
          return pApplet->OnInquire(* ((CPLINFO*)lp2) );
       case CPL_STARTWPARMS:
          pApplet->OnLaunch(CWnd::FromHandle(hwnd), (LPCSTR)lp2);
          return TRUE; // handled
       case CPL_SELECT:
          return pApplet->OnSelect();
       case CPL_DBLCLK:
          return pApplet->OnLaunch(CWnd::FromHandle(hwnd), NULL);
       case CPL_STOP:
          return pApplet->OnStop();
       }
    }
    ASSERT(FALSE);    // shouldn't get here
    return 1;         // failed
 }
 
 //////////////////
 // Default handler for CPL_INIT checks that there's at least one
 // applet registered and that all applets are valid (_DEBUG only).
 //
 BOOL CControlPanelApp::OnInit()
 {
    CPTRACEFN(_T("CControlPanelApp::OnInit\n"));
 #ifdef _DEBUG
    POSITION pos = m_lsApplets.GetHeadPosition();
    if (!pos) {
       CPTRACE(_T("*** No applets registered! You forgot to call AddApplet!\n"));
    } else {
       while (pos) {
          CCPApplet* pApplet = STATIC_DOWNCAST(CCPApplet,
             m_lsApplets.GetNext(pos));
          ASSERT_VALID(pApplet);
       }
    }
 #endif
    return !m_lsApplets.IsEmpty();
 }
 
 //////////////////
 // Default handler for CPL_EXIT: do nothing. 
 // Override if you need to perform cleanup.
 //
 LRESULT CControlPanelApp::OnExit()
 {
    CPTRACEFN(_T("CControlPanelApp::OnExit\n"));
    return 0; // success
 }
 
 ////////////////////////////////////////////////////////////////
 // CCPApplet
 //
 IMPLEMENT_DYNAMIC(CCPApplet, CCmdTarget)
 
 CCPApplet::CCPApplet(UINT nIDRes, CRuntimeClass* pDialogClass, BOOL bDynamic)
 {
    CPTRACEFN(_T("CCPApplet::CCPApplet\n"));
 
    ASSERT(pDialogClass == NULL ||
       pDialogClass->IsDerivedFrom(RUNTIME_CLASS(CDialog)) ||
       pDialogClass->IsDerivedFrom(RUNTIME_CLASS(CPropertySheet)));
    m_pDialogClass = pDialogClass;
    m_nIDRes   = nIDRes;
    m_bDynamic = bDynamic;
 }
 
 CCPApplet::~CCPApplet()
 {
    CPTRACEFN(_T("CCPApplet::~CCPApplet\n"));
 }
 
 //////////////////
 // Default implementation for CPL_NEWINQUIRE:
 // Load up the info struct from resource string.
 // Use the CCPApplet itself as the lData item.
 //
 LRESULT CCPApplet::OnNewInquire(NEWCPLINFO& info) 
 {
    CPTRACEFN(_T("CControlPanelApp::OnNewInquire\n"));
 
    // Fill in the data
    info.dwSize = sizeof(NEWCPLINFO);
    info.dwFlags = 0;
    info.dwHelpContext = 0;
    info.lData = (LONG)this;
    info.hIcon = m_pApp->LoadIcon(m_nIDRes);
 
    // Load resource string.
    // Format is "Icon Name\nLong Description\nFILENAME.HLP".
    // Use AfxExtractSubString to parse the substrings.
    //
    CString sResource, sName, sDescription, sHelpFile;
    if (sResource.LoadString(m_nIDRes)) {
       AfxExtractSubString(sName,        sResource, 0);
       AfxExtractSubString(sDescription, sResource, 1);
       AfxExtractSubString(sHelpFile,    sResource, 2);
    }
    strcpy(info.szName,     sName);
    strcpy(info.szInfo,     sDescription);
    strcpy(info.szHelpFile, sHelpFile);
 
    return 0; // success
 }
 
 //////////////////
 // Default implementation for CPL_INQUIRE:
 // Load up the information struct from resource string.
 // Use the CCPApplet itself as the lData item.
 //
 LRESULT CCPApplet::OnInquire(CPLINFO& info) 
 {
    CPTRACEFN(_T("CControlPanelApp::OnInquire\n"));
 #ifdef _WIN32
    if (m_bDynamic)
       return 1; // (failed)
 
    info.idIcon = m_nIDRes;
    info.idName = m_nIDRes;
    info.idInfo = m_nIDRes;
    info.lData = (LONG)this;
    return 0; // success
 #else
    return 1; // fail
 #endif
 }
 
 //////////////////
 // Default hander for CPL_SELECT: do nothing
 LRESULT CCPApplet::OnSelect() 
 {
    CPTRACEFN(_T("CControlPanelApp::OnSelect\n"));
    return 0; // success
 }
 
 //////////////////
 // User double-clicked an icon: launch the applet.
 LRESULT CCPApplet::OnLaunch(CWnd* pWndCpl, LPCSTR lpCmdLine)
 {
    CWnd* pw = (CWnd*)m_pDialogClass->CreateObject();
    if (pw) {
       if (pw->IsKindOf(RUNTIME_CLASS(CPropertySheet))) {
          CPropertySheet* ps = (CPropertySheet*)pw;
          ps->SetActivePage(lpCmdLine ? atoi(lpCmdLine) : 0);
          ps->DoModal();
       } else if (pw->IsKindOf(RUNTIME_CLASS(CDialog))) {
          CDialog* pd = (CDialog*)pw;
          pd->DoModal();
       }
    }
    return pw==NULL;
 }
 
 //////////////////
 // Default hander for CPL_STOP: do nothing
 LRESULT CCPApplet::OnStop() 
 {
    CPTRACEFN(_T("CControlPanelApp::OnStop\n"));
    return 0; // success
 }