The State and Notifications Broker API Part 3

6/4/2007

Jim Wilson, JW Hedgehog, Inc.

May 2007

Summary

This month's column completes Jim's three-part State and Notifications Broker series. In this installment Jim covers a variety of State and Notifications Broker-related topics including over 40 new Windows Mobile 6 State and Notifications Broker values, how to create and handle custom notifications, multithreaded notification handling, the nuances of sharing persistent notifications across native and managed applications, and some unexpected details in the managed State and Notifications Broker class' bitmask handling implementation. Just as in the previous two installments, Jim covers both native and managed code throughout.

Applies To

Microsoft® .NET Compact Framework 1.0

Microsoft .NET Compact Framework 2.0

Microsoft Visual Studio® 2005

Microsoft Windows Mobile® 5.0

Microsoft Windows Mobile 6

Introduction

New Windows Mobile 6 State and Notification Values

Custom State and Notifications Broker Values

Custom State and Notifications Broker Values in Native Code – Housekeeping

Custom State and Notifications Broker Values in Native Code – Improving Efficiency

Threading and Notification Handling

Persistent Notifications – Native and Managed Application Compatibility

One Last Thing: Bitmasks and Managed Applications

Conclusion

Introduction

Welcome to part three of the State and Notifications Broker API series. We have just a few more things to cover and then you will know pretty much everything there is to know about using the State and Notifications Broker API.

If you haven't been following along with the series or if you need a refresher, both Part 1 and Part 2 of this series are still available. Part 1 focuses on the basics of using the State and Notifications Broker API to access state values and receive notification of changes. Part 2 focuses on notification specialization, things such as conditional notifications, batch notifications, and persistent notifications.

In this installment, we'll cover the new Windows Mobile 6 State and Notifications Broker values, custom notifications, multithreaded notification handling, sharing persistent notifications across native and managed applications, and the implementation of bitmask handling in the managed State and Notifications Broker classes. Like always, we cover both native and managed code throughout.

New Windows Mobile 6 State and Notification Values

Windows Mobile 6 introduces over 40 new State and Notifications Broker values. The new values cover a number areas including Bluetooth status information, Wi-Fi status information, various lock states, cellular system availability, and more.

The addition of these new State and Notifications Broker values demonstrates the importance of the State and Notifications Broker API managed class, SystemState, being part of the Windows Mobile platform rather than being tied to a particular version of the .NET Compact Framework library. If the SystemState class were part of a .NET Compact Framework library, we'd have to wait for the next release of the .NET Compact Framework for an updated version of the SystemState class that includes the static properties that expose the new state values. As part of the Windows Mobile platform, the SystemState class doesn't have this problem because the assembly containing the SystemState class, Microsoft.WindowsMobile.Status, is contained in the Windows Mobile device itself and is therefore updated with each new Windows Mobile release. Installing the Windows Mobile 6 SDK on your development computer installs an updated version of the design-time Microsoft.WindowsMobile.Status assembly so that the compilers and Visual Studio 2005 are aware of the new properties.

Rather than using a class-based abstraction to access the State and Notifications Broker state values, native applications read the values directly from the registry and therefore need the registry path information at compile time. The Windows Mobile 6 version of the snapi.h header file is updated to contain the macro definitions for the new state value registry paths.

Appendix A contains the list of the new Windows Mobile 6 State and Notifications Broker state values.

Note

The Windows Mobile 6 SDK includes an important fix that affects both native and managed code. In the Windows Mobile 5.0 SDK, the SystemState.KeyboardPresent property and the corresponding macro SN_KEYBOARDPRESENT_ROOT in snapi.h incorrectly referred to the HKEY_LOCAL_MACHINE registry hive as containing the keyboard present value; this value is actually stored under the HKEY_CURRENT_USER hive. The Windows Mobile 6 SDK corrects this issue in both the SystemState class and in the snapi.h macros.

Custom State and Notifications Broker Values

As you've seen throughout this series, the State and Notifications Broker provides a powerful and easy-to-use API that provides a central resource for all Windows Mobile–related information including a publish/subscribe model for applications wanting to receive notifications of changes in those state values. The State and Notifications Broker is not limited to exposing state values related to Windows Mobile itself; any application can expose state information through the State and Notifications Broker.

Just as with the Windows Mobile state values, applications can subscribe to these application-defined state values and the State and Notifications Broker will notify the subscribing application when the value changes. The State and Notifications Broker API provides all of the same features for application-defined state values at it does for the Windows Mobile state values, including conditional notifications, batch notifications, and persistent notifications.

Note

For a refresher on the details of conditional notifications, batch notifications, and persistent notifications, check out Part 2 of this series.

The documented guidelines indicate that you should create application-defined state values under either the HKEY_CURRENT_USER\Software\State registry key or the HKEY_LOCAL_MACHINE\Software\State registry key. Unless you're planning to sign your application as a privileged application, I recommend that you avoid the HKEY_LOCAL_MACHINE hive and always store your application-defined state values under the HKEY_CURRENT_USER\Software\State registry key.

Future releases of Windows Mobile will likely have greater security restrictions than Windows Mobile has today. As part of the increased security restrictions, there's a strong possibility that Windows Mobile will restrict which applications are allowed to modify system-related areas such as the registry keys and values under HKEY_LOCAL_MACHINE. Keeping your application-defined state values under the HKEY_CURRENT_USER hive helps you to reduce the likelihood that your application will encounter security-related compatibility issues with future versions of Windows Mobile.

In general, you should organize all application-defined state values for a particular application on a single registry key. Keeping the state values tied to a single registry key creates a logical relationship between common state values and makes working with the state values much easier during application development and debugging. A good example of grouping common state values on a single registry key is the registry key containing the Pocket Outlook® Task values, HKEY_CURRENT_USER\Software\State\Tasks, as shown in Figure 1.

Figure 1. The Pocket Outlook Task state values in the registry

To minimize the likelihood that state values from different programs and different organizations collide, I suggest that you use a naming hierarchy similar to that used to store application configuration information. As you may recall, the Windows guidelines indicate that application-specific configuration values should be located under the HKEY_CURRENT_USER\Software registry key, with each organization creating a key under the Software key, and each application having a key under the organization key. For example, figure 2 shows the registry hierarchy that stores the Microsoft ActiveSync® configuration information.

Figure 2. The Microsoft ActiveSync configuration values in the registry

When creating the application-defined state values, we'll use this same naming hierarchy except that the application-defined state values are placed under the HKEY_CURRENT_USER\System\State registry key rather than under the HKEY_CURRENT_USER\Software registry key. Using this naming scheme, all state values for an application named ExampleApp from an organization named Proseware are located on the HKEY_CURRENT_USER\Software\State\Proseware\ExampleApp registry key.

Custom State and Notifications Broker Values in Native Code

When working in native code, there is very little difference between working with application-defined state values and Windows Mobile state values. You still use the RegistryGetString and RegistryGetDWORD functions to retrieve the state values and you still use the RegistryNotifyWindow and RegistryNotifyApp functions to register for value change notifications.

The only special consideration is how an application creates and changes state values. For this, you use the RegistrySetString and RegistrySetDWORD functions. Like RegistryGetString and RegistryGetDWORD, the only difference between RegistrySetString and RegistrySetDWORD is the registry data type on which they operate. Like the other State and Notifications Broker API functions, RegistrySetString and RegistrySetDWORD require that you include the <regext.h> header file.

The following code example demonstrates using RegistrySetString to set a registry value named "Sales Region" on the HKEY_CURRENT_USER\System\State\Proseware\ExampleApp registry key.

#define EXAMPLEAPP_SALESREGION_ROOT HKEY_CURRENT_USER
#define EXAMPLEAPP_SALESREGION_PATH _T("System\\State\\Proseware\\ExampleApp") 
#define EXAMPLEAPP_SALESREGION_VALUE _T("Sales Region")

HRESULT hr = RegistrySetString(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
  EXAMPLEAPP_SALESREGION_VALUE, "Northeast");

An application can now register to be notified of changes in the "Sales Region" state value using the RegistryNotifyWindow function just as it would when registering for Windows Mobile state value change notifications. The following code demonstrates registering for "Sales Region" state value change notifications.

HRESULT hr = RegistryNotifyWindow(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
  EXAMPLEAPP_SALESREGION_VALUE, g_hWnd, WM_SALESREGIONCHANGED, 0, NULL, &g_hNotifySalesRegion);

Note

If you need a refresher on registering for state value notifications, check out the Receiving Notifications section in Part 1 of this series. For a refresher in registering for persistent notifications, check out the Persistent Notifications section in Part 2 of this series.

Custom State and Notifications Broker Values in Native Code – Housekeeping

The RegistrySetString and RegistrySetDWORD functions require that the key you specify exist. If the key does not exist, the function fails. One way to ensure that your application successfully sets the registry value is to check for the registry key before calling the RegistrySetXXX functions. In my opinion, this solution is unnecessarily costly. The registry key likely exist the vast majority of the time that your application calls the RegistrySetXXX functions. The only time the key doesn't exist is the very first time your application calls either of the RegistrySetXXX functions to set a value on the application's registry key.

Because you can normally expect the key to exist, I prefer to simply call the RegistrySetXXX function and check the return code. If the RegistrySetXXX function fails, create the appropriate registry key and then try to set the value again. The following code example demonstrates this technique.

HRESULT SetSalesRegion(TCHAR *salesRegion)
{
  HRESULT hr = RegistrySetString(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
   EXAMPLEAPP_SALESREGION_VALUE, salesRegion);
  if (FAILED(hr))
  {
    CreateRegistryKeyForExampleApp();
    hr = RegistrySetString(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
     EXAMPLEAPP_SALESREGION_VALUE,  salesRegion);
  }
  return hr;
}

The preceding code example wraps the call to the RegistrySetString function in an application function named SetSalesRegion. The SetSalesRegion function encapsulates all of the necessary logic to set the registry value and create the registry key if necessary. When the SetSalesRegion function is called, the function immediately calls the RegistrySetString function to store the salesRegion parameter value in the registry. If the RegistrySetString function executes successfully, there is no further work for the SetSalesRegion function so the SetSalesRegion function returns. If, on the other hand, the RegistrySetString function should fail, the SetSalesRegion function calls the CreateRegistryKeyForExampleApp function that, as its name implies, creates the registry key that will contain all of the state values for the ExampleApp application. When the CreateRegistryKeyForExampleApp function returns, the SetSalesRegion function again calls the RegistrySetString function to store the value of the salesRegion parameter. With the registry key now created, the call to RegistrySetString succeeds.

The following code example shows the implementation of the CreateRegistryKeyForExampleApp function.

void CreateRegistryKeyForExampleApp()
{
  HKEY hKey = NULL;
  long errorCode = 0;

  errorCode = RegCreateKeyEx(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
 0, NULL, 0, NULL, NULL, &hKey, NULL);
  if (errorCode == 0)
    RegCloseKey(hKey);
}

As the preceding code example shows, the implementation of the CreateRegistryKeyForExampleApp function is very simple. The CreateRegistryKeyForExampleApp function creates the required registry key, HKEY_CURRENT_USER\Software\State\Proseware\ExampleApp, by calling the RegCreateKeyEx function. The returned registry key handle is then closed. The registry key handle is no longer needed because the call to the RegistrySetString function in the SetSalesRegion function accesses the registry key using a registry path string rather than using the registry key handle.

Note

Notice that the CreateRegistryKeyForExampleApp function only creates the registry key; the function does not create the registry value. Although the RegistrySetXXX functions require that the registry key exist, there is no such requirement for the registry value. The RegistrySetXXX functions will automatically create the registry value if the value doesn't already exist.

Custom State and Notifications Broker Values in Native Code – Improving Efficiency

By accepting the registry key path as a string, the RegistrySetString and RegistrySetDWORD functions make setting state values easy. You do not have to open, manage, or close a registry handle; instead you simply specify a string containing the registry key path. Although convenient, this approach has a cost. The RegistrySetString and RegistrySetDWORD functions must traverse the registry tree, open the registry key, set the value, and close the registry key each time you call either of these functions. The convenience of passing the registry key path as a string works well in situations where an application only occasionally changes a state value; however, for applications that make frequent changes to a state value requiring the RegistrySetString and RegistrySetDWORD functions to repeatedly open and close the registry handle each time the application sets the state value results in a great deal of unnecessary overhead.

You can avoid incurring this unnecessary overhead by having your application explicitly open the registry key and store the returned registry key handle. Each time your application needs to set the state value, pass the registry key handle to the RegistrySetString or RegistrySetDWORD function instead of passing the registry key path. With the registry key handle, the RegistrySetString and RegistrySetDWORD functions access the registry key directly; this makes the process of setting the state value very efficient.

Note

If you're monitoring a string state value that changes frequently, the same efficiency concerns exist. As you'll recall from Part 1, notification messages for DWORD state values include the new value in the message; however, string values require your application to read the new value from the registry. To improve the efficiency of an application monitoring a frequently changing string value, you should follow the same efficiency guidelines discussed in this section when calling the RegistryGetString function.

The following example code demonstrates explicitly opening the registry key and passing the registry key handle to the RegistrySetString function.

static HKEY hRegExampleApp = NULL;

HRESULT SetSalesRegion(TCHAR *salesRegion)
{
  if (hRegExampleApp == NULL)
    RegCreateKeyEx(EXAMPLEAPP_SALESREGION_ROOT, EXAMPLEAPP_SALESREGION_PATH, 
     0, NULL, 0, NULL, NULL, &hRegExampleApp, NULL);

  HRESULT hr = RegistrySetString(hRegExampleApp, NULL, EXAMPLEAPP_SALESREGION_VALUE, salesRegion);

  return hr;
}

In the preceding code example, the SetSalesRegion function starts by checking the hRegExampleApp static field to see if the registry key is open. On the first call to this function, the registry key handle will be NULL, resulting in the SetSalesRegion function calling the RegCreateKeyEx function to retrieve a handle to the appropriate registry key. The returned handle is stored in the hRegExampleApp static field.

It may seem strange to always call the RegCreateKeyEx function rather than the RegOpenKeyEx function; calling the RegCreateKeyEx function may seem to imply that the application never expects the registry key to exist. This however is not the case. The reason the SetSalesRegion function uses the RegCreateKeyEx function is because the RegCreateKeyEx function is architected to be flexible. If the specified key does not exist, the RegCreateKeyEx function creates and opens the key; if the specified key already exists then the RegCreateKeyEx function simply opens the key. In either case, the function returns a handle to the opened key.

Note

If your application needs to know whether the RegCreateKeyEx function creates the key or simply opens it, you can pass a reference to a DWORD variable as the last parameter to the RegCreateKeyEx function. When the RegCreateKeyEx function returns, the DWORD variable contains a value indicating the specific action the RegCreateKeyEx function performed. The two possible values are REG_CREATED_NEW_KEY or REG_OPENED_EXISTING_KEY.

With the key handle stored in the hRegExampleApp static field, all successive calls to the SetSalesRegion function utilize the open registry key's handle and efficiently update the state value without needing to perform tree traversal or incur the overhead of needing to open the key.

Note

Just a little note for any readers who may not have had the opportunity to use their C programming skills in a while: in the preceding code example the keyword static is used in the C context rather than in the C++ context. This means that the hRegExampleApp field will maintain its value between calls to the SetSalesRegion function.

Custom State and Notifications Broker Values in Managed Code

As you know from Part 1 of this series, you use the SystemState class to interact with Windows Mobile state values. The SystemState class encapsulates the registry details associated with the Windows Mobile state values and allows you to interact with these state values through static properties on the SystemState class or by specifying the SystemProperty enumeration to the SystemState class constructor. The SystemState class is actually a thin veneer over a class named RegistryState, which is the class that takes care of all of the details of interacting with and monitoring the state values. The SystemState class is provided as a convenience to simplify accessing the system state values.

Note

The implementation of the SystemState class uses the RegistryState class to do the actual work. The SystemState class maintains a HashTable that maps the SystemProperty enumeration values to the appropriate registry keys and value names. When an instance of the SystemState class is created, the SystemState constructor looks up the registry information that corresponds to the SystemProperty value passed to the SystemState constructor; the SystemState constructor then uses the registry information to create an internal instance of the RegistryState class. Once constructed, most of the SystemState class members are simply wrappers over the corresponding RegistryState members.

Interacting with custom state values requires that you have a way to explicitly specify the registry key and value name. To do this, you bypass the SystemState class and use the RegistryState class directly.

The following code example demonstrates how to construct an instance of the RegistryState class that is associated with the Sales Region value on the HKEY_CURRENT_USER\Software\State\Proseware\ExampleApp registry key.

const string exampleAppSalesRegionKey = @"HKEY_CURRENT_USER\Software\State\Proseware\ExampleApp";
const string exampleAppSalesRegionValue = "Sales Region";
RegistryState salesRegionState = new RegistryState(exampleAppSalesRegionKey, exampleAppSalesRegionValue);

As you can see in the preceding code example, constructing the RegistryState instance is as simple as specifying the registry key as the first constructor parameter and the value name as the second parameter. Once constructed, a RegistryState instance provides basically the same features as an instance of the SystemState class: the CurrentValue property retrieves the current state value from the registry and the Changed event fires each time the state value in the registry changes.

The following example code demonstrates accessing the Sales Region state value and monitoring the value for changes.

public partial class FormMain : Form
{
  const string exampleAppSalesRegionKey = @"HKEY_CURRENT_USER\Software\State\Proseware\ExampleApp";
  const string exampleAppSalesRegionValue = "Sales Region";

  private void FormMain_Load(object sender, EventArgs e)
  {
    _salesRegionState = 
      new RegistryState(exampleAppSalesRegionKey, exampleAppSalesRegionValue);
    tbCurrentRegion.Text = (string)_salesRegionState.CurrentValue;
    _salesRegionState.Changed += new ChangeEventHandler(_salesRegionState_Changed);
  }
  RegistryState _salesRegionState; 
  // ...
  // ...
}

In the preceding code example, the RegistryState variable, _salesRegionState, is declared as a class-level variable. When the application form loads, the RegistryState instance is created and associated with the ExampleApp's Sales Region state value. The application retrieves the current value for the Sales Region state value and displays it in a textbox, tbCurrentRegion. Finally, the application registers to receive notifications anytime that the Sales Region state value changes. As you can see, other than the arguments passed to the constructor, working with the RegistryState class is just like working with the SystemState class.

In addition to providing support for reading the registry state value and monitoring the state value for changes, the RegistryState class also allows you to set the registry state value. You use the same RegistryState property to set the state value that you use when reading the state value, the CurrentValue property. The following example code demonstrates updating the ExampleApp's Sales Region state value.

private void SetSalesRegion(string newSalesRegion)
{
  _salesRegionState.CurrentValue = newSalesRegion;
}

As the preceding code example shows, updating the registry state value really is as simple as assigning the new value to the CurrentValue property.

Note

Both the RegistryState and SystemState classes inherit from a common base class, StateBase. The StateBase class is the class that actually defines the virtual property CurrentValue. The StateBase class defines the property to have both a getter and a setter, which is evident by the RegistryState class' CurrentValue property implementation. This implies that the SystemState class must also support setting the CurrentValue property; however, that's not quite true. The SystemState class does provide a CurrentValue property setter method but the SystemState class implements the setter to throw a NotSupportedException. This prevents applications from attempting to modify Windows Mobile system state values.

As you can see, the RegistryState class provides one-stop shopping for interacting with custom state values. It provides support for consumers of a state value by making the value available through the CurrentValue property and exposing change notifications through the Changed event. For publishers of a state value, the RegistryState class supports changing the state value by simply assigning the new value to the CurrentValue property.

Custom State and Notifications Broker Values in Managed Code – Improving Efficiency

Just as when passing the registry path as a string to the RegistryGetString, RegistrySetString, RegistryGetDWORD, and RegistrySetDWORD C functions, the .NET Compact Framework RegistryState class and SystemState class must also incur the overhead of traversing the registry path string, opening the registry key, accessing the registry value, and closing the registry key each time your application accesses a registry state value. However, unlike the C functions, the RegistryState class and SystemState class do not provide support for accessing the registry keys directly with an existing registry handle; these classes only support specifying the registry location using a registry path string.

If you are developing an application that frequently updates a system state value and you find that the overhead of the RegistryState class or SystemState class are negatively affecting your application performance, you can update the registry directly using the registry-related classes in the Microsoft.Win32 namespace. In this case, you simply open the key containing the value and then modify the registry state value just as you would normally modify any registry value. Even though you're using the regular registry-related classes rather than the RegistryState class, the State and Notifications Broker will still notify any applications that have subscribed to change notifications for the value.

The following code example demonstrates updating the registry value using the Microsoft.Win32 registry-related classes.

  const string exampleAppSalesRegionSubKey = @"Software\State\Proseware\ExampleApp";
  const string exampleAppSalesRegionValue = "Sales Region";

  RegistryKey _salesRegionSubKey = null;

  private void FormMain_Load(object sender, EventArgs e)
  {
    _salesRegionSubKey = 
      Registry.CurrentUser.CreateSubKey(exampleAppSalesRegionSubKey);
  }

  RegistryState _salesRegionState;
  private void SetSalesRegion(string newSalesRegion)
  {
    _salesRegionSubKey.SetValue(exampleAppSalesRegionValue, newSalesRegion);
  }

  private void FormMain_Closing(object sender, CancelEventArgs e)
  {
    if (_salesRegionSubKey != null)
      _salesRegionSubKey.Close();
  }
  // ...
  // ... Rest of application logic will call SetSalesRegion
  // ...  as required to update the state value during
  // ...  application processing
  // ...

In situations where your application frequently updates the Sales Region state value, the preceding code example provides significantly improved performance over using the RegistryState class. In the preceding implementation, your application opens the registry key when the form loads and keeps the registry key open until the form is closing. By keeping the registry key open, each time the application calls the SetSalesRegion method, the new value is directly stored into the registry without the additional overhead of reopening the registry key or traversing the registry hierarchy.

Threading and Notification Handling

In many cases, when applications are monitoring a state value, the code that executes on each state value change is relatively short and requires a few milliseconds or less to complete. For example, a simple state value change handler might set a Boolean flag or update the application display. There are, however, those cases where the state value change handler is more complex, requiring a more substantial period of time to complete. Handling such a request on the main application thread causes the user interface to momentarily freeze each time the event occurs. An application would also have similar issues if a monitored state value goes through periods of rapid change. In either case, the application user interface will appear sluggish or unresponsive because the state value change notifications are monopolizing the main application thread.

To avoid these and similar issues, you might consider architecting your application so that the code responsible for monitoring these state values executes on a thread other than the main application thread. Introducing this sort of multithreaded implementation can help your application provide a much more positive user experience because the main application thread remains available to respond to user events.

The implications of using the State and Notifications Broker API in a multithreaded application differ for native and managed developers. Native developers explicitly create the window that receives State and Notifications Broker notifications. With this being the case, native developers who want their application to handle state value change notifications on a thread separate from the main application thread can simply create a new thread and then have the new thread create the window that will handle the state value change notifications. This is no different than any other situation in which you want to handle a particular window's messages on a thread separate from the main application thread.

Note

If you're not familiar with the issues associated with creating windows in a multithreaded environment or if you would like a refresher on the subject, you can check out Using Messages and Message Queues and About Messages and Message Queues.

For managed developers, the details of window creation and message handling are abstracted by the RegistryState class; therefore, the RegistryState class needs to expose a facility for an application to request that the message handling occur on a separate thread. The RegistryState class exposes such a facility through the constructor's useFormThread parameter.

The RegistryState class exposes several constructors, two of which expose a Boolean parameter, useFormThread. When you call the RegistyrState constructor with the useFormThread parameter set to false, the RegistryState class creates a new thread that then creates the window and performs the window's message handling. For simplicity, we'll call this new thread the RegistryState thread. The newly created RegistryState thread handles all the state value change notification processing for that RegistryState class instance, which leaves the application's main thread free to handle user events. Keep in mind that the SystemState class uses the RegistryState class internally; therefore, the multithreading issues that apply to the RegistryState class apply equally to the SystemState class.

The following code example demonstrates using the SystemState class to monitor for changes in network connectivity using a thread separate from the main application thread; the code example displays the text "Connected" or "Not Connected" in a StatusBar each time the application gains or loses connectivity.

SystemState _connectionsCount = null;

private void FormMain_Load(object sender, EventArgs e)
{
  _connectionsCount = new SystemState(SystemProperty.ConnectionsCount, false);
  _connectionsCount.Changed += new ChangeEventHandler(ConnectionsCount_Changed);
}

void ConnectionsCount_Changed(object sender, ChangeEventArgs args)
{
  int numberOfConnections = (int)args.NewValue;
  string displayText = numberOfConnections > 0 ? "Connected" : "Not Connected";

  _statusBar.BeginInvoke((MethodInvoker) delegate { _statusBar.Text = displayText; });
} 

private void Form1_Closing(object sender, CancelEventArgs e)
{
if (_connectionsCount != null)
  _connectionsCount.Dispose();
}

#if PocketPC || Smartphone
delegate void MethodInvoker(); 
#endif

The code constructs the _connectionsCount class variable's instance to monitor for changes in the ConnectionsCount state value using a newly created thread rather than the main application thread. We'll again call this new thread the RegistryState thread. By monitoring for state value change notifications on the RegistryState thread, the corresponding Changed event handler, the ConnectionsCount_Changed method, executes on that same thread, not the main application thread. Because the ConnectionsCount_Changed method runs on the RegistryState thread instead of the main application thread, you must use caution when interacting with other parts of your application. Your application is now a multithreaded application and requires that you use good multithreaded programming practices. One important consideration is ensuring that the event handler method safely interacts with application controls.

As you may know, Windows Forms controls can only be accessed from the thread on which they are created. Interacting with the controls from any other thread requires that the application marshal the call to the correct thread using either the control's Invoke method or BeginInvoke method. As you can see in the preceding code example, rather than attempting to directly update the _statusBar.Text property, which would throw an exception due to the cross-thread access, the ConnectionsCount_Changed method calls the _statusBar.BeginInvoke method. As a result, the code that actually assigns the displayText variable to the _statusBar.Text property executes on the main application thread.

The syntax of the _statusBar.BeginInvoke method call may seem a little strange if you're not familiar with C# anonymous methods. In the case of the preceding code example, rather than explicitly define a one-line named method to set the _statusBar.Text property, I've defined an anonymous method within the BeginInvoke method call that sets the _statusBar.Text property. There are two key benefits to using an anonymous method in this case: first, the anonymous method requires less code than explicitly defining a named method; second, the anonymous method can access the ConnectionsCount_Changed method's displayText local variable, which eliminates the need to declare and pass a parameter containing the displayText variable's value.

Note

If you would like to know more about anonymous methods, there are a number of good resources available. The following are a couple of resources that I like. A good article to get started with is Create Elegant Code With Anonymous Methods, Iterators, And Partial Classes. Also, Ian Griffith has an excellent blog post discussing delegates, which includes some helpful information on anonymous methods.

You'll notice that the preceding code example calls the _connectionsCount.Dispose method when the form is closing. The Dispose method plays a very important role in the RegistryState class (and the SystemState class) because the Dispose method exits the RegistryState class' notification monitoring thread. If you forget to call the Dispose method on a RegistryState class instance that was created with a useFormThread value of false, your application will never fully exit because the application's process waits indefinitely for the RegistryState thread to exit. The problem is that the RegistryState thread never exits because the RegistryState thread remains running until the application calls the Dispose method.

Note

If you would like to know more about how threads affect the application exit process, see Foreground and Background Threads in the .NET Framework Developers Guide.

One of the most telltale signs that you may have forgotten to call the RegistryState.Dispose method is when you're debugging the application with Visual Studio 2005 and the application appears to exit on the device or emulator but Visual Studio 2005 remains in debug mode. Visual Studio 2005 remains in debug mode because the application is still running waiting for the RegistryState thread to exit. If this happens, simply terminate the application using the Visual Studio 2005 Stop Debugging feature (Debug | Stop Debugging on the Visual Studio 2005 menu), which forcibly terminates the RegistryState thread.

The final point regarding the preceding code example is the declaration of the MethodInvoker delegate at the end of the code example, the MethodInvoker delegate is a delegate defined in the full .NET Framework but not in the .NET Compact Framework. The MethodInvoker delegate is defined as a delegate that has an empty parameter list and a void return value; I find that the MethodInvoker delegate is often useful when using anonymous methods. I've placed the MethodInvoker delegate declaration within the #if preprocessor block so that my declaration is only used when targeting Windows Mobile. This same code compiled for the desktop uses the .NET Framework defined MethodInvoker delegate.

Threading and Notification Handling: Managing Long-Running Tasks

We've seen that moving the state value change notification processing off the main application thread prevents long-running change notification processing from interfering with the application user interface. The next issue that we need to look into is how long-running state value change notification handlers affect other notification handlers within the same application. By way of an example, the following code example monitors two state values: ConnectionsCount and DisplayRotation.

SystemState _connectionsCount = null;
SystemState _displayRotation = null;

private void FormMain_Load(object sender, EventArgs e)
{
  _connectionsCount = new SystemState(SystemProperty.ConnectionsCount, false);
  _connectionsCount.Changed += new ChangeEventHandler(ConnectionsCount_Changed);

  _displayRotation = new SystemState(SystemProperty.DisplayRotation, false);
  _displayRotation.Changed += new ChangeEventHandler(DisplayRotation_Changed);
}

void ConnectionsCount_Changed(object sender, ChangeEventArgs args)
{
  int numberOfConnections = (int)args.NewValue;
  if (numberOfConnections > 0)
    UploadDataToServer();
}

void DisplayRotation_Changed(object sender, ChangeEventArgs args)
{
  int displayOrientation = (int)args.NewValue;

  CalculateNewDisplayPositions(displayOrientation);
  this.BeginInvoke((MethodInvoker)UpdateApplicationDisplay);
}

private void FormMain_Closing(object sender, CancelEventArgs e)
{
  if (_connectionsCount != null)
      _connectionsCount.Dispose();
  if (_displayRotation != null)
      _displayRotation.Dispose();
}

When the ConnectionsCount state value changes to a value greater than zero, indicating that the device has connected to a network, the application uploads data from the local device to the server. When the DisplayRotation state value changes, indicating that the user has rotated the device display, the application recalculates the size and position of the user interface controls, and then updates the application display using the newly calculated user interface controls' sizes and positions.

Note

Be aware that in a real-life application, the simple fact that an application receives a notification that the device has connected to a network is not sufficient to begin the process of transferring data to a remote server. One also needs to verify that the connection makes the server of interest reachable. For an example of how to perform such a test, download the URLReachableDemo demo from this blog post.

You'll notice that each of the state values is monitored by a separate SystemState class instance with each instance constructed with the bUseFormThread parameter set to false. Just as you expect, with the bUseFormThread parameter set to false, each of the state values are monitored on a thread separate from the main application thread; however, what you may not expect is that the state values are not monitored on threads separate from one another. All RegistryState class instances created with a useFormThread parameter value of false use the same thread to monitor their respective state values.

Note

Just a reminder that the SystemState class internally uses the RegistryState class; therefore, from a threading perspective there is no difference from a SystemState class instance or a RegistryState class instance. The same thread is used for monitoring state values without regard for which of the classes you use in your application.

The decision of the RegistryState class implementers to use one thread to monitor all of the state values is a reasonable one. Each additional thread your application creates adds overhead and consumes resources. In most cases, a particular state values changes only occasionally, therefore, using a common thread to monitor the state values is a judicious use of resources. Your responsibility as an application developer is to implement your state value change handlers such that one handler does not interfere with another. The preceding code example demonstrates how one state value change event handler might delay the execution of another state value change handler.

The issue of concern in the preceding code example is that the ConnectionsCount state value change handler, the ConnectionsCount_Changed method, can potentially run for a long period of time. Depending on the amount of data that the application must upload, the UploadDataToServer method, and therefore the ConnectionsCount_Changed method, may run for several seconds, minutes, or even possibly hours. The notification handlers for all RegistryState class instances created with a useFormThread parameter value of false share a single thread; to avoid confusion, we'll continue to refer to this thread as the RegistryState thread. This thread, the RegistryState thread, cannot perform any other work until the ConnectionsCount_Changed method returns. As a result, if the user were to rotate the device display while the ConnectionsCount_Changed method is active, the application will not update the application display until the data upload completes because the DisplayRotation_Changed method is not able to receive the DisplayRotation state value change notification until the ConnectionsCount_Changed method returns and makes the RegistryState thread available to handle other notifications.

Resolving the problem of the two state value notification handlers competing for the same thread is easily resolved in the preceding code example. In this case, the DisplayRotation state value is being monitored with the wrong thread. The processing to handle a change in the DisplayRotation state value is tied directly to the user interface. With this being the case, the main application thread is the most reasonable choice to handle the DisplayRotation state value change notification. With the DisplayRotation state value change handling moved to the main application thread, the two state value change handlers, DisplayRotation_Changed and ConnectionsCount_Changed, no longer compete for the same thread and therefore do not interfere with one another.

Moving the DisplayRotation state value notification handler to the main application thread resolves the issue of the ConnectionsCount state value notification handler interfering with the DisplayRotation state value notification handler but the ConnectionsCount state value notification handler, ConnectionsCount_Changed, still monopolizes the RegistryState thread making the handling of any non-user interface oriented notifications difficult. For example, most applications that need to be notified when a connection becomes available also need to be notified when a connection is no longer available. Consider the following ConnectionsCount_Changed method implementation.

void ConnectionsCount_Changed(object sender, ChangeEventArgs args)

{

if (numberOfConnections > 0)

UploadDataToServer();

else

PauseDataUpload();

}

This ConnectionsCount_Changed method implementation uploads data from the device to the server when a connection becomes available and is supposed to pause the upload process when a connection is no longer available. Unfortunately, the application still has an issue with the ConnectionsCount_Changed event handler monopolizing the RegistryState notification thread when uploading the data. When a connection becomes available, the ConnectionsCount_Changed event handler executes and calls the UploadDataToServer method, which may still run for a long period of time. Until the UploadDataToServer method returns, which then allows the ConnectionsCount_Changed event handler to return, no other notifications on the RegistryState notification thread can execute; therefore, when the network connection disconnects, the system is unable to execute the ConnectionsCount_Changed event handler with the updated ConnectionsCount state value of 0; therefore, the PauseDataUpload method is never called. Instead, the UploadDataToServer method continues executing until it throws an exception because there is no valid connection available.

The fundamental problem in both this case and the earlier DisplayRotation state value case is that the ConnectionsCount state value handler is allowed to monopolize the RegistryState thread indefinitely. State value handlers should be designed to execute quickly and then return; otherwise, you'll always have the potential that your application will run into event handling problems due to the event handling thread being monopolized by one long-running state value notification handler.

Avoiding long-running event handlers doesn't mean that your application is limited to performing only simple tasks when a state value changes. The key is to separate the event notification handling from the actual task processing that the event controls. In cases where your application needs to perform some potentially long-running tasks based on state values, you should think of your state value notification handler as the manager of the task rather than the worker. The worker should instead execute on a thread other than the RegistryState notification handler thread.

There is no one right way to manage threads from the state value event handlers. You can start a new thread to execute the required task using the Thread class. Alternatively, you can use the ThreadPool class to queue the task to execute on one of the Common Language Runtime (CLR) managed threads. Finally, you can use synchronization objects such as the AutoResetEvent or ManualResetEvent classes to start and stop the task-processing running on a separate thread.

Note

There are many caveats and situation-specific considerations when designing a multithreaded application. If you're new to working with threads, you should try to work with someone who has experience in multithreaded programming if you can; you'll also want to spend some time reading about the tools and techniques used in multithreaded programming. Here are a couple of resources that I think are a good for getting started: Developing Multithreaded Applications for the .NET Compact Framework and Multithreaded Programming with Visual Basic .NET. The Patterns and Practices group also provides a fairly comprehensive guide on .NET application scalability and multithreading: Improving .NET Application Performance and Scalability. It's a good resource but may be a bit much for someone who is just getting started in multithreaded programming.

The following code example demonstrates the technique of starting a new thread to perform the actual task.

Thread _uploadDataToServerThread;

void ConnectionsCount_Changed(object sender, ChangeEventArgs args)
{
  
  int numberOfConnections = (int)args.NewValue;

  if (numberOfConnections > 0)
  {
    ThreadStart threadStartMethod = new ThreadStart(UploadDataToServer);
    _uploadDataToServerThread = new Thread(threadStartMethod) ;
    _uploadDataToServerThread.Start();
  }
  else
    PauseDataUpload();
}

The new implementation of the ConnectionsCount state value change handler, ConnectionsCount_Changed, now starts a new thread to perform the actual data upload and returns immediately. In this implementation, the ConnectionsCount_Changed method executes and returns quickly, which makes the RegistryState thread available to process other state value change notifications almost immediately. Notice that the PauseDataUpload method is still run directly by the handler. This is because pausing the upload process generally involves little more than setting a flag or otherwise signaling that the upload process should stop; therefore, the PauseDataUpload method is not likely to monopolize the RegistryState thread.

Persistent Notifications – Native and Managed Application Compatibility

Back in Part 2, we introduced persistent notifications. As you'll recall, persistent notifications allow you to provide notification handling that outlives a given application. Persistent notifications provide the important feature of automatically starting the associated application if necessary; persistent notifications also survive dead batteries and soft resets of the device.

When registering a persistent notification, an application can register itself as the target of a persistent notification or the application can register a different application as the notification target. In Part 2, we covered how a native application can use the RegisterNotificationApp function to register another native application as the notification target; similarly, we covered how a managed application can use the EnableApplicationLauncher method to register another managed application as the notification target. The issue that we put off until this month is how to register persistent notifications that cross the native/managed boundary: native applications that register a managed application or vice-versa.

A complete system often involves multiple applications and it is not uncommon for some of the applications to be native applications written in C/C++ and other applications in the system to be managed applications written in C# or VB.NET. With this being the case, it is reasonable to expect that there may be a need for a native application to register a persistent notification that starts a managed application; the opposite is also likely. The persistent notification infrastructure has no problem handling these two scenarios but there are some special issues that we need to address to make everything work.

Registering persistent notifications between native and managed applications does not involve the traditional concept of native/managed interoperability because the actual persistent notification registration occurs in the registry. In this case, interoperability simply means that a native application creates a persistent notification registry entry that is in the format required by a managed application; the same is true for a managed application registering a persistent notification for a native application.

Note

You can find the list of persistent notification registry entries in the Persistent Notifications section of Part 2.

For the most part, the persistent notification registry fields contain the same values for both managed and native applications. The fields that differ are the Class Name, Window Name, and Message fields; this is because of the difference in the notification handling implementation between the two environments. Native developers have direct control over the window and message id associated with a persistent notification; therefore, the registry Class Name, Window Name, and Message fields will contain the values defined by the native application.

Managed developers do not control these fields; the implementation of the RegistryState class abstracts the window and message details from the application developer. A persistent notification registered for a managed application must specify the Class Name, Window Name, and Message fields that the RegistryState class expects. Fortunately, these values are very predictable.

Two of the fields, Class Name and Message, are always the same. The RegistryState class processes State and Notifications Broker notification messages with a class derived from the .NET Compact Framework MessageWindow class. The .NET Compact Framework MessageWindow class has a Windows class name of #NETCF_AGL_MSG_; therefore, #NETCF_AGL_MSG_is the value that you specify in the registry Class Name field. Internally the RegistryState's MessageWindow-derived class expects State and Notifications Broker notifications to have a message id of 0x8001, which is 32769 decimal; therefore, 32769 is the value you specify for the registry Message field. Although not always the same, the last registry value, Window Name, is easily determined. The RegistryState class always names the notification-processing window as the concatenated value of the string "Notifications_" followed by the application id. Table 1 summarizes the Class Name, Window Name, and Message registry fields for managed applications.

Note

If you're wondering why the RegistryState class implementers chose 0x8001 as the message id, the reason is that 0x8001 is the message id one gets when adding 1 to the C/C++ WM_APP constant. In C/C++, application-specific message values are always defined as starting at the WM_APP constant.

Table 1. Registry field values for persistent notifications targeting managed applications

Registry Field Value

Class Name

#NETCF_AGL_MSG_

Window Name

Notifications_applicationidentifier

Message

32769

Now that we understand how the RegistryState class implements notification message processing, we can easily write a native application to register a persistent notification that targets a managed application as shown in the following code example.

const TCHAR* appId = _T("CompanyA.MyManagedApp.MediaPlayerTrackTitle");
const TCHAR* targetApplication = _T("\\Program Files\\MyManagedApp\\MyManagedApp.exe");
const TCHAR* messageWindowClassName = _T("#NETCF_AGL_MSG_");
const UINT notificationMessage = WM_APP + 1;

HRESULT CreatePersistentNotificationForMgdApp()
{
  TCHAR messageWindowWindowName[256];
  _stprintf(messageWindowWindowName, _T("Notifications_%s"), appId);

  HRESULT hResult = RegistryNotifyApp(SN_MEDIAPLAYERTRACKTITLE_ROOT, 
    SN_MEDIAPLAYERTRACKTITLE_PATH, SN_MEDIAPLAYERTRACKTITLE_VALUE, 
    appId, targetApplication, messageWindowClassName, messageWindowWindowName, 
notificationMessage, RNAF_NONAMEONCMDLINE, NULL); 

  return hResult;
}

As shown in the preceding code, the native application still creates the persistent notification using the RegistryNotifyApp function and uses the constants from snapi.h to associate the persistent notification with the Microsoft Windows Media® Player track title. Specifying the application id and application executable path is also the same as when targeting a native application. The only differences from targeting a native application are the three values discussed earlier. The managed application's windows class name and message id are declared as the constants messageWindowsClassName and notificationMessage respectively. To create the window's window name, the call to _stprintf concatenates the string "Notifications_" with the appId constant. That's all there is to it; the next time the Windows Media Player track title changes, the State and Notifications Broker will launch the MyMannagedApp application and send it the appropriate message for the SystemState class or RegistryState class to process the notification. When the track title changes again, the State and Notifications Broker will send the notification directly to the application just as it would had another managed application registered the persistent notification.

Because managed applications can import native functions, writing a managed application that creates a persistent notification targeting a native application is even easier. Simply DllImport the native RegistryNotifyApp function and call the function passing the same parameters that a native application does.

One Last Thing: Bitmasks and Managed Applications

As you've seen throughout our discussion of the State and Notifications Broker, a single registry value may contain the bits that make up a number of different state values; therefore, extracting a state value from the registry may require a number of steps. You'll recall from Part 1 that the SystemState class' static properties completely abstract these operations from the application developer. A great example is the SystemState.PowerBatteryStrength property, which returns a BatteryLevel enumeration value indicating whether the battery strength is VeryHigh, High, Medium, Low, or VeryLow. Determining the PowerBatteryStrength state value requires you to read the registry value, apply a bit-shift and a bitwise-and to the registry value that produces the integer representation of the PowerBatteryStrength state value; the integer representation must then be cast to a BatteryLevel enumeration.

The SystemState static properties do such a great job of making the state values easily accessible, a managed developer may find himself lured into falsely believing that one does not need to understand the registry representation of the state values. Nothing could be further from the truth.

Remember that in addition to the SystemState class' static properties; you also create SystemState class instances using the SystemProperty enumeration. The combination of the static properties and the class instance provide three ways that your application might access a state value: the SystemState static property, the SystemState.CurrentValue instance property, and the ChangeEventArgs parameter in the Changed event notification handler. Because the SystemState static property, the SystemState.CurrentValue instance property, and the ChangedEventArgs.NewValue property all represent the same state value, one might expect each to return the same result; however, this is not the case because they each handle the issues of bitmasks and type conversion differently.

Consider the following code example.

SystemState _batteryStrength;

public FormMain()
{
  InitializeComponent();

  _batteryStrength = new SystemState(SystemProperty.PowerBatteryStrength);
  _batteryStrength.Changed += new ChangeEventHandler(PowerBatteryStrength_Changed);
}

void PowerBatteryStrength_Changed(object sender, ChangeEventArgs args)
{
  Debug.WriteLine("SystemState.PowerBatteryStrength = " + SystemState.PowerBatteryStrength);
  Debug.WriteLine("PowerBatteryStrength args.NewValue = " + args.NewValue);
  Debug.WriteLine("_batteryStrength.CurrentValue = " + _batteryStrength.CurrentValue);
}

The preceding code creates an instance of the SystemState class, _batteryStrength that is associated with the PowerBatteryStrength state value; the application also handles the _batteryStrength.Changed event. In the Changed event handler, the application displays to the output window the SystemState.PowerBatteryStrength static property, the _batteryStrength.Changed event handler's ChangedEventArgs.NewValue property, and the _batteryStrength.CurrentValue instance property. Figure 3 shows the three values displayed from the PowerBatteryStrength_Changed Changed event handler.

Figure 3. The different values returned for the PowerBatteryStrength state value

As you can see, the values in each case are different. The static property has a value of Medium, which is the appropriate BatteryLevel enumeration value. The ChangedEventArgs.NewValue property has a value of 41; the value 41 is the integer equivalent of the BatteryLevel.Medium enumeration value. The CurrentValue property on the SystemState instance has a value of 2686980. The value 2686890 is not actually the PowerBatteryLevel value but rather the registry value that contains the bits that make up the PowerBatteryLevel value.

The reason for the differences in each property value is that each property performs a different level of conversion on the registry contents. The static property performs the most complete conversion: applying the bit-level work and converting the value to the appropriate data type. The ChangedEventArgs.NewValue property applies the bit-level work but does not perform the data type conversion, which means that the contents of the ChangedEventArgs.NewValue property are always the same data type as the registry value that contains the state value. The CurrentValue instance property performs no conversion on the registry contents; the CurrentValue property exposes the raw registry contents. Your application must perform any bit-oriented work and data type conversions required to extract the state value from the CurrentValue property.

Even with these value discrepancies, the SystemState class is still very useful at making state values easily accessible. As you work with the SystemState class, just be sure that you understand the relationship between the state value and the corresponding registry contents so that you know what to expect from each of the three SystemState-related properties. State values that are stored in the registry as strings or simple integers can be safely accessed through any of the three properties. You just need to use caution in those cases where a single registry value contains the bits that make up multiple state values.

Bitmasks also require caution when working with the RegistryState class. The presence of the Bitmask property on the RegistryState class might lead one to believe that the RegistryState.CurrentValue property and the corresponding RegistryState.Changed event's ChangedEventArgs.NewValue property take care of applying the bitmask to the registry value. This, however, is not the case; the RegistryState class doesn't perform any bitmask handling.

You can assign a value to the BitMask property and retrieve the bitmask value back from the property but the RegistryState class never uses the bitmask; the RegistryState class always works against the raw registry value. The primary benefit of the RegistryState.BitMask property is that it allows your application to associate a bitmask with a particular RegistryState instance so that your application doesn't need to track the bitmask separately. In other words, the RegistryState class will store a bitmask value but your application must explicitly apply the bitmask to the value returned by the RegistryState.CurrentValue property or the ChangedEventArgs.NewValue property.

My goal in pointing out these bitmask-related issues with the SystemState and RegistryState classes is not to provide a negative finish to our series. On the contrary, the SystemState and RegistryState classes are incredibly powerful and useful. That doesn't mean that they are perfect. My hope is that the more you understand about the State and Notifications Broker and the more you know about what it can and cannot do, the more you'll take advantage of it and the more effectively you'll apply it in your applications.

Conclusion

The State and Notifications Broker continues to impress me as a well-designed and implemented feature of the Windows Mobile platform. The addition of the over 40 new state values by Windows Mobile 6 reinforces my belief that the State and Notifications Broker API is one of the most powerful tools we have access to on the Windows Mobile platform and is equally valuable to both native and managed developers. I've really enjoyed digging into the many capabilities of the State and Notifications Broker; I hope you've enjoyed reading about it.

This completes our three-part series on the State and Notifications Broker but that doesn't mean that there isn't a lot more great stuff to talk about. Join me again next month as we dig into some of the new capabilities provided by Windows Mobile 6.

You can always find the list of column topics under the You Can Take It with You node in the MSDN® library. If you have any questions about what we’ve covered, or if you have ideas for things that you would like to see me cover, please contact me through my blog.

See Also

What's New for Developers in Windows Mobile 6

What’s New for Developers in Windows Mobile 5.0

Using Messages and Message Queues

About Messages and Message Queues

Create Elegant Code With Anonymous Methods, Iterators, And Partial Classes

Foreground and Background Threads

C# v2.0 Delegate Syntax Blog Post

Author Bio

Jim Wilson is president of JW Hedgehog, Inc. (https://www.jwhh.com) a New Hampshire–based consulting firm specializing in solutions, content creation, and mentoring for the Windows Mobile platform. Jim has worked extensively with the .NET Framework and .NET Compact Framework since the original beta release of each; he has over 20 years experience in the software industry including more than 14 years experience with relational database programming including SQL Server™ and SQL Server Compact Edition. Jim writes frequently for MSDN and has developed mobility curriculums for two of the industry’s leading technology training organizations, DevelopMentor and PluralSight. Jim speaks regularly at Tech Ed, the Professional Developer's Conference (PDC), VSLive, and the Mobility & Embedded DevCon. You'll find Jim online at https://pluralsight.com/blogs/jimw.

Appendix A

This appendix provides the list of new State and Notifications Broker state values introduced with Windows Mobile 6. Each state value lists the managed SystemState property and the C/C++ macro definitions from the snapi.h header file.

You'll recall from Part 1 that the snapi.h header file defines several macros for each state value with each macro relating to a particular state value having the same prefix: prefix_ROOT, prefix_KEY, prefix_VALUE, and prefix_BITMASK. This remains true for most of the macros but not all. In some cases where a single registry value represents multiple state values with each state value identified by a different bitmask, several bitmask macros share common root, key, and value macros.

In an attempt to make reading through the new state values a little easier, I've grouped the state values into separate tables based on the way the macros are defined. State values that follow the standard naming of using a common prefix for the root, key, value, and bitmask macros appear in the first table. For the remainder of the macros, each table lists the bitmask macros that all share common root, key, and value macros. I know it seems arbitrary but it's the easiest way I've found to display the values in a reasonably organized list.

Table 2 shows most of the new state values. The C/C++ macros for these values follow the standard naming practice of the root, key, value, and bitmask macros sharing a common prefix.

Table 2. Main list of New Windows Mobile 6 State and Notifications Broker state values

SystemState Property C/C++ Macro Prefix Description

BluetoothStateA2DPConnected

SN_BLUETOOTHSTATEA2DPCONNECTED

Gets a value indicating whether Bluetooth Advanced Audio Distribution Profile is connected

BluetoothStateDiscoverable

SN_BLUETOOTHSTATEDISCOVERABLE

Gets a value indicating whether Bluetooth is discoverable

BluetoothStateHandsFreeAudio

SN_BLUETOOTHSTATEHANDSFREEAUDIO_

Gets a value indicating whether the device is under Bluetooth hands-free audio and control

BluetoothStateHandsFreeControl

SN_BLUETOOTHSTATEHANDSFERECONTROL

Gets a value indicating whether device is under Bluetooth hands-free control - The macros for this value are spelled as shown here, the E and the R in the word FREE are transposed in the macros just as shown here

BluetoothStateHardwarePresent

SN_BLUETOOTHSTATEHARDWAREPRESENT

Gets a value indicating whether Bluetooth hardware is present

BluetoothStatePowerOn

SN_BLUETOOTHSTATEPOWERON

Gets a value indicating whether Bluetooth is powered on

CameraEnabled

SN_CAMERAENABLED

Gets a value indicating whether a camera is enabled

ClamshellClosed

SN_CLAMSHELLCLOSED

Gets a value indicating whether the clamshell is closed

PhoneTalkingCallStartTime

SN_PHONETALKINGCALLSTARTTIME

Gets the time of the current active call in FILETIME format

WiFiStateConnected

SN_WIFISTATECONNECTED

Gets a value indicating whether Wi-Fi is connected to a network

WiFiStateConnecting

SN_WIFISTATECONNECTING

Gets a value indicating whether Wi-Fi is connecting to a network

WiFiStateHardwarePresent

SN_WIFISTATEHARDWAREPRESENT

Gets a value indicating whether Wi-Fi hardware is present

WiFiStateNetworksAvailable

SN_WIFISTATENETWORKSAVAILABLE

Gets a value indicating whether Wi-Fi networks are available

WiFiStatePowerOn

SN_WIFISTATEPOWERON

Gets a value indicating whether Wi-Fi is powered on

Table 3 shows the new state values related to cellular system availability. All of the state values in this table are contained in the Cellular System Available registry value under the registry key HKLM\System\State\Phone. All of the bitmask macros are used in conjunction with the SN_CELLSYSTEMAVAILABLE_ROOT, SN_CELLSYSTEMAVAILABLE_PATH, and SN_CELLSYSTEMAVAILABLE_VALUE macros.

Table 3. New Windows Mobile 6 Cell System Availability Macros

SystemState Property C/C++ Bitmask Macro Name Cellular System

CellularSystemAvailable1xrtt

SN_CELLSYSTEMAVAILABLE_1XRTT_BITMASK

1XRTT

CellularSystemAvailableEdge

SN_CELLSYSTEMAVAILABLE_EDGE_BITMASK

EDGE

CellularSystemAvailableEvdo

SN_CELLSYSTEMAVAILABLE_1XEVDO_BITMASK

1XEVDO

CellularSystemAvailableEvdv

SN_CELLSYSTEMAVAILABLE_EVDV_BITMASK

EVDV

CellularSystemAvailableGprs

SN_CELLSYSTEMAVAILABLE_GPRS_BITMASK

GPRS

CellularSystemAvailableHsdpa

SN_CELLSYSTEMAVAILABLE_HSDPA_BITMASK

HSDPA

CellularSystemAvailableUmts

SN_CELLSYSTEMAVAILABLE_UMTS_BITMASK

UMTS

Table 4 shows the new state values related to cellular system connectivity. All of the state values in this table are contained in the Cellular System Connected registry value under the registry key HKLM\System\State\Phone. All of the bitmask macros are used in conjunction with the SN_CELLSYSTEMCONNECTED_ROOT, SN_CELLSYSTEMCONNECTED_PATH, and SN_CELLSYSTEMCONNECTED_VALUE macros.

Table 4. New Windows Mobile 6 Cell System Connectivity Macros

SystemState Property C/C++ Bitmask Macro Name Cellular System

CellularSystemConnected1xrtt

SN_CELLSYSTEMCONNECTED_1XRTT_BITMASK

1XRTT

CellularSystemConnectedCsd

SN_CELLSYSTEMCONNECTED_CSD_BITMASK

CSD

CellularSystemConnectedEdge

SN_CELLSYSTEMCONNECTED_EDGE_BITMASK

EDGE

CellularSystemConnectedEvdo

SN_CELLSYSTEMCONNECTED_1XEVDO_BITMASK

1XEVDO

CellularSystemConnectedEvdv

SN_CELLSYSTEMCONNECTED_EVDV_BITMASK

EVDV

CellularSystemConnectedGprs

SN_CELLSYSTEMCONNECTED_GPRS_BITMASK

GPRS

CellularSystemConnectedHsdpa

SN_CELLSYSTEMCONNECTED_HSDPA_BITMASK

HSDPA

CellularSystemConnectedUmts

SN_CELLSYSTEMCONNECTED_UMTS_BITMASK

UMTS

Table 5 shows the new state values related to device lock states. All of the state values in this table are contained in the Lock registry value under the registry key HKLM\System\State. All of the bitmask macros are used in conjunction with the SN_LOCK_ROOT, SN_LOCK_PATH, and SN_LOCK_VALUE macros. In addition to exposing properties for each of the individual lock-related state values, the SystemState class also exposes the LockStates property, which returns a LockStates enumeration value for the various device lock states.

Table 5. New Windows Mobile 6 Device Lock States Macros

SystemState Property C/C++ Bitmask Macro Name Description

DeviceLocked

SN_LOCK_BITMASK_DEVICELOCKED

Gets a value indicating whether the device is locked

KeyLocked

SN_LOCK_BITMASK_KEYLOCKED

Gets a value indicating whether the keys are locked

LockStates

Use DeviceLocked, KeyLocked, and SimLocked macros

Returns a managed enumeration identifying the Device, Key, and SIM locked states

SimLocked

SN_LOCK_BITMASK_SIMLOCKED

Gets a value indicating whether the SIM is locked

Table 6 shows the new state values related to Internet Sharing. All of the state values in this table are contained in the InternetSharing registry value under the registry key HKLM\System\State\Connectivity. All of the bitmask macros are used in conjunction with the SN_INTERNETSHAREING_ROOT, SN_ INTERNETSHAREING _PATH, and SN_ INTERNETSHAREING _VALUE macros.

Managed developers use the RegistryState class to access the registry value for the Internet Sharing state values because the SystemState class does not expose properties for the Internet Sharing state values. The following code example shows the appropriate RegistryState class constructor call.

RegistryState _internetSharingState;
_internetSharingState = new RegistryState(@"HKEY_LOCAL_MACHINE\System\State\Connectivity", "Value");

To access the individual Internet Sharing state values, convert the RegistryState.CurrentValue property from an object to a uint, and then perform a bitwise-and against the bitmask that corresponds to the state value of interest. The bitmask for each state value is listed in the first column of the table. The following code example demonstrates how to test if Internet Sharing is running.

uint internetSharingRegistryValue = (uint) _internetSharingState.CurrentValue;

bool isRunning = (internetSharingRegistryValue & 0x00000001) > 0;

Note

Unlike the other bitmask macros, the bitmask macros for Internet Sharing state values do not contain the word BITMASK in the macro name.

Table 6 New Windows Mobile 6 Internet Sharing State Macros

Bitmask Value C/C++ Bitmask Macro Name Description

0x00000001

SN_INTERNETSHARING_PROCESS_RUNNING

InternetSharing has successfully been loaded and is ready for use

0x00000002

SN_INTERNETSHARING_ENABLED

InternetSharing data session is currently enabled—this means InternetSharing is either connecting or connected

0x00000004

SN_INTERNETSHARING_DATA_CONNECTED

InternetSharing has a valid cellular data connection

0x00000008

SN_INTERNETSHARING_HOST_CONNECTED

InternetSharing has a valid connection with PC

0x00000010

SN_INTERNETSHARING_HOST_USB

Connection with PC is over USB

0x00000020

SN_INTERNETSHARING_HOST_BLUETOOTH

Connection with PC is over Bluetooth