Originally the Windows Template Library (WTL) was touted as a library for
creating small and efficient tools in C++. You would think, "Oh,
a library for creating Control Panels Applets then. They are small.".
I actually use WTL for nearly all my Windows projects;
some are quite large with more than 200,000 lines of code.
WTL just seems more straight forward compared to using MFC or
even other frameworks with gnarly C++ syntax and WTL is certainly
not a library designed just for small tools.
But eventually the day came when I needed to write a Control Panel
Applet myself. And this is the helper class I came up with.
The Windows Control Panel was one of the areas in Windows Vista that
received some interesting improvements. The Control Panel view has
moved from a strictly pick-an-icon view, to a task-oriented view where you
locate a task (textual link) that matches your desired action.
For the untrained Windows user, finding exactly the right task to click
on can be just as difficult, but fortunately the user can make use of the
Search wordwheel to try to locate a task. Each task can be associated with a
number of keywords, so that the Control Panel can list tasks that hopefully
matches the user's wordwheel ramblings.
The Control Panel API dates back to the early Windows. Applets are basically
code compiled in a DLL which exports a CPlApplet
function.
The DLLs are usually given the .cpl
file extension and the
Shell communicates with it through the exported function, sending it
commands to initialize and display itself.
On legacy Windows platforms you have to put the DLL in the \System32
folder but eventually in Windows 2000 the location was added to the Windows Registry
and you should now install the file (.dll
is allowed) in your
program folder.
With Windows XP you also needed to register a category so the applets could
be grouped in the listview. And finally in Windows Vista we can now
associate various tasks with the applet, so the user picks from what
he wants to do, rather than trying to figure out what the tool that does it
for him looks like.
The information needed to create a Control Panel Applet is.
Name | Operation | |
Name | The display name of the applet. | |
Description | Long description of Applet. Usually used for ToolTips. | |
Task | Textual description of a task the applet performs. | |
Keywords | A list of keywords associated with a task. | |
Canonical Name | Name used to launch the Control Panel applet. Form is "Vendor.ItemName". |
Using the code
This is the minimal code you need to write to create an applet DLL.#include "atlcplapplet.h"
[1]
class CMyApplet : public CCPlAppletBase<CMyApplet>
{
public:
BOOL ShowApplet(HWND hWnd, LONG_PTR lData, LPCTSTR pstrCommand)
{
AtlMessageBox(hWnd, _T("My Applet"), pstrCommand);
return TRUE;
}
};
[2]
BEGIN_CPLAPPLET_MAP()
CPLAPPLET_ENTRY(CMyApplet, "Test.MyApplet", MyApplet)
END_CPLAPPLET_MAP()
CAppModule _Module; // ATL/WTL Module
CCPlAppletModule _Applets;
[3]
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID)
{
if( dwReason == DLL_PROCESS_ATTACH ) _Module.Init(NULL, hModule);
if( dwReason == DLL_PROCESS_DETACH ) _Module.Term();
return TRUE;
}
extern "C" LONG APIENTRY CPlApplet(HWND hwndCPl, UINT msg,
LPARAM lParam1, LPARAM lParam2)
{
return _Applets.CPlApplet(hwndCPl, msg, lParam1, lParam2);
}
STDAPI DllRegisterServer()
{
return _Applets.RegisterApplets();
}
STDAPI DllUnregisterServer()
{
return _Applets.UnregisterApplets();
}
- This defines the applet C++ class which derives from a base class to give it some basic functionality.
- The macro map defines the applet so it can be registered during the installation. This definition also controls how the applet manages its display name and chooses its icon at runtime.
- The remaining code defines the DLL export functions and links the export functions with the library code.
The macro takes the form:
CPLAPPLET_ENTRY(<C++ class>, "<Canonical Name>", <Internal name>)
The Canonical Name can be used in Windows Vista to launch your applet.
The C++ class you define must derive from the
CCPlAppletBase
class. The library will manage the communication with the Shell to initialize
the Control Panel properly, and eventually ends up calling the C++ class'
ShowApplet
function to allow you to display the Property Page
dialog or other user interface.
You'll need to define a couple of string and icon resources in the resource file.
IDS_MyApplet "My Test Applet"
IDS_Description_MyApplet "A Description of My Applet"
IDS_Keywords_MyApplet "Test;Keyword;Stuff"
IDS_Task_MyApplet "Run Applet"
IDI_MyApplet ICON DISCARDABLE "res/MyApplet.ico"
Make sure to include as many different resolutions as possible in the
icon resource, so it looks good on Windows Vista. Windows Vista will
allow the user to switch to Humongous Large Icon mode that use
256x256 icons in hi-res colours.
The resource names are related to the internal name used when you defined the applet in the code with
CPLAPPLET_ENTRY
.
There are a couple of alternative ways to register your applet
through the library which will allow you to customize the
resources and the way they are loaded, but this is the easy way.
The sample above takes advantage of the DllRegisterServer
mechanism for installing a COM DLL. Basically it allows you to install
the applet using the regsvr32.exe
utility. Installing the applet
registers it in the Windows Registry and is only needed once.
To keep the Visual Studio compiler happy with the new exported DLL
functions, you'll need to add a Module Defintion (.def
) file to
the C++ project with code that looks something like this.
LIBRARY AppletTool
EXPORTS
CPlApplet @1 PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
The applet is registered under the "System and Maintenance" category. To change the category you can add a special macro to the applet class.
class CMyApplet : public CCPlAppletBase<CMyApplet>
{
public:
DECLARE_APPLET_CATEGORY(0x01)
...
The following categories are available. Names varies depending on Windows platform.
ID | Name | |
0 | Additional Options. | |
1 | Appearance and Personalization | |
2 | Hardware and Sound | |
3 | Network and Internet | |
5 | System and Maintenance | |
6 | Clock, Language, and Region | |
7 | Ease of Access | |
8 | Programs | |
9 | User Accounts | |
10 | Security |
The CPLAPPLET_ENTRY
defined applet will by default also
register itself with just one associated task.
It generates an .xml
file which it writes to the location
of the .cpl
file. This XML file contains a detailed description
of which tasks the applet exposes.
You may include your own registry file if you wish to create an applet
that defines multiple tasks. You'll need to write your own XML
task file (as described on MSDN under Creating Searchable Task Links for a Control Panel Item)
and use another special macro in your C++ class.
class CMyApplet : public CCPlAppletBase<CMyApplet>
{
public:
DECLARE_APPLET_REGISTRYID(IDR_MYAPPLET)
...
Then import the XML file into the project under the "XML"
resource type.
In the XML task file you describe what command to execute when a given task
is picked by the user.
A neat little trick is to use the new control.exe
tool to launch
your Control Panel applet. The Control tool takes the form:
control.exe /name <canonical name> /page <param>
Given the correct canonical name of your applet, the Control tool will launch it.
Use the Page argument to have a task launch your applet with a custom argument.
You can parse this argument in the ShowApplet
function of your C++
class. Most of the time you'll want to pass the Property page number to
give focus to when the applet is initially shown.
And finally - to make sure that your Property Page user interface
is displayed with the correct theming (Windows XP and Vista visual
styles) you should add a manifest.
Then after adding it, change the manifest resource ID in the resource file
from CREATEPROCESS_MANIFEST_RESOURCE_ID (1)
to ISOLATIONAWARE_MANIFEST_RESOURCE_ID (2)
, like this:
ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST
"res\\AppletTool.dll.manifest"
and add a define to the top of your stdafx.h
header.
#define ISOLATION_AWARE_ENABLED
Don't ask. It's just one of those things you're required to do when
you have a DLL which may have needs (theming, in this case) that
can overrule what needs the hosting executable file has.
If your development environment can automatically generate a manifest
file during the link stage, just make sure that the project's manifest
settings are set to Isolation Aware.
It also appears that the manifest file must contain a
trustInfo
element in order to prevent an annoying
Application Compatibility popup. You'll need it anyway
if you're aspiring to the Designed for Windows Vista logo,
but be aware of the dreaded
Blue Screen of Death.
Have a look in the sample code available below to see an
example of a working manifest file.
More new things
Finally there is a couple of other things I haven't mentioned.
Windows Vista finally breaks free of the DLL limitation and allows
you to embed the Control Panel functionality in your .exe
file. This is all done with the magic of the task XML file.
The library presented here was aimed with backward compatibility in mind, so
it still requires you to create a separate DLL file where the applet lives.
Also more widely used in Windows Vista are the Shell Folders. Several of the Control Panel applets defined by Microsoft do not show the regular Property Page interface but rather navigates into the Explorer window and displays its information inlined in Explorer. This can be seen with the Fonts folder, and the Network and Sharing folder. These applets are really Shell Extensions that are rooted below the Control Panel virtual folder. Hopefully I'll be able to bring you some sample code for this kind of applet later.
Source Code Dependencies
Microsoft WTL 8.0 LibraryInstallation Guide
- Register the DLL using the REGSVR32 utility.
- To make it work on Windows 95, you'll need to rename the .DLL to .CPL and copy and register it from the \System folder.
See Also
A Shell Folder Control Panel for Windows VistaDownload Files
![]() | Source Code (69 Kb) |