The New Menu
The Pocket PC shell is centered on two menus, the Start menu and the New menu. The Start menu is basically the same start menu used by the Explorer shell with the exception that it is displayed from the top of the screen down, but the New menu is unique to the Pocket PC.The New menu is available on the Today screen. Tapping any item on the New menu typically launches an application with a template for creating a new item, such as a note, a contact, or an Excel spreadsheet. The new menu can optionally be displayed by applications. This is done by creating a button on the Menu bar with an ID of IDM_SHAREDNEW. When the button, which looks to the user like a menu, is tapped, the system displays the New menu.The New menu can be modified by applications in two ways. First, menu items can be added to the New menu either on a permanent basis, which adds an item when any application is running, or on a temporary basis, which adds an item only when the application is in the foreground. In addition, the foreground application can override the default actions of New menu items.Managing the New Menu
The New menu can be added to a Menu bar in two ways. In the first way, in shared mode, tapping the New menu sends a WM_COMMAND message to the application with an ID value of IDM_SHAREDNEW. In simple (nonshared) mode, tapping the New menu displays a series of permanent menu items gleaned from the registry.
These permanent items are specified in the registry under the key HKEY_LOCAL_MACHINE]\Software\Microsoft\Shell\Extensions\NewMenu. This key lists a series of GUIDs that define COM in-process servers that implement an IID_INewMenuItemServer interface. The IID_INewMenuItemServer interface is actually quite simple. Aside from the standard IUnknown methods, the only method supported is
HRESULT INewMenuItemServer::CreateNewItem (HWND hwndParent);
The single parameter is the handle to the window that currently owns the menu bar. When the user selects the permanent item on the menu bar that references the COM object that implements the IID_INewMenuItemServer interface, the Pocket PC first sends a WM_NOTIFY message with the notification NMN_INVOKECOMMAND to the window owning the menu bar. If the application returns 1, the Pocket PC assumes that the application has taken care of the menu selection and no further action occurs. If the application returns 0, the Pocket PC will load the COM object and call the CreateNewItem method. In response, the COM object typically launches the appropriate application.
The NewMenuX Example
The following code is a simple New menu item extension that launches the calculator. If eMbedded Visual C++ is used to compile and download the NewMenuX server, it will register itself and add the proper registry key to tell the New menu of the existence of NewMenuX. If

="Prog Win CE New Menu Extension"
[HKEY_CLASSES_ROOT\CLSID{130F6E46-C3F9-4fa8-B8BC-75720BC73231}\InProcServer32]
=\Windows\NewMenuX.dll
[HKEY_LOCAL_MACHINE]\Software\Microsoft\Shell\Extensions\NewMenu\
{130F6E46-C3F9-4fa8-B8BC-75720BC73231} = Launch Calc
Enabled = 1
The first few lines register the in-proc server. The last three lines tell the shell about the added New Menu item. The default value of the key above is Launch Calc. The one value under the key is a DWORD value named Enabled, which is set to 1. Listing 17-2 contains the source for the NewMenuX example.Listing 17-2: The NewMenuX example
NewMenuX.def
;
;Standard COM library DEF file
;
LIBRARY NEWMENUX.DLL
EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE
NewMenuX.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
// Declare these here so that the MenuBar example can know the GUID
// {130F6E46-C3F9-4fa8-B8BC-75720BC73231}
static const GUID CLSID_NewMenuX =
{0x130f6e46,0xc3f9,0x4fa8,{0xb8,0xbc,0x75,0x72,0xb,0xc7,0x32,0x31}};
const TCHAR szCLSIDNewMenuX[] =
TEXT ("{130F6E46-C3F9-4fa8-B8BC-75720BC73231}");
#ifndef JUST_GET_THE_GUID
// This isn't defined by the current Pocket PC SDK. Uncomment if needed.
// DECLARE_INTERFACE_(INewMenuItemServer, IUnknown)
// {
// // *** IUnknown methods ***
// STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE;
// STDMETHOD_(ULONG,AddRef) (THIS) PURE;
// STDMETHOD_(ULONG,Release) (THIS) PURE;
// // *** INewMenuItemServer methods ***
// STDMETHOD(CreateNewItem) (THIS_ HWND hwndParent) PURE;
// };
// **** Start of Generic COM declarations ****
//======================================================================
// MyClassFactory - Object declaration
//
class MyClassFactory : public IClassFactory {
private:
long m_lRef;
public:
MyClassFactory();
~MyClassFactory();
//IUnknown methods
STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppv);
STDMETHODIMP_(ULONG) AddRef (THIS);
STDMETHODIMP_(ULONG) Release (THIS);
//IClassFactory methods
STDMETHODIMP CreateInstance (LPUNKNOWN pUnkOuter, REFIID riid,
LPVOID *ppv);
STDMETHODIMP LockServer (BOOL fLock);
};
// **** End of Generic OLE declarations ****
//======================================================================
// MyNewMenuItemServer - Object declaration
//
class MyNewMenuItemServer : public INewMenuItemServer {
private:
long m_lRef;
HWND m_hwndParent;
public:
MyNewMenuItemServer();
~MyNewMenuItemServer();
//IUnknown methods
STDMETHODIMP QueryInterface (THIS_ REFIID riid, LPVOID *ppvObj);
STDMETHODIMP_(ULONG) AddRef (THIS);
STDMETHODIMP_(ULONG) Release (THIS);
//INewMenuItemServer
HRESULT STDMETHODCALLTYPE CreateNewItem (HWND hwndParent);
};
#endif //JUST_GET_THE_GUID
NewMenuX.cpp
//======================================================================
// NewMenuX - A Pocket PC New menu extension
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include <commctrl.h> // Command bar includes
#define INITGUID
#include <initguid.h>
#include <coguid.h>
#include <aygshell.h> // Pocket PC shell includes
#include <shlguid.h> // Shell GUIDs inc New menu ext
#include "NewMenuX.h" // My IM common includes
long g_DllCnt = 0; // Global DLL reference count
HINSTANCE hInst; // DLL instance handle
const TCHAR szFriendlyName[] = TEXT ("Prog Win CE New Menu Extension");
//======================================================================
// DllMain - DLL initialization entry point
//
BOOL WINAPI DllMain (HANDLE hinstDLL, DWORD dwReason,
LPVOID lpvReserved) {
hInst = (HINSTANCE)hinstDLL;
return TRUE;
}
//======================================================================
// DllGetClassObject - Exported function called to get pointer to
// Class factory object
//
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
MyClassFactory *pcf;
HRESULT hr;
// See if caller wants us...
if (IsEqualCLSID (rclsid, CLSID_NewMenuX)) {
// Create IClassFactory object.
pcf = new MyClassFactory();
if (pcf == NULL)
return E_OUTOFMEMORY;
// Call class factory's query interface method.
hr = pcf->QueryInterface (riid, ppv);
// This will cause an obj delete unless interface found.
pcf->Release();
return hr;
}
return CLASS_E_CLASSNOTAVAILABLE;
}
//======================================================================
// DllCanUnloadNow - Exported function called when DLL can unload
//
STDAPI DllCanUnloadNow () {
if (g_DllCnt)
return S_FALSE;
return S_OK;
}
//======================================================================
// DllRegisterServer - Exported function called to register the server
//
STDAPI DllRegisterServer () {
TCHAR szName[MAX_PATH+2];
TCHAR szTmp[128];
DWORD dwDisp;
HKEY hKey, hSubKey;
INT rc;
GetModuleFileName (hInst, szName, sizeof (szName));
// Open the key.
wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNewMenuX);
rc = RegCreateKeyEx (HKEY_CLASSES_ROOT, szTmp, 0, TEXT ("),
0, 0, NULL, &hKey, &dwDisp);
if (rc != ERROR_SUCCESS)
return E_FAIL;
// Set the friendly name of the new menu item extension.
RegSetValueEx (hKey, TEXT ("), 0, REG_SZ, (PBYTE)szFriendlyName,
(lstrlen (szFriendlyName)+1) * sizeof (TCHAR));
// Create subkeys.
// Set the module name of the new menu item server
rc = RegCreateKeyEx (hKey, TEXT ("InProcServer32"), 0, TEXT ("),
0, 0, NULL, &hSubKey, &dwDisp);
rc = RegSetValueEx (hSubKey, TEXT ("), 0, REG_SZ, (PBYTE)szName,
(lstrlen (szName)+1) * sizeof (TCHAR));
RegCloseKey (hSubKey);
RegCloseKey (hKey);
//
// Add entry to add new menu item
//
// Create string, use multuple lines due to book format limits
lstrcpy (szTmp, TEXT ("Software\\Microsoft\\Shell\\"));
lstrcat (szTmp, TEXT ("Extensions\\NewMenu\\"));
lstrcat (szTmp, szCLSIDNewMenuX);
rc = RegCreateKeyEx (HKEY_LOCAL_MACHINE, szTmp, 0, TEXT ("),
0, 0, NULL, &hKey, &dwDisp);
if (rc != ERROR_SUCCESS)
return E_FAIL;
// Set the friendly name of the new menu item extension.
lstrcpy (szTmp, TEXT ("Launch Calc"));
RegSetValueEx (hKey, TEXT ("), 0, REG_SZ, (PBYTE)szTmp,
(lstrlen (szTmp)+1) * sizeof (TCHAR));
dwDisp = 1;
RegSetValueEx (hKey, TEXT ("Enabled"), 0, REG_DWORD, (PBYTE)&dwDisp,
sizeof (DWORD));
RegCloseKey (hKey);
return S_OK;
}
//======================================================================
// DllUnregisterServer - Exported function called to remove the server
// information from the registry
//
STDAPI DllUnregisterServer() {
INT rc;
TCHAR szTmp[128];
wsprintf (szTmp, TEXT ("CLSID\\%s"), szCLSIDNewMenuX);
rc = RegDeleteKey (HKEY_CLASSES_ROOT, szTmp);
if (rc != ERROR_SUCCESS)
return E_FAIL;
// Create string, use multuple lines due to book format limits
lstrcpy (szTmp, TEXT ("Software\\Microsoft\\Shell\\"));
lstrcat (szTmp, TEXT ("Extensions\\NewMenu\\"));
lstrcat (szTmp, szCLSIDNewMenuX);
rc = RegDeleteKey (HKEY_CLASSES_ROOT, szTmp);
if (rc != ERROR_SUCCESS)
return E_FAIL;
return S_OK;
}
//**********************************************************************
// MyClassFactory Object implementation
//----------------------------------------------------------------------
// Object constructor
MyClassFactory::MyClassFactory () {
m_lRef = 1; //Set ref count to 1 on create.
return;
}
//----------------------------------------------------------------------
// Object destructor
MyClassFactory::~MyClassFactory () {
return;
}
//----------------------------------------------------------------------
// QueryInterface - Called to see what interfaces this object supports
STDMETHODIMP MyClassFactory::QueryInterface (THIS_ REFIID riid,
LPVOID *ppv) {
// If caller wants our IUnknown or IClassFactory object,
// return a pointer to the object.
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_IClassFactory)) {
*ppv = (LPVOID)this; // Return pointer to object.
AddRef(); // Inc ref to prevent delete on return.
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);
}
//----------------------------------------------------------------------
// AddRef - Increment object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::AddRef (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//----------------------------------------------------------------------
// Release - Decrement object ref count.
STDMETHODIMP_(ULONG) MyClassFactory::Release (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0)
delete this;
return cnt;
}
//----------------------------------------------------------------------
// LockServer - Called to tell the DLL not to unload, even if use cnt 0
STDMETHODIMP MyClassFactory::LockServer (BOOL fLock) {
if (fLock)
InterlockedIncrement (&g_DllCnt);
else
InterlockedDecrement (&g_DllCnt);
return NOERROR;
}
//----------------------------------------------------------------------
// CreateInstance - Called to have class factory object create other
// objects
STDMETHODIMP MyClassFactory::CreateInstance (LPUNKNOWN pUnkOuter,
REFIID riid,
LPVOID *ppv) {
MyNewMenuItemServer *pMyNMX;
HRESULT hr;
if (pUnkOuter)
return (CLASS_E_NOAGGREGATION);
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_INewMenuItemServer)) {
// Create New menu item object.
pMyNMX = new MyNewMenuItemServer();
if (!pMyNMX)
return E_OUTOFMEMORY;
// See if object exports the proper interface.
hr = pMyNMX->QueryInterface (riid, ppv);
// This will cause an object delete unless interface found.
pMyNMX->Release ();
return hr;
}
return E_NOINTERFACE;
}
//**********************************************************************
// MyNewMenuItemServer Object implementation
//----------------------------------------------------------------------
// Object constructor
MyNewMenuItemServer::MyNewMenuItemServer () {
m_lRef = 1; //Set ref count to 1 on create.
g_DllCnt++;
return;
}
//----------------------------------------------------------------------
// Object destructor
MyNewMenuItemServer::~MyNewMenuItemServer () {
g_DllCnt--;
return;
}
//----------------------------------------------------------------------
// QueryInterface - Called to see what interfaces this object supports
STDMETHODIMP MyNewMenuItemServer::QueryInterface (THIS_ REFIID riid,
LPVOID *ppv) {
// If caller wants our IUnknown or IID_IInputMethod2 object,
// return a pointer to the object.
if (IsEqualIID (riid, IID_IUnknown) ||
IsEqualIID (riid, IID_INewMenuItemServer)) {
// Return pointer to object.
*ppv = (INewMenuItemServer *)this;
AddRef(); // Inc ref to prevent delete on return.
return NOERROR;
}
*ppv = NULL;
return (E_NOINTERFACE);
}
//----------------------------------------------------------------------
// AddRef - Increment object ref count.
STDMETHODIMP_(ULONG) MyNewMenuItemServer::AddRef (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedIncrement (&m_lRef);
return cnt;
}
//----------------------------------------------------------------------
// Release - Decrement object ref count.
STDMETHODIMP_(ULONG) MyNewMenuItemServer::Release (THIS) {
ULONG cnt;
cnt = (ULONG)InterlockedDecrement (&m_lRef);
if (cnt == 0) {
delete this;
return 0;
}
return cnt;
}
//----------------------------------------------------------------------
// CreateNewItem - The new menu item has been selected.
//
STDMETHODIMP_(HRESULT) MyNewMenuItemServer::CreateNewItem
(HWND hwndParent) {
SHELLEXECUTEINFO se;
// Launch the calculator.
memset (&se, 0, sizeof (se));
se.cbSize = sizeof (se);
se.hwnd = hwndParent;
se.lpFile = TEXT ("calc.exe");
se.lpVerb = TEXT ("open");
se.lpDirectory = TEXT ("\\windows");
ShellExecuteEx (&se); // Launch the control panel.
return S_OK;
}
All the preceding code supports the last routine, CreateNewItem, in the NewMenuX example. CreateNewItem simply launches the calculator application by using ShellExecuteEx.
Handling the New Menu from Within an Application
When your application is running, you can extend the New menu by fielding WM_NOTIFY messages with the notify code of NMN_GETAPPREGKEY. This notification is sent when the New menu is about to be displayed. The lParam value points to a NMNEWMENU structure, which is defined as
typedef struct tagNMNEWMENU
{
NMHDR hdr;
TCHAR szReg[80];
HMENU hMenu;
CLSID clsid;
} NMNEWMENU, *PNMNEWMENU;
The hMenu field of this structure contains the handle to the New menu that is about to be displayed. The easiest way to extend the New menu is to use AppendMenu to add menu items to the menu. The added menu items should have ID values greater than IDM_NEWMENUMAX.Some Pocket PC SDKs do not define the NMNEWMENU structure nor do they define the notification messages. When compiling using those SDKs, you will need to define these items in your application. The definitions for the notification codes are shown here.
#define NMN_GETAPPREGKEY 1101
#define NMN_NEWMENUDESTROY 1102
#define NMN_INVOKECOMMAND 1103
The NMN_GETAPPREGKEY notification is sent to the foreground application when the New menu is displayed. As you might expect, the NMN_NEWMENUDESTROY notification is sent when the New menu is destroyed. Finally, the NMN_INVOKECOMMAND notification is sent when the user selects a specific item.The following code fragment fields the notification and adds an extra item to the New menu.
#define IDM_MYNEWMENUITEM (IDM_NEWMENUMAX+1)
// See if New menu is being displayed.
if (lpnhr->code == NMN_GETAPPREGKEY) {
lpNewMenu = (PNMNEWMENU) lParam;
AppendMenu (lpNewMenu->hMenu, MF_ENABLED, IDM_MYNEWMENUITEM,
TEXT ("My own New menu item"));
AppendMenu (lpNewMenu->hMenu, MF_SEPARATOR, 0, 0);
}
When the user selects the added item on the New menu, a WM_COMMAND message will be sent with the ID value of the menu item added to the New menu.