Adding a Control Panel Applet (PPC)

January 14th, 2009

The WM SDK ships with a little sample project that demonstrates how to create your own custom control panel applet.  Basically, each icon is a DLL that is loaded into the Shell32 process, which looks for an entry point called CPlApplet.  The sample applet is called MyBacklight.  Below is the CPlApplet function which could serve for many control panel applets without modification.  The framework talks to this entry point through the messages CPL_INIT, CPL_GET_COUNT, CPL_NEW_INQUIRE, CPL_DBLCLK, etc., which allow you to define the applet’s behavior and appearance.  Often these applets are just fancy front-ends on a bunch of registry settings.  Now if only I could think of something that needs configuring…

extern “C”
__declspec(dllexport)
LONG WINAPI CPlApplet(HWND hwndCPL, UINT uMsg, LONG lParam1, LONG lParam2)
{
static int        iInitCount = 0;
int                iApplet;

switch (uMsg)
{
// First message sent. It is sent only once to
// allow the dll to initialize it’s applet(s)
case CPL_INIT:
if (!iInitCount)
{
if (!InitApplet(hwndCPL))
return FALSE;
}
iInitCount++;
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)((sizeof(SystemApplets))/(sizeof(SystemApplets[0])));

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

lpNewCPlInfo                = (LPNEWCPLINFO)lParam2;
iApplet                        = (int)lParam1;
lpNewCPlInfo->dwSize        = (DWORD)sizeof(NEWCPLINFO);
lpNewCPlInfo->dwFlags        = 0;
lpNewCPlInfo->dwHelpContext    = 0;
lpNewCPlInfo->lData            = SystemApplets[iApplet].icon;
lpNewCPlInfo->hIcon            = LoadIcon(g_hInstance,(LPCTSTR)MAKEINTRESOURCE(SystemApplets[iApplet].icon));
lpNewCPlInfo->szHelpFile[0]    = ‘\0′;

LoadString(g_hInstance,SystemApplets[iApplet].namestring,lpNewCPlInfo->szName,32);
LoadString(g_hInstance,SystemApplets[iApplet].descstring,lpNewCPlInfo->szInfo,64);
}

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. Return 0 if applet successfully launched, non-zero otherwise
case CPL_DBLCLK:
iApplet = (UINT)lParam1;
if (!CreatePropertySheet(hwndCPL,iApplet))
return 1;
break;

// Sent once per applet, before CPL_EXIT
case CPL_STOP:
break;

// Sent once before the dll is unloaded
case CPL_EXIT:
iInitCount–;
if (!iInitCount)
TermApplet();
break;

default:
break;
}

return 0;
}

Delegates: Function Pointers in .NET

December 31st, 2008

DLLs contain functions that, when linked to by an application, can be called from the application.  However, what if you want to receive notification of something that occurs asynchronously within the DLL?  One way you can do this is to register a callback, which is basically a pointer to a function in the calling application that can be called from within the DLL.

But what happens if your calling app is managed .NET code?  One of the key design features of C# (and Java) is that it does not support pointers (much).  Well, in .NET you can create “delegates” which basically serve the same purpose as function pointers.  There is an API called GetFunctionPointerForDelegate that you can use to “cast” a delegate to a function pointer. But if you pass a delegate as a parameter into P/Invoke (which you would do to register the callback with the DLL) it handles this casting implicitly.  Now your managed and unmanaged code can call each other.

Visual C++ 2008 Redistributable Package

December 31st, 2008

If you have an C++ application or DLL built with Visual Studio 2008 that works on your development PC but not on a target PC, probably what is missing from the target PC is the “Microsoft C++ 2008 Redistributable Package (x86) “, which “installs runtime components of Visual C++ Libraries required to run applications developed with Visual C++ on a computer that does not have Visual C++ 2008 installed.”  In fact, I would suggest that if you ever build anything in VS2008 that you plan to distribute, you create a Setup project that installs this package along with your application.

Also: you can’t distribute Debug versions of your builds when deploying with the Redistributable Package.  Only Release versions will work.

What is Shell32.exe?

December 31st, 2008

What is Shell32.exe?  It’s a process that is always running on your WM device.  The same way that poutlook.exe encapsulates multiple “apps” (the tasks application, contacts application, the calendar application), shell32.exe encapsulates a lot of the graphical elements of the WM experience.  For example, the Today screen, the task bar, screen rotation, and those little pop-up notifications are all handled by shell32.exe.  On a development system, there might also be a file called shell.exe: this is the CE Shell, otherwise known as CE Target Control, and is loaded much earlier in the boot process, if present.

Decoding IOCTLs

December 31st, 2008

If you ever have to decode an IOCTL that was created with Microsoft’s CTL_CODE macro, there’s an online decoder for it here .

These things are a pain to decode manually because of all the bit-shifting involved.

What Certificates are Signing My Modules?

December 31st, 2008

You can view the digital certificates that your modules are signed with from the desktop - just locate the .exe or .dll (in the release directory) and right-click it.  Then click “Properties” and click on the “Digital Signatures” tab.  The default test cert is called “Priviledged Signing Cert” (this is not the first time I have seen Microsoft misspell this word), and the issuer is “Stinger Privileged Root” (got it right that time!).

COMPILE_DEBUG

December 31st, 2008

If you’re building a RETAIL build in CE but want to build a debug version of something, you can use the COMPILE_DEBUG environment variable.  From MSDN: “This macro definition specifies that Build.exe must pass debug macro definitions to the linker and compiler even if WINCEDEBUG is set to retail. If COMPILE_DEBUG iis set when WINCEDEBUG is set to retail, the libraries linked are the release libraries.”  This saves you from having to copy files from the debug build directories, and it’s helpful if you want to build debug versions of PUBLIC components since you don’t have to have debug versions of all the dependencies.

WM7 Release Slips

December 31st, 2008

Microsoft lately announced that WM7, which was originally slated for release Q4 ‘08 or Q1 ‘09 will now be released nine months later, around September 2009.  In the meantime, they plan to have another major release of WM6 sometime next year which will be based on AKU 2.0, but will be called Windows Mobile 6.5.  I was recently at a Microsogt WM7 “Jumpstart” event, and some of the MS employees were asking the crowd for suggestions on how certain components should behave, which seemed at the time like something they would not be asking mere months from release.  I guess that’s why.

Autocomplete Poetry

December 31st, 2008

The following poem was composed using only the available autocomplete suggestions when writing a text message on WM6.1 build 20240:

Xavier continued waiting under green water.

Ordinary lives expect ordinary results.

Former research killed young trees.

“Xavier” was the only suggestion for “x”, and I’m surprised that “killed” is among the top four words starting with “k”.

‘placement new’ and delete

December 31st, 2008

Not really specific to Windows CE, but something I thought was interesting…

There is a somewhat esoteric usage of the operator ‘new’ called ‘placement new’.  This can be used for constructing an object at a particular address in memory, with the assumption that the memory there was previously allocated.  If you’re going to be doing a lot of dynamic allocations, using ‘placement new’ can be faster.

My question was that if I allocate a (relatively) large buffer like this:

void* pBuffer = new BYTE[10];

and then use ‘placement new’ to create a (relatively) smaller structure there like this:

typedef struct {
BYTE elements[5];
} MyStruct;

MyStruct *   pStruct = new (pBuffer) MyStruct;

do I have to delete pStruct or pBuffer?  It sounds like deleting pStruct would only delete the first 5 bytes and leak the rest.   But I tried it and it looks like deleting pStruct frees all the memory.  I guess this is because since the ‘placement new’ call doesn’t actually allocate anything it just inherits the allocation of the pointer parameter it was called with.