viksoe.dk

Control Panel applet

Control Panel applet


This article was submitted .


This is a class for installing a Win32 Control Panel Applet under Windows.

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(); }

  1. This defines the applet C++ class which derives from a base class to give it some basic functionality.
  2. 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.
  3. 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 Library

Installation 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 Vista

Download Files

DownloadSource Code (69 Kb)

To the top