Working with the Shell
Because the Explorer shell is derived from the Windows 95 shell, I must cover some system definitions first introduced with Windows 95. In general, while the concepts remain the same, the implementation is completely different under the covers.
The Shell Namespace
From Windows 95 on, the Windows shell has used the concept of a shell namespace. The Explorer shell and the Pocket PC shell also use the namespace concept to track the objects in the shell. Simply put, the shell namespace is the entire collection of the operating system's objects, files, directories, printers, control panel applets, and so forth. The idea is that by addressing files the same way as control panel applets, the shell makes it easy to deal with the diverse collection of objects.A folder is simply a collection of objects. A directory is a collection of files on a disk. A folder generalizes and extends the directory concept, in that a folder doesn't merely contain files, but can include other objects such as control panel objects, printers, or remote connection links. Each object in a folder is called an item. Items are identified by an item ID.
The item ID is a data structure that uniquely identifies the item in the folder. Since folders also have identifiers, an individual item can be uniquely defined by means of a list of item IDs that identify the item, its folder, and the parent folders of the folder. Think of this list of item identifiers as a completely specified pathname of a file. A system might have many files named foobar, but only one in a specific directory. This list of item IDs is appropriately called an ID list. A pointer to such a list is a pointer to an ID list, frequently abbreviated as pidl, which is generally and rather unfortunately pronounced piddle. Shell functions usually reference items in the shells by their pidls. There is, of course, a translation function that converts a pidl to a filename.
Special Folders
The Windows CE shell, like the shells for the desktop versions of Windows, has a set of folders that are treated differently from normal directories in the file system. An example of this is the recycle bin, which is simply a hidden directory to which the shell moves files and directories when the user deletes them. Another example is the Programs folder, which contains a set of shortcuts that are then displayed on the Start menu.The list of special folders changes with each shell. The Windows 95/98/Me shells and the Windows NT/2000/XP shells have a different set of special folders from those of the Windows CE shells. The Pocket PC, Smartphone, and Explorer shells each implements its own subset of special folders. Fortunately, the function to return the path of a specific special folder is the same on all these systems. That function, SHGetSpecialFolderPath, is prototyped as
BOOL SHGetSpecialFolderPath (HWND hwndOwner, LPTSTR lpszPath,
int nFolder, BOOL fCreate);
The hwndOwner parameter is the handle to a window that will be the owner of any dialog box that the function creates. The second parameter, lpszPath, points to a buffer at least MAX_PATH characters, not bytes, in length, which will receive the returned path. The nFolder parameter is set to the constant that indicates what folder you need. The fCreate parameter is a Boolean that you can set to TRUE if you want the system to create the directory if one currently doesn't exist.The nFolder parameter can be one of many constants that are common across the Windows operating systems. Not all the values are supported on all Windows CE platforms, but the following short list includes some constants that most platforms support.
CSIDL_BITBUCKETThe location of the recycle bin.
CSIDL_DESKTOPThe folder that stores the objects that appear on the desktop. Note that the use of this constant is different than it was under Windows 95.
CSIDL_FONTSThe folder that contains the system fonts.
CSIDL_DRIVESThe root of the file system.
CSIDL_PROGRAMSThe folder that contains the items shown in the Programs submenu of the Start menu.
CSIDL_PERSONALThe default folder in which to save documents.
CSIDL_FAVORITESThe folder that contains shortcuts to favorite items.
CSIDL_STARTUPThe folder that contains programs or shortcuts to programs that will be launched when the system is restarted.
CSIDL_RECENTThe folder that contains the list of recently used documents.
The SHGetSpecialFolderPath function was first supported in Windows CE 3.0. For earlier versions of Windows CE, you must use two other functions, SHGetSpecialFolderLocation and SHGetPathFromIDList. The function SHGetSpecialFolderLocation takes the constants in the preceding list and returns a pidl. Then you need to call SHGetPathFromIDList to translate the pidl to a path. The two functions are prototyped as
HRESULT SHGetSpecialFolderLocation (HWND hwndOwner, int nFolder,
LPITEMIDLIST *ppidl);
and
BOOL WINAPI SHGetPathFromIDList (LPCITEMIDLIST pidl, LPTSTR pszPath);
If you needed only to call SHGetSpecialFolderLocation and follow that by calling SHGetPathFromIDList to get the path, life would be simple. Unfortunately, the process isn't that easy. The pidl that's returned by SHGetSpecialFolderLocation points to a buffer that has been allocated by the shell. You need to call the shell back to free this buffer after you're finished with the ID list. You free this buffer using an IMalloc interface provided by the shell.The IMalloc interface contains methods that allow an application to allocate, free, and otherwise manipulate memory in the local heap of the IMalloc provider. In the case of the shell, a pointer to its IMalloc interface can be acquired with a call to SHGetMalloc. The function is prototyped as
HRESULT SHGetMalloc (LPMALLOC *ppMalloc);
Once you have a pointer to the interface, you can call the Free method to free any ID lists returned by ShGetSpecialFolderLocation.
On some early Windows CE systems, SHGetSpecialFolderLocation returns a constant, typed as a pidl, which can then be passed to SHGetPathFromIDList to get a directory name. Those systems don't implement IMalloc. To support those early machines, you can use a routine like the following, which attempts to get the IMalloc interface. However, if this call fails, the routine simply proceeds to call SHGetSpecialFolderLocation and SHGetPathFromIDList to query the directory.
INT MyGetSpecialDirectory (HWND hWnd, INT nFolderID,
LPTSTR lpDir) {
int rc;
LPITEMIDLIST pidl;
BOOL fUseIMalloc = TRUE;
LPMALLOC lpMalloc = NULL;
// Attempt to get the Shell Malloc interface.
rc = SHGetMalloc (&lpMalloc);
if (rc == E_NOTIMPL)
fUseIMalloc = FALSE;
else if (rc != NOERROR)
return rc;
rc = SHGetSpecialFolderLocation (hWnd, nFolderID, &pidl);
if (rc == NOERROR) {
// Translate the idlist to a directory name.
if (SHGetPathFromIDList (pidl, lpDir))
rc = E_FAIL;
// Free the idlist.
if (fUseIMalloc)
IMalloc_Free(lpMalloc,pidl);
}
// Free shell's IMalloc interface.
if (fUseIMalloc)
IMalloc_Release(lpMalloc);
return rc;
}
Note that on the Pocket PC, the combination of two functions—SHGetSpecialFolderLocation and SHGetPathFromIDList—supports a greater number of the CSIDL_ constants than does the single function SHGetSpecialFolderPath. For this reason, and to remain backward-compatible with older systems, I tend to use the combination of the older functions instead of the newer function.
Shortcuts
Shortcuts are small files that, when opened, launch an application or open a document in another folder. The idea behind shortcuts is that you could have an application located in one directory but you might want to be able to launch it from other directories. Since the shell uses the contents of special directories to define what is in the Start menu and on the desktop, placing a shortcut in one of those special directories allows an application to appear in the Start menu or on the desktop.While the concept of shortcuts was taken from the desktop versions of Windows, the method of creating them was not. Instead of using a COM interface, as is done on the desktop, you create a shortcut in Windows CE using the following function:
BOOL SHCreateShortcut (LPTSTR szShortcut, LPTSTR szTarget);
The first parameter specifies the name and location of the shortcut. This name should be a fully qualified filename with an extension of LNK. The second parameter is the fully qualified filename of the application you want to start or the file you want to open. The function returns TRUE if successful.Another function that will create a shortcut is
DWORD SHCreateShortcutEx (LPTSTR lpszDir, LPTSTR lpszTarget,
LPTSTR szShortcut, LPDWORD lpcbShortcut);
Like SHCreateShortcut, the first two parameters specify the name of the shortcut and the name of the target file. The third parameter is a buffer that will receive the name of the shortcut that was created. The fourth parameter first contains the number of characters that can fit in szShortcut and is filled by the function with the number of characters copied into szShortcut. The resulting name of the shortcut is a derivation of the string pointed to by lpszDir. For example, if lpszDir pointed to \temp\joe.lnk, the resulting shortcut would be \temp\shortcut to joe.lnk as long as a file with that name didn't already exist. If a file with that name did exist, the resulting name would be \temp\shortcut to joe (2).lnk. The advantage of SHCreateShortcutEx is the function's guarantee of a unique file name for the resulting shortcut file.You can determine the contents of a shortcut by calling this function:
BOOL SHGetShortcutTarget (LPTSTR szShortcut, LPTSTR szTarget,
int cbMax);
The first parameter is the filename of the shortcut. The remaining two parameters are the buffer that receives the target filename of the shortcut and the size of that buffer.
Configuring the Start Menu
Shortcuts come into their own when you're customizing the Start menu. When the Start button is clicked, the taskbar looks in its special folder and creates a menu item for each item in the folder. Subfolders contained in the special folder become submenus on the Start menu.The Start menu of the Explorer shell is limited in that you can't customize the Start menu itself. You can, however, modify the Programs submenu and the submenus it contains. To add an item to the Programs submenu of the Explorer Start menu, you place a shortcut in the folder returned after you called SHGetSpecialFolderPath with the folder constant CSIDL_PROGRAMS. For example, look at the following short code fragment, which lists the Calc program in the Programs submenu of the Start directory on a device.
INT rc;
TCHAR szDir[MAX_PATH];
rc = SHGetSpecialFolderPath (hWnd, szDir, CSIDL_PROGRAMS, FALSE);
if (rc == NOERROR) {
lstrcat (szDir, TEXT ("\\Calc.lnk"));
SHCreateShortcut (szDir, TEXT ("\\windows\\calc.exe"));
}
This fragment uses the routine SHGetSpecialFolderPath to return the folder used by the Programs submenu. Once that's found, all that is required is to append the necessary LNK extension to the name of the link and call SHCreateShortcut specifying the location of Calc.exe.The Start menu of the Pocket PC is more flexible than the Explorer shell because you can add items directly to the Start menu itself. To accomplish this, add shortcuts to the folder returned with SHGetSpecialFolderLocation and the constant CSIDL_STARTMENU.Although it is possible to download executables directly to the Start menu directories, a better idea is to create a directory under the \Programs folder to store your application and place a shortcut pointing to your application in the Start menu. This solution allows your application to keep any necessary DLLs and additional files isolated in their own directory instead of dumping them in the Start menu directory.
Recent Documents List
A feature of the Start menu since it was introduced in Windows 95 is the Documents submenu. This menu lists the last 10 documents that were opened by applications in the system. This list is a convenient place in which users can reopen recently used files. The system doesn't keep track of the last-opened documents. Instead, an application must tell Windows that it has opened a document. Windows then prunes the least recently opened document on the menu and adds the new one.
Under Windows CE, the function that an application calls to add a document to the recently used list is
void SHAddToRecentDocs (UINT uFlags, LPCVOID pv);
The first parameter can be set to one of two flags, SHARD_PATH or SHARD_PIDL. If uFlags is set to SHARD_PATH, the second parameter points to the fully qualified path of the document file. If SHARD_PIDL is specified in uFlags, the second parameter points to a pointer to an ID list. If the second parameter is 0, all items in the recently used document menu are deleted.
Launching Applications
Windows CE supports one of the standard Windows shell functions, ShellExecuteEx. Although Windows CE doesn't support much of the functionality of ShellExecuteEx, the functionality that remains is still quite useful. ShellExecuteEx is somewhat simpler to use than CreateProcess to create new processes. ShellExecuteEx also has the advantage of being able to automatically associate data files with the application that should open them. Furthermore, it opens the Explorer to a specific directory. The function prototype for ShellExecuteEx is
BOOL WINAPI ShellExecuteEx (LPSHELLEXECUTEINFO lpExecInfo);
The only parameter is a pointer to the rather complex SHELLEXECUTEINFO structure, defined as
typedef struct _SHELLEXECUTEINFO {
DWORD cbSize;
ULONG fMask;
HWND hwnd;
LPCSTR lpVerb;
LPCSTR lpFile;
LPCSTR lpParameters;
LPCSTR lpDirectory;
int nShow;
HINSTANCE hInstApp;
// Optional members
LPVOID lpIDList;
LPCSTR lpClass;
HKEY hkeyClass;
DWORD dwHotKey;
HANDLE hIcon;
HANDLE hProcess;
} SHELLEXECUTEINFO;
The first field is the traditional size field that must be set to the size of the structure. The fMask field can contain two flags: SEE_MASK_FLAG_NO_UI, which instructs the function not to display an error dialog box if the function fails, and SEE_MASK_NOCLOSEPROCESS, which will return the handle to the child process in the hProcess field. If you set the latter flag, your application is responsible for closing the returned handle. The hwnd field is the handle to a window that owns any error dialog displayed as a result of the function.The lpVerb field points to a string that tells ShellExecuteEx what to do. The documented "verbs" are open, print, explore, edit, and properties, but for the current Windows CE Explorer shell, the verb is basically ignored. The default is open. The lpFile field should point to a string that contains the name of a file—a data file, a directory, or an executable. If lpFile points to an application name, the lpParameters field can contain the address of a string containing the command line parameters for the application. If lpFile points to a document file or a directory, lpParameters should be NULL.Of all the remaining fields, only hInstApp and hProcess are used. All the others are ignored. The hInstApp field should be set to the instance handle of the application calling the function. As I mentioned earlier, if you set the SEE_MASK_NOCLOSEPROCESS flag in fMask, the function returns the handle of the child process. For example, the following code fragment opens a Pocket Word document in the root directory of a Windows CE system:
SHELLEXECUTEINFO si;
memset (&si, 0, sizeof (si));
si.cbSize = sizeof (si);
si.fMask = 0;
si.hwnd = hWnd;
si.lpFile = TEXT ("\\doc1.pwd");
si.lpVerb = TEXT ("Open");
rc = ShellExecuteEx (&si);
The shell launches the proper application by looking in the registry to associate a data file's extension with an associated application. This process is essentially identical to the method used on the desktop. The shell searches the registry for a subkey under [HKEY_CLASSES_ROOT] that matches the extension of the data file. The default value of that subkey then identifies another subkey that indicates the application to launch.
The Taskbar
The taskbar interface under Windows CE is almost identical to the taskbar interface under the desktop versions of Windows. I've already talked about how you can configure the items in the Start menu. The taskbar also supports annunciators, those tiny icons on the far right of the taskbar. The taskbar icons are programmed with methods similar to those used in Windows XP. The only limitation under the Explorer shell or the Pocket PC shell is that they don't support tooltips on the taskbar icons.Programs can add, change, and delete taskbar icons using this function:
BOOL Shell_NotifyIcon (DWORD dwMessage, PNOTIFYICONDATA pnid);
The first parameter, dwMessage, indicates the task to accomplish by calling the function. This parameter can be one of the following three values:
NIM_ADDAdds an annunciator to the taskbar
NIM_DELETEDeletes an annunciator from the taskbar
NIM_MODIFYModifies an existing annunciator on the taskbar
The other parameter points to a NOTIFYICONDATA structure, which is defined as
typedef struct _NOTIFYICONDATA {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
WCHAR szTip[64];
} NOTIFYICONDATA;
The first field, cbSize, must be filled with the size of the structure before a call is made to Shell_NotifyIcon. The hWnd field should be set to the window handle that owns the icon. This window receives messages notifying the window that the user has tapped, double-tapped, or moved her pen on the icon. The uID field identifies the icon being added, deleted, or modified. This practice allows an application to have more than one icon on the taskbar. The uFlags field should contain flags that identify which of the remaining fields in the structure contain valid data.When you're adding an icon, the uCallbackMessage field should be set to a message identifier that can be used by the taskbar when notifying the window of user actions on the icon. This value is usually based on WM_USER so that the message value won't conflict with other messages the window receives. The taskbar looks at this field only if uFlags contains the NIF_MESSAGE flag.
The hIcon field should be loaded with the handle to the 16-by-16-pixel icon to be displayed on the taskbar. You should use LoadImage to load the icon because LoadIcon doesn't return a small format icon. The taskbar looks at this field only if the NIF_ICON flag is set in uFlags. Finally, the szTip field would contain the tool-tip text for the icon on other Windows systems but is ignored by the current Windows CE shells.Managing a taskbar icon involves handling the notification messages the taskbar sends and acting appropriately. The messages are sent with the message identifier you defined in the call to Shell_NotifyIcon. The wParam parameter of the message contains the ID value of the taskbar icon that the message references. The lParam parameter contains a code indicating the reason for the message. These values are actually the message codes for various mouse events. For example, if the user taps your taskbar icon, the lParam value in the notification message will be WM_LBUTTONDOWN, followed by another message containing WM_LBUTTONUP.
The TBIcons Example Program
The TBIcons program demonstrates adding and deleting taskbar annunciator icons. Figure 16-1 shows the TBIcons window. The buttons at the bottom of the window allow you to add and delete icons from the taskbar. The list box that takes up most of the window displays the callback messages as the taskbar sends them. In the taskbar, you can see two icons that TBIcons has added. The list box contains a list of messages that have been sent by the taskbar back to the TBIcons window.

Figure 16-1: The Windows CE desktop with a TBIcons window
The source code for TBIcons is shown in Listing 16-1. The program uses a dialog box as its main window. The routines that add and delete taskbar icons are DoMainCommandAddIcon and DoMainCommandDelIcon. Both these routines simply fill in a NOTIFYICONDATA structure and call Shell_NotifyIcon. The routine that handles the notification messages is DoTaskBarNotifyMain. This routine is called when the window receives the user-defined message MYMSG_TASKBARNOTIFY, which is defined in

TBIcons.rc
//======================================================================
// Resource file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h>
#include "TBIcons.h" // Program-specific stuff
//----------------------------------------------------------------------
// Icons and bitmaps
//
ID_ICON ICON "TBIcons.ico" // Program icon
//----------------------------------------------------------------------
TBIcons DIALOG discardable 25, 5, 120, 110
STYLE WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU |
DS_CENTER | DS_MODALFRAME
CAPTION "TBIcons"
BEGIN
LISTBOX IDD_OUTPUT, 2, 2, 116, 90,
WS_TABSTOP | WS_VSCROLL | LBS_NOINTEGRALHEIGHT
PUSHBUTTON "&Add Icon", IDD_ADDICON, 2, 95, 55, 12, WS_TABSTOP
PUSHBUTTON "&Delete Icon",
IDD_DELICON, 61, 95, 55, 12, WS_TABSTOP
END
TBIcons.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Returns number of elements
#define dim(x) (sizeof(x) / sizeof(x[0]))
//----------------------------------------------------------------------
// Generic defines and data types
//
struct decodeUINT { // Structure associates
UINT Code; // messages
// with a function.
BOOL (*Fxn)(HWND, UINT, WPARAM, LPARAM);
};
struct decodeCMD { // Structure associates
UINT Code; // menu IDs with a
LRESULT (*Fxn)(HWND, WORD, HWND, WORD); // function.
};
//----------------------------------------------------------------------
// Generic defines used by application
#define ID_ICON 1
#define IDD_ADDICON 10 // Control IDs
#define IDD_DELICON 11
#define IDD_OUTPUT 12
#define MYMSG_TASKBARNOTIFY (WM_USER + 100)
//----------------------------------------------------------------------
// Function prototypes
//
void Add2List (HWND hWnd, LPTSTR lpszFormat, ...);
// Window procedures
BOOL CALLBACK MainDlgProc (HWND, UINT, WPARAM, LPARAM);
// Message handlers
BOOL DoInitDlgMain (HWND, UINT, WPARAM, LPARAM);
BOOL DoCommandMain (HWND, UINT, WPARAM, LPARAM);
BOOL DoTaskBarNotifyMain (HWND, UINT, WPARAM, LPARAM);
// Command functions
LPARAM DoMainCommandExit (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandAddIcon (HWND, WORD, HWND, WORD);
LPARAM DoMainCommandDelIcon (HWND, WORD, HWND, WORD);
TBIcons.cpp
//======================================================================
// TBIcons – Taskbar icon demonstration for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "TBIcons.h" // Program-specific stuff
//----------------------------------------------------------------------
// Global data
//
const TCHAR szAppName[] = TEXT ("TBIcons");
HINSTANCE hInst; // Program instance handle
INT nIconID = 0; // ID values for taskbar icons
// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
WM_INITDIALOG, DoInitDlgMain,
WM_COMMAND, DoCommandMain,
MYMSG_TASKBARNOTIFY, DoTaskBarNotifyMain,
};
// Command Message dispatch for MainWindowProc
const struct decodeCMD MainCommandItems[] = {
IDOK, DoMainCommandExit,
IDCANCEL, DoMainCommandExit,
IDD_ADDICON, DoMainCommandAddIcon,
IDD_DELICON, DoMainCommandDelIcon,
};
//======================================================================
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow) {
#if defined(WIN32_PLATFORM_PSPC)
// If Pocket PC, allow only one instance of the application.
HWND hWnd = FindWindow (NULL, TEXT("TBIcons"));
if (hWnd) {
SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
return -1;
}
#endif
hInst = hInstance;
// Display dialog box as main window.
DialogBoxParam (hInstance, szAppName, NULL, MainDlgProc, 0);
return 0;
}
//======================================================================
// Message handling procedures for main window
//----------------------------------------------------------------------
// MainDlgProc - Callback function for application window
//
BOOL CALLBACK MainDlgProc (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
INT i;
//
// Search message list to see if we need to handle this
// message. If in list, call procedure.
//
for (i = 0; i < dim(MainMessages); i++) {
if (wMsg == MainMessages[i].Code)
return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
}
return FALSE;
}
//----------------------------------------------------------------------
// DoInitDlgMain - Process WM_INITDIALOG message for window.
//
BOOL DoInitDlgMain (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam){
return 0;
}
//----------------------------------------------------------------------
// DoCommandMain - Process WM_COMMAND message for window.
//
BOOL DoCommandMain (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam){
WORD idItem, wNotifyCode;
HWND hwndCtl;
INT i;
// Parse the parameters.
idItem = (WORD) LOWORD (wParam);
wNotifyCode = (WORD) HIWORD (wParam);
hwndCtl = (HWND) lParam;
// Call routine to handle control message.
for (i = 0; i < dim(MainCommandItems); i++) {
if (idItem == MainCommandItems[i].Code) {
(*MainCommandItems[i].Fxn)(hWnd, idItem, hwndCtl,
wNotifyCode);
return TRUE;
}
}
return FALSE;
}
//----------------------------------------------------------------------
// DoTaskBarNotifyMain - Process MYMSG_TASKBARNOTIFY message for window.
//
BOOL DoTaskBarNotifyMain (HWND hWnd, UINT wMsg, WPARAM wParam,
LPARAM lParam) {
TCHAR szText[128];
SetForegroundWindow (hWnd);
wsprintf (szText,
TEXT ("icon %d "), wParam);
switch (lParam) {
case WM_MOUSEMOVE:
lstrcat (szText, TEXT ("WM_MOUSEMOVE"));
break;
case WM_LBUTTONDOWN:
lstrcat (szText, TEXT ("WM_LBUTTONDOWN"));
break;
case WM_LBUTTONUP:
lstrcat (szText, TEXT ("WM_LBUTTONUP"));
break;
case WM_LBUTTONDBLCLK:
lstrcat (szText, TEXT ("WM_LBUTTONDBLCLK"));
break;
}
Add2List (hWnd, szText);
return 0;
}
//======================================================================
// Command handler routines
//----------------------------------------------------------------------
// DoMainCommandExit - Process Program Exit command.
//
LPARAM DoMainCommandExit (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
NOTIFYICONDATA nid;
// Delete any remaining taskbar icons.
memset (&nid, 0, sizeof nid);
nid.cbSize = sizeof (NOTIFYICONDATA);
nid.hWnd = hWnd;
while (nIconID) {
nid.uID = nIconID--;
Shell_NotifyIcon (NIM_DELETE, &nid);
}
EndDialog (hWnd, 0);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandAddIcon - Process Add Icon button.
//
LPARAM DoMainCommandAddIcon (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
NOTIFYICONDATA nid;
nIconID++;
nid.cbSize = sizeof (NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = nIconID;
nid.uFlags = NIF_ICON | NIF_MESSAGE; // NIF_TIP not supported
nid.uCallbackMessage = MYMSG_TASKBARNOTIFY;
nid.hIcon = (HICON)LoadImage (hInst, MAKEINTRESOURCE (ID_ICON),
IMAGE_ICON, 16,16,0);
nid.szTip[0] = '\0';
Shell_NotifyIcon (NIM_ADD, &nid);
return 0;
}
//----------------------------------------------------------------------
// DoMainCommandDelIcon - Process Del Icon button.
//
LPARAM DoMainCommandDelIcon (HWND hWnd, WORD idItem, HWND hwndCtl,
WORD wNotifyCode) {
NOTIFYICONDATA nid;
if (nIconID == 0)
return 0;
memset (&nid, 0, sizeof nid);
nid.cbSize = sizeof (NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = nIconID--;
Shell_NotifyIcon (NIM_DELETE, &nid);
return 0;
}
//----------------------------------------------------------------------
// Add2List - Add string to the report list box.
//
void Add2List (HWND hWnd, LPTSTR lpszFormat, ...) {
int i, nBuf;
TCHAR szBuffer[512];
va_list args;
va_start(args, lpszFormat);
nBuf = _vstprintf(szBuffer, lpszFormat, args);
i = SendDlgItemMessage (hWnd, IDD_OUTPUT, LB_ADDSTRING, 0,
(LPARAM)(LPCTSTR)szBuffer);
if (i != LB_ERR)
SendDlgItemMessage (hWnd, IDD_OUTPUT, LB_SETTOPINDEX, i,
(LPARAM)(LPCTSTR)szBuffer);
va_end(args);
}
TBIcons can run on a Pocket PC, but the task bar annunciators are visible only when the Today screen is showing. Aside from this difference, the program runs under the Pocket PC as it does under other versions of Windows CE.
The Out Of Memory Error Dialog Box
Because Windows CE applications are almost always running in a limited memory environment, it seems likely that they'll need an Out Of Memory Error dialog box. The standard Windows CE shells give you just such a dialog box as a system service. Figure 16-2 shows this dialog box on a Pocket PC.

Figure 16-2: The Windows CE Out Of Memory Error dialog box
The advantage of using the system-provided Out Of Memory Error dialog box is that you don't have to create one yourself in what, by definition, is already a low-memory condition. The dialog box provided by the system is also correctly configured for the proper screen size and local language. To display an Out Of Memory Error dialog box, you call this function:
int SHShowOutOfMemory (HWND hwndOwner, UINT grfFlags);
The two parameters are the owner window and grfFlags, which must be set to 0.