Click here to Skip to main content
Click here to Skip to main content

Adding Custom Pages to Control Panel Applets

By , 3 Apr 2001
 

Introduction

After writing my tutorial on property sheet shell extensions, I had some folks ask me how to go about customizing pages in Control Panel applets. The procedure is very similar to writing a "normal" property sheet extension, so I have written this article that summarizes how to do it.

I classified this article as intermediate level, as you should already be familiar with property sheet extensions, the COM interfaces involved and their methods, and how to code property pages in straight SDK calls. If you need a refresher, see my tutorial.

Also keep in mind that customizing Control Panel applets shouldn't be done lightly. If you're writing custom driver software (e.g., something like Microsoft's IntelliPoint or IntelliType), then it's perfectly reasonable. But if you just have a little wallpaper-switching program, you'd be better served to write your own configuration app instead of adding to the already-crowded Display Control Panel.

The sample project for this article is a simple extension that adds two custom pages to the Display applet.

The Extension Interfaces

As with a normal property sheet extension, a Control Panel extension implements two interfaces: IShellExtInit and IShellPropSheetExt. The sequence of method calls is a bit different from normal property sheet extensions. The sequence goes like this:

  • IShellExtInit::Initialize() is called first, but since there are no files to be selected, the pidlFolder and lpdobj parameters are both NULL. You can perform any one-time initialization in Initialize().
  • IShellPropSheetExt::ReplacePage() is called once for each page that is replaceable, if the applet allows pages to be replaced. You can replace one page in each ReplacePage() call; I'll explain this below.
  • IShellPropSheetExt::AddPages() is called once. You can add as many pages as you like in AddPages().

The big difference is the ReplacePage() method, which went unused in normal property sheet extensions. Certain Control Panel applets let extensions replace pages in the property sheet. You can find the exact pages and applets in the cplext.h file, but I've summarized them here:

  • Display applet: You can replace the Background page.
  • Keyboard applet: You can replace the Speed page.
  • Mouse applet: You can replace the Buttons and Motion pages.

The initialization interface

As I mentioned above, IShellExtInit::Initialize() is called, but since there is no selection in a Control Panel applet, the parameters are meaningless. If you have any one-time initialization to do, Initialize() is a good place to do it.

The property sheet interface

The Display applet allows extensions to replace one page (Background), so the IShellPropSheetExt::ReplacePage() method is called once. An extension can just return S_OK if it doesn't want to replace the page. If the extension does want to replace the page, it creates a new property page and the applet displays it in place of the built-in Background page.

The prototype for ReplacePage() is:

HRESULT IShellPropSheetExt::ReplacePage (
    UINT uPageID,
    LPFNADDPROPSHEETPAGE lpfnReplaceWith,
    LPARAM lParam );

The parameters are:

uPageID
A constant from cplext.h that indicates which page the applet is querying for. For example, the Display applet calls ReplacePage() with uPageID set to CPLPAGE_DISPLAY_BACKGROUND to indicate the extension can replace the Background page. In applets that allow more than one page to be replaced (such as the Mouse applet), ReplacePage() is called once for each page.
lpfnReplaceWith, lParam
lpfnReplaceWith is a function pointer that the extension calls to actually replace the page. lParam is a value that's meaningful to the shell and gets passed to the lpfnReplaceWith function.

The process for replacing a page is pretty much the same as adding a page. Our extension fills in a PROPSHEETPAGE struct, creates a page, and calls the lpfnReplaceWith function to replace the Background page. The sample page has no controls on it; the purpose is just to show how to get pages in the Display applet. I'm assuming that you're able to code a dialog proc for the page; if you need help with this, check out other articles on property sheets at CodeProject.

Here's the code for our ReplacePage() method. We first verify that uPageID is a value we expect.

#include <cplext.h>

STDMETHODIMP CDisplayCplExt::ReplacePage ( UINT uPageID, 
                                           LPFNADDPROPSHEETPAGE lpfnReplaceWith,
                                           LPARAM lParam )
{
    <FONT COLOR="#009900">// Check that we're being called with the page ID we expect.</FONT>
    if ( CPLPAGE_DISPLAY_BACKGROUND != uPageID )
        return S_OK;

We then fill in a PROPSHEETPAGE struct. The code here references the dialog proc and a callback function, which I'll get to in a bit.

PROPSHEETPAGE  psp;
HPROPSHEETPAGE hPage;

    <FONT COLOR="#009900">// Set up the PROPSHEETPAGE struct.</FONT>
    ZeroMemory ( &psp, sizeof(PROPSHEETPAGE) );

    psp.dwSize      = sizeof(PROPSHEETPAGE);
    psp.dwFlags     = PSP_USEREFPARENT | PSP_DEFAULT | PSP_USECALLBACK;
    psp.hInstance   = _Module.GetResourceInstance();
    psp.pszTemplate = MAKEINTRESOURCE(IDD_REPLACEPAGE);
    psp.pfnDlgProc  = ReplacementPageDlgProc;
    psp.pfnCallback = ReplacementPageCallbackProc;
    psp.pcRefParent = (UINT*) &_Module.m_nLockCnt;

We then create a page and pass it to the lpfnReplaceWith function.

    <FONT COLOR="#009900">// Create the page & get a handle.</FONT>
    hPage = CreatePropertySheetPage ( &psp );

    if ( NULL != hPage )
        {
        <FONT COLOR="#009900">// Call the shell's callback function, so it adds the page to
        // the property sheet.</FONT>
        if ( !lpfnReplaceWith ( hPage, lParam ))
            {
            DestroyPropertySheetPage ( hPage );
            }
        }

    return S_OK;
}

There are two additional functions, ReplacementPageDlgProc and ReplacementPageCallbackProc. You can check out the code in the sample project; they are just skeletons since the page has no controls.

Note that shell version 4.71 and later includes a Display extension that has a Background page replacement, so if you use the above code without disabling the shell's own extension, you'll still see a Background tab, as shown here:

 [Our page with the shell's page - 13K]

An extension can also add any number of pages to the property sheet from its AddPages() method. This is done just as in normal property sheet extensions, and the code is almost identical to the ReplacePage() code above. Check out the sample project if you're dying to see the code.

The sample extension adds one page to the Display applet in AddPages(), and here are the final results:

 [Our replacement page - 8K]

 [Our additional display page - 7K]

Registering the Extension

The Control Panel applets that can be customized have their own area of the registry in HKEY_LOCAL_MACHINE. Unfortunately, the registry key used to extend the main Display property sheet is different on 9x and 2000, so we can't register our extension with an RGS script. On Windows 9x, it's HKLM\Software\Microsoft\Windows\CurrentVersion\Controls Folder\Display\shellex\PropertySheetHandlers, and on Windows 2000 it's HKLM\Software\Microsoft\Windows\CurrentVersion\Controls Folder\Desk\shellex\PropertySheetHandlers. We create a new key under PropertySheetHandlers for our extension in DllRegisterServer(), and remove the key in DllUnregisterServer().

I don't have an NT 4 system handy to check which registry key it uses, so for the time being the registration code treats NT 4 the same as Win 2000. I'd appreciate it if a kind reader could check on NT 4 for me, and let me know if the code is using the wrong key.

Debugging Control Panel Extensions

It's easy to debug a Control Panel extension in the Visual C debugger, once you know what debug settings to use. Microsoft Knowledge Base articles Q166168 and Q135068 have a good description of how to do it, but I'll give a quick summary here.

In your project settings, go to the Debug tab and set the executable to "rundll32.exe". The program arguments should be "shell32.dll,Control_RunDLL" followed by the full path to the CPL file that contains the applet. (Note that the "Control_RunDLL" part is case-sensitive.) It's not always obvious which CPL file to use, so here are some common ones:

Applet

Arguments (change "C:\windows\system" if you have Windows installed in some other directory)

Display

shell32.dll,Control_RunDLL C:\windows\system\desk.cpl

Mouse

shell32.dll,Control_RunDLL C:\windows\system\main.cpl

Keyboard

shell32.dll,Control_RunDLL C:\windows\system\main.cpl,@1

The "@1" part indicates which applet in main.cpl to run. (CPL files can contain more than one applet; adding @n runs applet number n, where n is a 0-based count.)

Here's a screen shot that demonstrates the settings, in case things still are a bit unclear:

 [Debug settings - 7K]

When you start debugging, rundll32 will call the Control_RunDLL function in the shell, which in turn will launch the Control Panel applet. To end debugging, just close the applet's window.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Michael Dunn
Software Developer (Senior) VMware
United States United States
Member
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.
 
Mike was a VC MVP from 2005 to 2009.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralLinker errors coming while building in XP 64 bitmemberSandilya Bhagi18 May '09 - 2:17 
Deleting intermediate files and output files for project 'DisplayPage - Win32 Release_AMD64'.
--------------------Configuration: DisplayPage - Win32 Release_AMD64--------------------
Creating Type Library...
Microsoft (R) 32b/64b MIDL Compiler Version 6.00.0366
Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.
Processing C:\Documents and Settings\Swathi\Desktop\64bit_ DisplayPage\DisplayPage.idl
DisplayPage.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\oaidl.idl
oaidl.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\objidl.idl
objidl.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\unknwn.idl
unknwn.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\wtypes.idl
wtypes.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\basetsd.h
basetsd.h
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\guiddef.h
guiddef.h
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\ocidl.idl
ocidl.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\oleidl.idl
oleidl.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\servprov.idl
servprov.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\urlmon.idl
urlmon.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\msxml.idl
msxml.idl
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\oaidl.acf
oaidl.acf
Processing C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\ocidl.acf
ocidl.acf
Compiling resources...
Compiling...
StdAfx.cpp
C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\atl\atlimpl.cpp(151) : warning C4068: unknown pragma
C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\atl\atlimpl.cpp(152) : warning C4068: unknown pragma
C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include\atl\atlimpl.cpp(175) : warning C4068: unknown pragma
Compiling...
DisplayCplExt.cpp
DisplayPage.cpp
Generating Code...
Linking...
Creating library Release_AMD64/DisplayPage.lib and object Release_AMD64/DisplayPage.exp
DisplayPage.obj : error LNK2019: unresolved external symbol __security_cookie referenced in function "public: long __cdecl ATL::CComModule::Init(struct ATL::_ATL_OBJMAP_ENTRY *,struct HINSTANCE__ *,struct _GUID const *)" (?Init@CComModule@ATL@@QEAAJ
PEAU_ATL_OBJMAP_ENTRY@2@PEAUHINSTANCE__@@PEBU_GUID@@@Z)
DisplayPage.obj : error LNK2019: unresolved external symbol __security_check_cookie referenced in function "public: long __cdecl ATL::CComModule::Init(struct ATL::_ATL_OBJMAP_ENTRY *,struct HINSTANCE__ *,struct _GUID const *)" (?Init@CComModule@ATL@
@QEAAJPEAU_ATL_OBJMAP_ENTRY@2@PEAUHINSTANCE__@@PEBU_GUID@@@Z)
Release_AMD64/DisplayPage.dll : fatal error LNK1120: 2 unresolved externals
Error executing link.exe.
 
DisplayPage.dll - 3 error(s), 3 warning(s)
 
Neo

GeneralMMC Property Sheetmemberalex@alexhitchins.com12 Aug '08 - 5:12 
Hi,
 
I have read the comments below, I was just wondering if you now have any information on how to add a custom property page to an MMC snap in dialog.
 
I'd like to add one in the IIS site properties page however can't see how this is achieved. I have got your example to work however as mentioned MMC doesn't seem to like the 'standard' shell interface.
 
Any help appreciated.
GeneralLnk file pathmemberpther14 Mar '08 - 2:41 
Hi,
 
I could not get the link file path using IContext. I hope if I could read the property of that file I can get it.
 
Because In this artical you also set the link file path in a static control. I just want to get using InvokeCommand() or using any technique but with right click on my menu item not property item of file.
GeneralThis code is not working in 64 bit XP [modified]memberNleshForU31 Aug '06 - 3:03 
Hi This code is not workin in WinXP 64 bit. Does any one has solution ???
 

-- modified at 2:18 Friday 1st September, 2006
GeneralMulti-language support for this classmemberchueh819 Apr '06 - 21:58 
Any good way to support multi-language for this class..
so that the text in the control panel page change following the user language?
Thanks.
General64-bit issuememberccm21624 Jan '06 - 16:24 
Great article...
 
Is it possible to get work on 64-bit environment?
GeneralRe: 64-bit issuesitebuilderMichael Dunn24 Jan '06 - 21:06 
I know little about x64, although I would expect the registry keys to be the same for backcompat reasons.
 
--Mike--
Visual C++ MVP Cool | :cool:
LINKS~! Ericahist | NEW!! PimpFish | CP SearchBar v3.0 | C++ Forum FAQ
Laugh it up, fuzzball.
Questionproperty sheet for mmc snap-in?membercycoder20 Sep '04 - 6:39 
These tutorials are great. Do you have the magic 10 lines of code that makes this work for adding a property sheet to the mmc snap-in lursmgr.msc? I'd like to have a custom tab on the user account properties page.
AnswerRe: property sheet for mmc snap-in?sitebuilderMichael Dunn22 Sep '04 - 12:40 
I've never written stuff for MMC, but I know MMC has its own snap-in system that's different from shell extensions. In VC 6, the ATL Object Wizard can make a skeleton snap-in for you, so that's a good place to start.
 
--Mike--
Personal stuff:: Ericahist | Homepage
Shareware stuff:: 1ClickPicGrabber | RightClick-Encrypt
CP stuff:: CP SearchBar v2.0.2 | C++ Forum FAQ

----
Laugh it up, fuzzball.
GeneralReplacePage for Drive Propertiessussswati chawdhary1 Sep '04 - 22:49 
Is it possible to disable/replace/delete a property page, provided by the shell for a drive letter? e.g. the Tools page for C:
GeneralHelp : Show the current time on new pagememberdavid8612212 Feb '04 - 23:31 
I am new for VC++. I have a question.
 
Now, I can add a new page to extend the display property page.Frown | :(
 
Who can teach me how to show the current time on new page,
and the time can update continually ?
 

Thanks
 
David
GeneralMarry that girl!memberCaddy16 Jun '03 - 16:49 
Michael, you better marry that girl, because if you break up and remove all your articles, I'll have to kill you Wink | ;)
 
Fantastic stuff, your shell programming guides are brilliant.
 
LostGolfBall
Generalgraphics in Property SheetmemberAnonymous14 Jun '02 - 5:15 
Hello, everybody,
 
I need to add a ShellEx property page.
I need a listbox on it, and an image as a background of the listbox.
 
I can add the prperty page - no problems - I made it.
I made a recource with listbox (LBS_OWNERDRAWFIXED in styles)
I filled the listbox with strings - no problem.
BUT
I can't put a image as background of the strings in the listbox.
In fact i can't draw an image in the property page at all.
 
I've tried to get DC of the listbox , as I have its handle,
but no - no image gets out.
 
Please help.
Thanks in advance.
 
ivanpeshev
Questionwhy I cant addpage on WIN 95 system?memberzhuwz_bill25 Mar '02 - 23:50 
hi,
I want to addpage about mouse on control panel. After I setup it on Win95 system,I meet a problem that is the mouse propsheet is the WIN95 system,not mine.And I trace the program to find WIN 95 is lack of regsvr32.exe,so the DLL cant be self-registered.Can you give me some advice?
 
My mail:zhuwz@cellink.com.cn
 
Thanks!!
Bill

AnswerRe: why I cant addpage on WIN 95 system?sitebuilderMichael Dunn2 Jun '02 - 18:33 
Copy regsvr32 to the machine. Smile | :) regsvr32.exe is redistributable, if that's a concern.
 
--Mike--
Just released - RightClick-Encrypt - Adds fast & easy file encryption to Explorer
Like the Google toolbar? Then check out UltraBar, with more features & customizable search engines!
My really out-of-date homepage
Sonork-100.10414 AcidHelm

Questionwhy Can't use COMBOBOXEX in this?memberwjy886 Dec '01 - 18:49 
I add a COMBOBOXEX to IDD_DIALOG1 but when I click this
page it should unload from the display cpl. Confused | :confused: Confused | :confused: Confused | :confused:
GeneralRemoving the pagememberBinesh7 Nov '01 - 1:24 
This mail had reference to your article " Adding Custom Pages to
Control Panel Applets". I have used the sample code given along with
this article to create a new page in the Control Panel for Display
settings. This page gets loaded when I load my display driver . This is
done by adding the page under
 
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Controls
Folder\Device\shellex\PropertySheetHandlers
using the inf file.
 
I want to remove this page when my driver is unistalled.
Pls suggest me a way to do this.
I have a doubt whether the DllUnRegisterServer is called when I
uninstall the driver.
 

GeneralInternet Options ExtensionmemberShaun10 Jul '01 - 11:00 
I have created a system control panel extension for the Internet Options. Sure enough, when I use the control panel to open up 'Internet Options', my extension is loaded as it should. However, when I open Internet Explorer, choose tools, and then select "Internet Options...', my extension is never loaded. Is this by design, or have I not done something correctly?
 
Of course, thanks for the great article.

GeneralRe: Internet Options ExtensionmemberMichael Dunn10 Jul '01 - 19:26 
Really? Where did you get the values and reg key names to use? cplext.h contains no mention of being able to add pages to the Internet applet.
 
--Mike--
http://home.inreach.com/mdunn/
Push the button, Frank.
GeneralRe: Internet Options ExtensionmemberShaun10 Jul '01 - 20:16 
I found the registry key names at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/shellcc/shell/Shell_Adv/ConPanel.asp
 
At the very end of the page it lists the registry keys to use, including "Internet" for the Internet Options control panel.

GeneralRe: Internet Options ExtensionmemberSerge Baltic3 Jan '05 - 7:42 
An outdated link. The proper path in the MSDN Contents is:
 
Win32 and COM Development -> User Interface -> Windows User Experience -> Windows Shell -> Shell Programmer's Guide -> Advanced Shell Techniques -> Control Panel Items
 
And there it is … Will try out.
 
* Origin: Silence Must Be Heard (2:5030/744.235)
GeneralSomeone asked me about doing this in DelphimemberMichael Dunn19 Nov '00 - 19:00 
Hi readers, someone mailed me about using Delphi, but the forum script glitched and didn't include your email address. So here's my response: Smile | :)

That brings me to the second issue: do you have any idea how i can implement these property pages in delphi? I searched the entire net (well, as a matter of speaking) but I didn't find any docs on shell extensions in delphi.
 
I've never used Delphi myself, but I did a quick search at Google and came up with these URLs:
 
http://www.cesis.lv/learn/delphi/ch15.htm
http://www.undu.com/Articles/000117b.html
 
You can search Google for "writing shell extension delphi" for some more pages.
 
--Mike--
http://home.inreach.com/mdunn/
"That probably would've sounded more commanding if I wasn't wearing my yummy sushi pajamas."
-- Buffy

GeneralRe: Someone asked me about doing this in DelphisussBoomerang5 Aug '02 - 3:43 
I'm currently trying to do it,
The most helpful example I've found is here:
http://ds.alt.ru/dtips/DEL82.html
 
but it still doesn't explain how to create a Delphi form inside the property sheet.
The solution i'm currently exploring is having the WindowProc create the Delphi form with CreateParentedControl in response for the WM_INITDIALOG message.
GeneralRegistering on NT4sussStuart Carter3 Nov '00 - 0:36 
Hi Michael,
 
Thanks for another top article!
 
Just tried it on NT4 and found the regkeys are indeed different from W2K, it uses the same key as Win95...
 
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Controls Folder\Display
 
Cheers,
Stu
GeneralRe: Registering on NT4memberMichael Dunn3 Nov '00 - 15:46 
Thanks, Stuart! I'll update the article & code shortly to reflect how NT 4 works.
 
--Mike--
http://home.inreach.com/mdunn/

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 4 Apr 2001
Article Copyright 2000 by Michael Dunn
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid