November 27, 2014
Hot Topics:

An Ancient Story of Control Panel Applets

  • March 3, 2005
  • By Alex Gusev
  • Send Email »
  • More Articles »

Responding to the CPL_DBLCLK message

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:

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

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:

ctlpnl.exe cplmain.cpl,<AppletIndex>[,<PropPageIndex>]

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:

Name Index Page
Contrast 0 n/a
Password 1
  • 0 - Password page
  • 1 - Hint page
Owner Information 2
  • 0 - Identification page
  • 1 - Notes page
  • 2 - Options page
Power 3
  • 0 - Main or Battery page
  • 1 - This can be a Standby or Wireless page
  • 2 - Advanced page
Memory 4
  • 0 - Main page
  • 1 - Storage Card page (if it exists)
  • 2 - Running Programs page
About 5
  • 0 - Version page
  • 1 - Device ID page
  • 2 - Copyrights page
Backlight 6
  • 0 - Battery Power page
  • 1 - External Power page
  • 2 - Brightness page (optional)
Screen 7
  • 0 - General page
  • 1 - Clear Type page
  • 2 - Text size page
Input 8
  • 0 - Input Method page
  • 1 - Word Completion page
  • 2 - Options page
Sounds & Notifications 9
  • 0 - Sounds page
  • 1 - Notifications page
Remove Programs 10 n/a
Menus 11
  • 0 - Start Menu page
  • 1 - New Menu page
Buttons 12
  • 0 - Program Buttons page
  • 1 - Up/Down Control page
Today 13
  • 0 - Appearance page
  • 1 - Items page
Not In Use 14 n/a
Beam 15 n/a
Clock & Alarms 16
  • 0 - Time page
  • 1 - Alarms page
Network Cards 17 n/a
Regional Settings 18
  • 0 - Region page
  • 1 - Number page
  • 2 - Currency page
  • 3 - Time page
  • 4 - Date page
Connections 19
  • 0 - Tasks page
  • 1 - Advanced page
Not in Use 20 n/a
Not in Use 21 n/a
Certificates 22
  • 0 - Personal page
  • 1 - Root page
Bluetooth 23
  • 0 - Mode page
  • 1 - Bounded Devices page

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:

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

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:

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

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.

About the Author

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.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel