http://www.developer.com/ws/pc/article.php/3487211/An-Ancient-Story-of-Control-Panel-Applets.htm
Control Panel is really an ancient creature in MS Windows. It was introduced when Windows first began. But nevertheless, it is still alive, and can serve your needs perfectly, providing an easy and powerful mechanism for Control Panel applets. You will definitely find a lot of articles on this theme all over the Web, which more or less desribe what you should do to develop an applet. In this tutorial, I'd like to combine different aspects of Control Panel programming, so you will be able to get a relatively full picture of what's happening there. Prior to diving into coding, let me formalize what I am going to speak about. Each Control Panel applet is just a DLL that exports only one function: CPlApplet. Well, applets also have an extension of 'cpl' instead of 'dll,' so you will simply need to change it in project settings. Thus, a typical DEF-file may look like this: CPlApplet is called upon for a few basic events. I'll show you its schematic definition: You'll find it quite simple. The OS informs your DLL when it should perform the following actions: That is actually the whole fact sheet you need to know about this business to start programming Control Panel applets. Well, you will be quite right in claiming that all that was written above is not enough to produce a standard-fit CP applet. This section will provide you with some additional useful information on this topic. You will add some flesh to the previously created skeleton. First, I'll present the NEWCPLINFO struct. A pointer to this data is sent to CPlApplet as an lParam2 parameter of the CPL_NEWINQUIRE message when Control Panel needs to obtain initial information regarding your applet and initialize it. The following table contains some short explanations and comments on NEWCPLINFO's members: To illustrate all this, the tiny code snippet below guides you through the flow: Microsoft's GUI requirements for 'Pocket PC breeded' systems say that you should create a full-screen window with its own title (not a navigation bar). Surely, you are free to choose which GUI you will support in your own applet. But, as in many other cases, a standard look-and-feel will be more comfortable for the end user. Hence, the property sheet style looks like the best choice for a Control Panel applet. Nevertheless, you always have another option to run an arbitrary standalone application in response to CPL_DBLCLK. The next sample shows you both opportunities: A 'host' application that calls applets is ctlpnl.exe. It enables you to easily call other applets programmatically. A syntax for a call is as follows: All it does is activate the default applet (cplmain.cpl), passing the desired predefined index of the system applet and, optionally, the property page index if the applet has several pages. Unfortunately, indexes of system applets vary between different versions of Windows CE, but this can be overcome easily. Below is a sample table that contains applets available on my Pocket PC 2003 SE. Notice that all indexing starts from 0: You can continue to explore cplmain.cpl even more. Becayse it is just a regilar DLL with a known exported function, you can easily load it and call CPlApplet directly:
Continuing on, you can call CPlApplet with CPL_NEWINQUIRE / CPL_STOP messages, getting more info about the implemented applets. I personally prefer a PropertySheet-like style for Control Panel applets because Property Sheet gives me a simple mechanism to provide a GUI title and hyperlinks to any other resources, including applets or whatever else. These features are available on the Pocket PC platform only. Actually, a window procedure should handle two messages: PSCB_GETTITLE and PSCB_GETLINKTEXT. The last code snippet show those simple handlers: Control Panel applets are still alive. They are easily doable and provide a convenient mechanism to configure your applications at the 'back-door'. You can use them even if your applications have their own GUI. I believe you'll enjoy developing applets for Control Panel. Alex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler.
An Ancient Story of Control Panel Applets
March 3, 2005
Dry Theory
LIBRARY TESTAPPLET
EXPORTS CPlApplet
LONG CALLBACK CPlApplet (HWND hwndCPL, UINT uMsg, LONG lParam1,
LONG lParam2)
{
...
switch (uMsg)
{
// First message sent. It is sent only once to allow the
// DLL to initialize its applet(s)
case CPL_INIT:
...
return TRUE;
// Second message sent. Return the count of applets supported
// by this DLL
case CPL_GETCOUNT:
// Return the number of applets we support
return (LONG) nApplets;
// Third message sent. Sent once for each applet supported by
// this DLL.
// The lParam1 contains the number that indicates which applet
// this is for, from 0 to 1 less than the count of applets.
// lParam2 is a NEWCPLINFO that should be filled with
// information about this applet before returning
case CPL_NEWINQUIRE:
{
LPNEWCPLINFO lpNewCPlInfo;
// Fill this struct in
...
}
break;
// This is sent whenever the user clicks an icon in Settings
// for one of the applets supported by this DLL. lParam1
// contains the number indicating which applet. Returns 0 if
// applet was successfully launched; non-zero otherwise
case CPL_DBLCLK:
...
break;
// Sent once per applet, before CPL_EXIT
case CPL_STOP:
break;
// Sent once before the DLL is unloaded
case CPL_EXIT:
...
break;
default:
break;
}
return 0;
}
Turning to Practice
Initializing an applet
typedef struct tagNEWCPLINFO {
DWORD dwSize;
DWORD dwFlags;
DWORD dwHelpContext;
LONG lData;
HICON hIcon;
TCHAR szName[32];
TCHAR szInfo[64];
TCHAR szHelpFile[128];
} NEWCPLINFO;
Name
Description dwSize
The length of struct in bytes; in other words, sizeof(NEWCPLINFO) dwFlags
Ignored in Windows CE dwHelpContext
Ignored in Windows CE lData
Application-defined data sent to your applet by Control Panel when it launches and stops it. The application should return an icon ID in this member hIcon
Icon handle to be displayed in Control Panel szName
Null-terminated string that contains the name displayed below the icon in Control Panel szInfo
Null-terminated string that contains the applet's description. The description is intended to be displayed when the icon for the applet is selected szHelpFile
Ignored in Windows CE
int iApplet = (int)lParam1;
LPNEWCPLINFO lpNewCPlInfo = (LPNEWCPLINFO)lParam2;
lpNewCPlInfo->dwSize = (DWORD)sizeof(NEWCPLINFO);
lpNewCPlInfo->dwFlags = 0;
lpNewCPlInfo->dwHelpContext = 0;
lpNewCPlInfo->lData = g_nIconID;
lpNewCPlInfo->hIcon = LoadIcon(g_hInstance,
(LPCTSTR)MAKEINTRESOURCE(g_nIconID));
lpNewCPlInfo->szHelpFile[0] = '\0';
LoadString(g_hInstance,IDS_NAME,lpNewCPlInfo->szName,32);
LoadString(g_hInstance,IDS_DESC,lpNewCPlInfo->szInfo,64);
Responding to the CPL_DBLCLK message
// Create appropriate Property Sheet
case CPL_DBLCLK:
{
int iApplet = (UINT)lParam1;
if (!CreatePropertySheet(hwndCPL,iApplet))
return 1;
}
break;
...
// Launch arbitrary application
case CPL_DBLCLK:
{
int iApplet = (UINT)lParam1;
PROCESS_INFORMATION pi;
if ( CreateProcess(L"SomeApp.exe",L"CmdLine",0,0,0,0,0,0,0,&pi) )
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
return 1;
}
break;
Hosting applets with Control Panel
ctlpnl.exe cplmain.cpl,<AppletIndex>[,<PropPageIndex>]
Name
Index
Page Contrast
0
n/a Password
1
Owner Information
2
Power
3
Memory
4
About
5
Backlight
6
Screen
7
Input
8
Sounds & Notifications
9
Remove Programs
10
n/a Menus
11
Buttons
12
Today
13
Not In Use
14
n/a Beam
15
n/a Clock & Alarms
16
Network Cards
17
n/a Regional Settings
18
Connections
19
Not in Use
20
n/a Not in Use
21
n/a Certificates
22
Bluetooth
23
typedef LONG (CALLBACK *LPCPlAppletType)(HWND hwndCPL, UINT uMsg,
LONG lParam1, LONG lParam2);
#include <cpl.h>
void CCPTestDlg::OnButtonHack()
{
HINSTANCE hInst = ::LoadLibrary(L"cplmain.cpl");
if ( hInst )
{
LPCPlAppletType pfnCPlApplet =
(LPCPlAppletType)GetProcAddress(hInst,L"CPlApplet");
if ( pfnCPlApplet )
{
LONG lRes = pfnCPlApplet(NULL,CPL_GETCOUNT,0,0);
CString sInfo;
sInfo.Format(L"AppletsCount = %lu",lRes);
AfxMessageBox(sInfo);
}
::FreeLibrary(hInst);
}
}
case PSCB_GETTITLE:
wcscpy((TCHAR*)lParam,TEXT("Test Applet"));
break;
case PSCB_GETLINKTEXT:
wcscpy((TCHAR*)lParam,TEXT("Go to <file:ctlpnl.exe cplmain.cpl,
11,1{menu}> applet.\r\n"));
wcscat((TCHAR*)lParam,TEXT("Go to <file:ctlpnl.exe cplmain.cpl,
4,1{memory}> applet.\r\n"));
wcscat((TCHAR*)lParam,TEXT("Go to <file:calc.exe{calculator}>.
\r\n"));
break;
Conclusions
About the Author