////////////////////////////////////////////////////////////////
// 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
}