Architectures of the Today Screen Plug-in and the Home Screen Plug-in
4/7/2010
Nicolas Guibourgé, Microsoft Corporation
February 2007
Summary
Compare the architectures of the Today screen plug-in (for Pocket PCs) and the Home screen plug-in (for Smartphones). This article also gives advice and samples to efficiently design these plug-ins. This article assumes that you have a basic understanding about the Microsoft® Windows® CE operating system, programming concepts, C++ language, and COM. (11 printed pages)
Download code sample from the Microsoft Download Center.
Applies To
Microsoft Windows Mobile®–based Pocket PCsWindows Mobile–based Smartphones
Introduction
How it Works
How to Optimize
Samples
Conclusion
Additional Information
Introduction
This article is divided into three sections: How It Works, How to Optimize, and Samples. The first section explains the architecture in the Today Screen plug-in and Home Screen plug-in. The second section discusses best practices and optimizations, and the third section provides some code samples.
How it Works
This section is divided into two parts: Today Screen, which describes the Today Screen plug-in architecture, and Home Screen, which describes the Home Screen plug-in architecture.
Today Screen
This section discusses the static organization, process and thread architecture, and initialization of a Today Screen plug-in. It is also important to remember that a Today Screen plug-in only targets Pocket PCs.
Static Organization
A Today screen plug-in is a child window of the Today screen. A Today screen plug-in is a dynamic-link library (DLL) that exports the InitializeCustomItem function and has its own window procedure. The Today screen application (Shell32.exe) dynamically loads the plug-in by means of the LoadLibrary functions. You can enable a Today screen plug-in by using the settings or the means of registry keys.
Process and Thread Architecture
A Today screen plug-in runs in the context of the Shell32 process. It is hosted by the Today screen thread that creates the Today screen and initializes the plug-ins by calling the InitializeCustomItem function, as shown in Figure 1.
Figure 1. Today screen process and thread architecture
Plug-in Interface
A Today screen plug-in is simple to write. The plug-in has to do the following:
- Export one entry point, which returns the window handle of the plug-in window: the InitializeCustomItem function. This function is exported by using an ordinal number. The Shell32 process uses this ordinal number to get a pointer to this function by using LoadLibrary and GetProcAddress functions.
- Manage the following specific messages in its window procedure (in addition to the standard messages, such as WM_PAINT):
- WM_TODAYCUSTOM_CLEARCACHE
- WM_TODAYCUSTOM_QUERYREFRESHCACHE
Plug-in Initialization
The Shell32 process initiates a plug-in initialization at boot time or each time the user changes the Today screen items list on the Pocket PC's settings. The Shell32 process:
- Loads a custom plug-in DLL if the DLL is not already loaded. (The DLL's DllMain is then called).
- Calls the InitializeCustomItem function that must return the plug-in’s window handle.
- Sends the WM_TODAYCUSTOM_CLEARCACHE message to the plug-in.
- Sends the WM_TODAYCUSTOM_QUERYREFRESHCACHE message to the plug-in. The plug-in must indicate its height and return TRUE; otherwise, the plug-in will not be displayed.
Home Screen
This section discusses the static organization, process and thread architecture, and initialization of a Home Screen plug-in. It is also important to remember that a Home Screen plug-in only targets Smartphones.
Static Organization
A Home screen plug-in does not have any windows; more accurately, it does not own a window—it only draws in a rectangle. The Home process, which hosts the Home screen plug-in, indicates the rectangle that has to be drawn.
A Home screen plug-in is a Component Object Model (COM) object that implements the IHomePlugin and IPersistStream interfaces. The Home process loads a plug-in by using a class factory mechanism.
The Home process uses XML files to get the list of the plug-ins that it must load. Each plug-in is identified by its GUID in the XML file. The GUID is the one that is associated with the COM object that contains the plug-in and that has been registered upon the plug-in installation.
Process and Thread Architecture
A Home screen plug-in runs in the context of the Home process. It is hosted by the Home screen thread that hosts the window procedure, as shown in Figure 2. A plug-in does not receive any Windows message because it does not create a window neither use a window procedure. The Home process sends events such as PE_PAINT or PE_KEYDOWN to its plug-in by calling the IHomePlugin::OnEvent interface of the plug-in. The Home process is the only application that can send PE_ events to plug-ins.
Figure 2. Home screen process and thread architecture
Plug-in Interface
Writing a Home screen plug-in is more complicated than writing a Today screen plug-in because the class factory and COM interface infrastructure need to be coded. However, it is possible to isolate the class factory and COM interface infrastructure and to focus on the drawing (for more information, see this article's download code sample). In such a design, the code that manages the drawing is very similar to a Today screen plug-in. The plug-in code must initialize itself using the IHomePlugin::Initialize and IPersistStream::Load methods, and it must manage events using the IHomePlugin::OnEvent interface, which replaces the window procedure of a Today screen plug-in.
Plug-in Initialization
A Home screen plug-in performs a two-step initialization process:
- When you select a Home screen layout that contains the plug-in, the Home screen application (Home.exe):
- Loads the plug-in's DLL and creates the plug-in instance by using the standard COM object creation technique of calling the DllGetClassObject function and the IClassFactory::CreateInstance interface method.
- Initializes the plug-in by calling the IHomePlugin::Initialize interface method. This interface provides access to the XML file that contains the Home screen layout by means of its parameters.
- Persists the plug-in data by calling the IPersistStream::Save interface method.
- Unloads the plug-in DLL.
- When the Smartphone boots or just after the user selects a new Home screen layout, the Home screen application (Home.exe) does the following:
- Loads the DLL and creates the plug-in instance by calling the DllGetClassObject function and the IClassFactory::CreateInstance interface.
- Loads the plug-in data that have been persisted by calling the IPersistStream::Load interface.
After the plug-ins initialize, the Home process draws them by calling the IHomePlugin::OnEvent interface method. This interface and other standard interfaces are used as long as the plug-in is running. When the user turns off the device or when the user selects another layout that does not contain the plug-in, the plug-in is released and its DLL is unloaded by using the standard COM technique of calling DLLCanUnloadNow. However, the DLL is not immediately unloaded after the DllCanUnloadNow function returns S_OK. The Home screen application waits several minutes before unloading it.
The first step of the initialization gives the developer the opportunity to save the initial data retrieved from the XML file in a format that is faster to manipulate. The second step is done each time the plug-in is loaded. The IPersistStream interface saves the data during the first step and reads the data during the second step.
How to Optimize
This section is divided into five parts: Data Flow, Power Management, Display Management, Memory Consumption, and Initialization. The subsections in each of these parts are subjects that software designers should take care of when they design a Today screen or a Home screen plug-in.
Data Flow
This section presents two common rules about data flow. These rules are:
- Separate data from drawing.
- Use notification instead of polling.
This section also explains how you can manage notification.
Separate Data from Drawing
Retrieving data can be slow or even very, very slow, for example, filtering all appointments of the current day.
The Shell32 or Home application draws its plug-ins one after another. For that purpose, it goes through its plug-in list and sends either a WM_PAINT message or a PE_PAINT message to each of them. If a plug-in takes time to retrieve or compute the data it needs when it receives WM_PAINT or PE_PAINT, it delays its own display and the display of the plug-ins that are following it in the list.
A Today screen or Home screen plug-in should retrieve and compute the data it needs in a thread that is different than the one that manages the drawing—even though this technique is more complicated than having data management and drawing done upon WM_PAINT or PE_PAINT message reception.
Use Notification Instead of Polling
Using notification instead of polling is a good rule that saves the CPU time, the battery energy, and also increases the plug-in’s responsiveness, and, in many cases, the responsiveness of the whole device.
To illustrate, assume that you should design a plug-in that displays the number of appointments the device user has during the current week. To always be up to date, this plug-in can do the following:
- Monitors the agenda database each second and checks if there is a new appointment or if an appointment has been deleted or moved. Then, it updates the appointment counter accordingly. When you use this technique, the plug-in consumes CPU time and battery energy each second to check if its appointment counter needs to be changed.
- Waits for the notification of new appointment, appointment change, or a deleted appointment from the agenda application and changes its counter accordingly. In such a case, CPU time and battery energy only is consumed when necessary.
Manage Notifications
There are several ways of handling notifications. The design must guarantee that data retrieval will not delay the plug-in’s drawing. In most cases, this means that the data will be retrieved and computed in another thread than the one that draws the plug-in.
An obvious design would be to implement a worker thread that receives notification, computes data, and then calls the IHomePluginEnvironment::InvalidatePlugin interface (Home screen plug-in) or the InvalidateRect method (Today screen plug-in) to initiate a new drawing of the plug-in as shown in Figure 3. The Home or Shell32 application consequently sends a PE_PAINT message or WM_PAINT message (respectively) to the plug-in.
Figure 3. Worker thread
A notification can consist of a callback procedure, an event, a message queue, or a Windows message. The state and notification broker, which sends notifications of system information in a Windows Mobile 5.0 system, can send notifications by using all of those mechanisms, whereas the Pocket Outlook® Object Model (POOM) only offers notification by means of Windows messages, such as the PIM_ITEM_* message from the calendar. The Today screen plug-in can easily manage this kind of notification by using its window procedure. However, you should not forget that the window procedure of a specific plug-in is executed in the context of the thread that also executes the window procedure of each activated plug-ins. Therefore, a good design would manage the data from the agenda in a worker thread that the window procedure uses upon reception of the Windows message notification.
Developers cannot use the Windows message design for Home screen plug-ins without some extra programming because a Home screen plug-in does not host any windows and, therefore, any window procedures. To handle the Windows message notification, a Home screen plug-in must implement a listener window that manages the Windows message in its window procedure. A listener window is simply a window that is not displayed and that does not draw anything. The window procedure of this listener window manages the Windows message notification. This concept is illustrated in the Notification sample in this article's download code sample. When implementing such a design, you should not forget that the window procedure is executed in the context of the thread that has created it. You must implement one of the following two designs:
- Have a listener window that forwards notifications to a worker thread, which manages the data and calls the IHomePluginEnvironment::InvalidatePlugin interface to initiate the drawing of the plug-in.
- Create a worker thread that creates the listener window and hosts the message loop. The window procedure handles the message, manages the data, and then calls the IHomePluginEnvironment::InvalidatePlugin interface to initiate the drawing of the plug-in.
Power Management
Because power is a restricted resource on Pocket PCs and Smartphones, it is essential to conserve it. A Today screen or a Home screen plug-in must limit the amount of power it uses and only redraw if the display is on or if the information is important enough, for example, a reminder. In the case of Smartphones, you can use the GetSystemPowerState API to determine whether the display is turned on, as the following code example shows.
DWORD PwrFlag, NameLength ;
TCHAR StateName[64] = { 0 } ;
GetSystemPowerState(StateName, NameLength, &PwrFlag) ;
if(POWER_STATE_ON != PwrFlag)
InvalidateRect(hPlugInWnd, NULL, TRUE) ;
Display Management
This section discusses items that you should take into account when you design the drawing of a Today screen plug-in or a Home screen plug-in.
Screen Resolution
Working with the correct resolution definitely matters, for example, the BitBlt function can be 10 times slower or more if the plug-in does not use the device resolution. The BitBlt function transfers a screen buffer into another screen buffer; it is often used to transfer an off-screen buffer into the screen buffer. If the resolutions of the two-screen buffers are different, the BitBlt function has to calculate each pixel of the targeted buffer according to the resolutions. If the resolutions of the two-screen buffers are equal, the BitBlt function only has to copy the bytes. If a plug-in creates an off-screen buffer by using the CreateDIBSection function with a 32 bit per pixel resolution ,the BitBlt function that transfers the off-screen buffer into the screen buffer is much slower than if this plug-in has used the device resolution to create the device-independent bitmap (DIB) section. You can use the GetDeviceCaps(hDC, BITSPIXEL) function to get the device resolution. For the very same reason, a plug-in should create the device context it uses to draw by using the CreateCompatibleDC function instead of the CreateDC function.
High Resolution
This section only applies to the Today screen plug-in.
A Windows Mobile 2003-based plug-in is still compatible with Windows Mobile 5.0. However, because a Windows Mobile 2003–based plug-in cannot be aware of a high-resolution display, drawing this plug-in is handled in a specific manner where pixels are doubled to fit in the high-resolution display.
If the plug-in only targets Windows Mobile 5.0 devices, it is better to avoid this pixel doubling technique and generate a high-resolution aware plug-in.
Screen Rotation
This section only applies to the Today screen plug-in.
Because a Today screen plug-in is a child window of the Today screen window, it does not receive the WM_SETTINGCHANGE message, which is sent when the screen rotates. It receives the following messages:
- WM_TODAYCUSTOM_CLEARCACHE
- WM_TODAYCUSTOM_QUERYREFRESHCACHE
If the plug-in height changes due to screen rotation, the new height must be indicated at this time. The Today screen plug-in receives this message before the plug-in window rotates; therefore, it is dangerous to use the window’s rectangle to determine the screen's orientation. It is safer to use the system metrics by using GetSystemMetrics function.
- WM_WINDOWPOSCHANGED or WM_SIZE
- WM_PAINT
The screen rotation is faster if the plug-in does not change its height.
Memory Consumption
Memory is consumed in the context of the hosting process—Shell32.exe for Pocket PCs and Home.exe for Smartphones. The memory heap belongs to the process; therefore, allocation consumes memory from the process heap that other plug-ins or DLLs also use (for example, the Shell32 process on Pocket PCs also hosts the control panel applications). Allocating big chunks of memory (several megabytes) from the process heap without releasing them can create a memory shortage situation where memory allocation is no longer possible from the heap even though enough RAM may be available on a system-wide perspective. This can prevent a DLL from being loaded, for example, each time the system loads, a DLL it temporarily needs to allocate memory from the heap.
Some Windows API functions consume memory from the process heap (for example, the CreateDIBSection function that allocates its buffer from the default process heap). For example, a plug-in that creates a DIB section that is 32 bit per pixels and is the same size as the screen will consume several megabytes from the process heap.
Initialization
This section discusses two topics: waiting for API readiness and enabling and disabling plug-ins in Settings.
Waiting for API Readiness
The Shell32 application is loaded early during the boot process; therefore, it can load a plug-in before an API set is ready for use. Using an API before it is ready causes the application to throw an exception. To determine if an API is ready, a plug-in can use the IsAPIReady function—or even better, the API ready event. The name of this event is defined in the registry (HKEY_LOCAL_MACHINE\System\Events in association with an event name, for example, ShellAPIReady for the Shell API).
Even though this applies more to Today screen plug-ins, it is safer to also apply it when designing Home screen plug-ins.
Enabling and Disabling Plug-ins in Settings
Each time the user changes the Today screen settings (for example, when enabling or disabling plug-ins or when changing the Today screen theme), the Shell32 process destroys the window for all of the previously enabled plug-ins and initializes all of the plug-ins that are enabled, which leads to the following series of events:
- All of the previously enabled plug-ins receive a WM_DESTROY message.
- The DLL of the disabled plug-in unloads, and the DLL main function is called (usually the DllMain function).
- All of the enabled plug-ins are initialized, and the InitializeCustomItem function of each plug-in is called.
The InitializeCustomItem function could consequently be called several times without the DLL being unloaded because the plug-in remains enabled from one setting validation to another one. When the InitializeCustomItem function is called, memory allocation must be carefully handled. You should take care not to leak memory by allocating it twice or instantiating an object twice. A safe rule to follow is to allocate memory and instantiate objects when the InitializeCustomItem function runs and to free memory and delete objects when the WM_DESTROY message is received.
You should also consider that the window handle of the plug-in window will change because a new plug-in window is created each time the InitializeCustomItem function is called. This is very important to remember when the plug-in window is receiving notification messages.
Samples
This section introduces the three samples that this article illustrates.
Today Screen Skeleton
The Today screen sample (part of this article's download code sample) writes text on the screen and handles screen rotation.
Home Screen Skeleton
The Home screen sample (part of this article's download code sample) is structured as follows:
- The PluginFactory class implements the IClassFactory interface.
- The SpPlugInBase class implements the standard interface that a Home screen plug-in must implement. This class does not implement the content-related methods, such as the OnEvent method, that are pure virtual. The SpPlugInBase class cannot be instantiated and must be derived by another SpPlugIn class, which implements those methods.
- The SpPlugIn class derives from the SpPlugInBase class and implements all of the methods that are related with the plug-in's content. This class draws the plug-in by using the DrawText function to keep things simple.
This three-class architecture simplifies the plug-in implementation by isolating the COM and class factory mechanisms in the PluginFactory and the SpPlugInBase classes and focuses on the plug-in content by implementing the SpPlugIn class.
Notification
The Notification handling sample (part of this article's download code sample) reuses the Today screen skeleton and adds the following:
- The DataMgt_t class that implements the worker thread/listener window structure. The worker thread creates the listener window and hosts its message loop. The listener window receives the POOM notification.
- The PoomAgenda_t class that deals with the POOM interface.
Conclusion
The main differences between a Today screen plug-in and a Home screen plug-in are the following:
- Class factory and COM architecture is used in the Home screen plug-in—and not in the Today screen plug-in, which is a standard Win32® DLL.
- The way Home screen and Today screen plug-ins are initialized.
- The drawing structures are different: window versus rectangle—or, more accurately, window procedures versus the IHomePlugin::OnEvent interface.
- A Today screen plug-in receives all WM_ messages that a child window can receive in addition to the messages that are directly sent to its window (for example, notification from POOM).
- A Home screen plug-in is notified of PE_* events, such as PE_PAINT or PE_KEYDOWN. Consequently, a Home screen plug-in is not able to directly manage any notifications that are sent by the means of WM_ messages (for example, POOM notifications).
Both a Today screen and a Home screen plug-in runs in the context of the host process (Home.exe of Shell32.ex) with the windows message handler and interface methods of each plug-in managed by the same thread, which also manages the other plug-ins. Therefore, a plug-in should take care about how it interacts with its hosting process, thread, and with the other plug-ins—particularly from data management, drawing, and memory consumption perspectives. A plug-in should be designed to always consider that battery energy is a restricted resource on Windows Mobile–based devices and should always try to save it.
The following are online resources about the Today screen (Pocket PC):
The following is an online resource about the Home screen (Smartphone):