From the August 2002 issue of MSDN Magazine

MSDN Magazine


System Event Notification Services and WMI Enable Flexible, Efficient Mobile Network Computing

Aspi Havewala
This article assumes you're familiar with WMI, ATL, and C++
Level of Difficulty     1   2   3 
Download the code for this article: SENS.exe (107 KB)
SUMMARY Networked applications must deal with a host of connection problems ranging from timeouts to congestion to unavailability. If these applications can check the current connection status and, when disconnected, cache transmissions, they become more efficient. Fortunately, both the System Event Notification System (SENS) and Windows Management Instrumentation (WMI) can send notifications to keep applications informed of network status.
      In this article, the author explains the use of several SENS interfaces, including ISensNetwork and ISensLogon, which trigger events for connects/disconnects and logons/logoffs, respectively. The author then shows how you can subscribe to each of these events, and follows with a discussion of when you might use WMI events instead.

Mobile computing is becoming more and more popular; at the same time, practically all useful information flows into a computer via a network. Users can connect portable computers to low-bandwidth, high-availability wireless networks such as CDPD and Motorola's RD-LAP, or to higher-bandwidth, lower-availability networks such as wired docking stations in the office or wireless LANs. Mobile computers cannot be connected to the network at all times because of such limiting factors as cost, lack of availability, and location. Thus, applications for mobile PCs must work well in transient networks. In this article, I'll discuss two Microsoft technologies, the System Event Notification System (SENS) and Windows® Management Instrumentation (WMI), that ensure that applications work well in mobile scenarios.
      Available for download with this article is a sample that subscribes to SENS notifications and also registers for and handles WMI events (see the link at the top of this article). It displays each notification as it occurs with a time stamp in a listbox in its only window. For notifications that require you to log off, you may also run the sample as a service. When run as a service, the sample logs all notifications into a file called Sensometer.log.

Transient Network Applications

      The speed and reliability of transient networks determine the perceived usability and responsiveness of an application on a mobile device. Let's look at several design considerations for applications that will be used in transient network environments.
      First, you should expect network connections to come and go. This is important to note for programs that open a network connection to send and receive data. Problems sending and receiving data could be caused by network congestion or unavailability, problems with the user's network card, or the unit going out of range. Traditional applications are often not robust enough to survive a disconnection, application shutdown, or an error message that requires a user response.
      You could assume that a network connection was lost because the user went out of range if the application timed out when sending or receiving data, but you would really only be guessing. Waiting for a timeout often means that you have to let the entire timeout value expire before making a decision. Frequently, ongoing network congestion forces you to require rather long timeouts since you don't want to give up on a connection too soon. In return, this means that the application has to wait for a while before concluding that the connection was lost.
      More often than not, each remote location is likely to be on a separate subnet. Mobile users need to travel through these subnets while maintaining a persistent connection to the network. Virtual private networking (VPN) is a popular way to tunnel across subnets; mobile IP is a less widespread option. Growing in popularity is a roaming-across-routers capability offered by some wireless LAN (WLAN) vendors like Proxim ( and Symbol (
      If roaming across subnets is not transparently supported by the network protocol, the application may find that the device is in range and the network is available, but its IP address may not be valid on the new subnet. In other words, you may be connected at the radio (link) layer, but not at the TCP/IP (network) layer. Newer operating systems like Windows 2000 and Windows XP are helpful because they can sense the presence or absence of network media or a connection. This capability can be used to renew an IP address using Domain Host Configuration Protocol (DHCP).
      Another design consideration in transient environments is the power state of the unit. WLAN PC cards and modems suck up enormous amounts of power and mobile users must make battery conservation a priority. Applications can mitigate this problem by batching network activity whenever possible so the unit can enter a low power consumption state when not in use.
      Transient network-aware applications also need to know when the user is interacting with their computer. If your app is gathering data while the user is interacting with the computer, the system will appear sluggish.
      To avoid this you could perform your background processing only if you are sure the computer is idle. As soon as the user starts interacting with the device, stop background processing. This will require some way to quickly start and stop background processing, as well as a way to save the ongoing state for later processing.


      As you can guess from its name, SENS is an event publisher for various system events. SENS is available only with Windows 2000 and Windows XP (with one exception that I'll note later). SENS uses COM+ event notifications to post its events. Based on a loosely coupled publisher-subscriber model, COM+ events are a refinement of the old connection point technique prevalent in COM.
      Three major interfaces handle notifications fired by SENS: ISensNetwork takes care of network connect/disconnect notifications; ISensLogon manages interactive user notifications; and finally, ISensOnNow handles power state notifications.
      ISensNetwork throws events when the network is connected or disconnected and when a destination becomes reachable. These events are delivered to a subscriber through the ISensNetwork interface. Unlike the other components of SENS, ISensNetwork notifications are available on any 32-bit Windows platform provided you install Microsoft® Internet Explorer 5.5 or later.
      Along with each event thrown, ISensNetwork can provide quality of connection (QOC) information if available. QOC information includes the speed of the network link. Because mobile computers might be connected to different networks at any given time, resulting in links of varying speeds, the link speed information can be used to best decide how to transfer data.
      For example, if a low-speed connection is available, the application may decide to compress all the information before sending it. In this scenario, the time spent compressing and decompressing the information would be better than choking up a low-bandwidth network. On the other hand, a high-speed connection may warrant sending uncompressed information.
      Take a closer look at the ISensNetwork interface:

  HRESULT ISensNetwork::ConnectionMade (BSTR bstrConnection,

This method is called when a network connection is made. The first argument names the connection, for example "LAN Connection." The second argument will return either 0 (LAN) or 1 (WAN). QOC data is passed in as a pointer to the third and final argument. The SENS_QOCINFO structure looks like this:

  typedef struct _SENS_QOCINFO
DWORD dwSize;
DWORD dwFlags;
DWORD dwOutSpeed;
DWORD dwInSpeed;

The dwFlags member is supposed to return a qualitative description of the speed of the network, namely: high, medium, or low. Just how the information is returned is undocumented. The members, dwOutSpeed and dwInSpeed, return the outgoing and incoming bandwidth in bytes per second. When no QOC information is available, this member of ISensNetwork is called:

  HRESULT ConnectionMadeNoQCInfo
(BSTR bstrConnection, ULONG ulType);

      After experimenting with ISensNetwork, I found that the interface works in a way that renders it almost unusable with wireless networks (a more detailed description will follow). Fortunately, there is an excellent alternative available, which I'll also discuss.


      ISensLogon provides an overview of how the user is interacting with the system. ISensLogon informs a subscriber when a user logs on or off, when the screen is locked or unlocked, when the screen saver starts or stops, or when the shell starts.
      All the ISensLogon methods supply only one argument to the subscriber, the login name of the user. Here is a list of the numerous methods of ISensLogon:

  HRESULT ISensLogon::Logon (BSTR bstrUserName);
HRESULT ISensLogon::Logoff (BSTR bstrUserName);
HRESULT ISensLogon::DisplayLock (BSTR bstrUserName);
HRESULT ISensLogon::DisplayUnLock (BSTR bstrUserName);
HRESULT ISensLogon::StartScreenSaver (BSTR bstrUserName);
HRESULT ISensLogon::StopScreenSaver (BSTR bstrUserName);
HRESULT ISensLogon::StartShell (BSTR bstrUserName);

Each of the notifications listed above is sent after the event that triggers it has occurred. For example, the StartScreenSaver method is called after the screen saver has been started by the system. If you want to trap the event before it occurs, this notification will not help at all. This applies to all the ISensLogon notifications.
      The DisplayLock and DisplayUnLock events are sent whenever the screen display is locked by the user. A user typically secures the display by pressing the Ctrl-Alt-Del buttons and then clicking the Lock Computer button. The display is then unlocked by logging in again with a user name and password.


      ISensOnNow provides three notifications related to power states: when the computer is switched to AC or battery power, or when the battery power is low. The notifications are delivered via the ISensOnNow interface.

  HRESULT OnACPower(void);

This method is called whenever the computer switches to AC power from battery power. If your laptop is on a network, it is most likely using a PCMCIA card modem or wireless NIC card. A typical WLAN PCMCIA card tends to use 350mA when transmitting and 250mA when receiving radio signals. A good approach is to avoid using the WLAN card as much as possible when on battery power, in an effort to reduce power consumption.
      An application can use this information and decide to cache data to be sent over a network locally while the computer is on battery power. When the computer switches to AC power, the application can then send or receive the data.
      There are no guarantees that the user will switch to AC power. And when she does, there is no telling if the computer will still be in range of a wireless network. Coding such optimizations requires intimate knowledge of how customers move within a mobile network and how they work with their data.
      When a user switches to battery, this method is called with the percent of battery life remaining in the computer's active battery:

  HRESULT OnBatteryPower(DWORD dwBatteryLifePercent);

The BatteryLow method is called periodically after the user has switched from AC to battery power:

  HRESULT BatteryLow (DWORD dwBatteryLifePercent);

However, I was never able to get this notification from SensOnNow, even when my battery drained out.

Receiving Notifications

      Now that I've discussed the SENS notifications and how they can be used to optimize mobile applications, I'll explain how to set up a subscriber to receive these events.
      COM+ subscriptions can be persistent or transient. Persistent subscriptions are maintained across reboots, and transient subscriptions are not. I won't dwell on the specific aspects of the COM+ publisher subscriber model. Instead, I'll refer you to the article "The COM+ Event Service Eases the Pain of Publishing and Subscribing to Data" by David Platt (MSJ, September 1999).
      In order to subscribe to the SENS notifications, I'll set up a transient subscription and walk you through the steps for creating an object for the ISensLogon interface. I'll use ATL, which allows me to get as close to COM as a framework will allow.
      The first step is to create a simple ATL executable application. Select the ATL Com AppWizard in the New Projects list and use the Project Wizard in Visual Studio® (see Figure 1).

Figure 1 ATL COM AppWizard
Figure 1 ATL COM AppWizard

      Next, insert a simple ATL object in the project. I called mine SensLogonSpy (see Figure 2). This process creates a class called CSensLogonSpy in my project.

Figure 2 SensLogonSpy
Figure 2 SensLogonSpy

      From the class viewer in Visual Studio, right-click on CSensLogonSpy and select the Implement Interface menu option. You'll see a warning dialog stating that Visual Studio was unable to find a type library for this project. Click OK to dismiss the dialog and move on.

Figure 3 Browsing Type Libraries
Figure 3 Browsing Type Libraries

      Next, you'll see a dialog that will allow you to browse available type libraries (see Figure 3). If you compile the project before you add the interface to the class, you will see an Implement Interface dialog box with nothing listed in the SENSOMETERLib tab (see Figure 4). Click on the Add Typelib button to the right, and the Browse Type Libraries dialog will be displayed. Scroll down until you find SENS Events Type Library (1.0). Notice that the dialog box tells you that the type library is implemented by C:\Winnt\System\Sens.dll. Check the box next to SENS Events Type Library and click OK.


      The SensEvents tab will now get added in the Implement Interface dialog. The three interfaces discussed previously will all be listed. Select the checkbox next to the ISensLogon interface and click OK. This will insert method implementations for the ISensLogon interface in CSensLogonSpy. Each method is an empty placeholder and returns E_NOTIMPL. You will need to fill in each method depending on how you want to handle each event.
      To attach the class to the interface, you will have to add the two lines indicated here to the IDL file:

  library SENSOMETERLib
import "SensEvts.idl"; //add this line
coclass SensLogonSpy
[default] interface ISensLogonSpy;
interface ISensLogon; //add this line

The ISensLogon interface must be added to the coclass, indicating that the CSensLogonSpy class is implementing the ISensLogon interface. To import the definition for ISensLogon (and any of the SENS interfaces, in fact), you must also import SensEvts.idl, which ships with the Microsoft Platform SDK. The files SensLogonSpy.cpp and SensLogonSpy.h, available for download with this article, contain all of the source code necessary for implementing the ISensLogon interface.
      Once you've set up your interface, you may register it so that you can receive ISensLogon events. To receive notifications, your interface must subscribe to the SENS events.

Subscribing to Events

      Subscriptions to SENS events are added via the IEventSystem interface. This interface is implemented by the EventSystem object (CLSID_CEventSystem) which is defined in the Platform SDK file EventSys.h.
      The Store method of IEventSystem adds a subscription.

     HRESULT Store (BSTR ProgID,
IUnknown *pInterface);

The ProgID argument of this method is always set to PROGID_EventSubscription, and the interface pointer is set to IEventSubscription. This interface is implemented in a system class (CLSID_EventSubscription) and is used to encapsulate the relationship between a subscriber and a publisher. The EventSubscription object is also defined in the file EventSys.h.
      You need to provide IEventSubscription a descriptive name for the subscription, the GUID of the publisher, and a pointer to the subscriber interface. I'll describe each of these briefly.
      A descriptive name for the subscription is a name used to identify the subscription; it's set via IEventSubscription:

  HRESULT IEventSubscription::put_SubscriptionName
(BSTR bstrSubscriptionName);

You can provide the GUID of the publisher that will fire the events with the put_EventClassID method:

  HRESULT IEventSubscription::put_EventClassID
(BSTR bstrEventClassID);

The BSTR used to identify the class ID is the string representation of the publisher's GUID. For the ISensLogon notifications, this is set to SENSGUID_EVENTCLASS_LOGON. This constant is defined in the file Sens.h section of the Platform SDK.
      Finally, the pointer to the subscriber interface must be passed to IEventSubscription. The coclass implementing the ISensLogon interface is initialized and its interface pointer is passed in a call as you can see in the following code:

  HRESULT IEventSubscription:: put_SubscriberInterface
(IUnknown ifpSubscriberInterface);

      IEventSubscription also has another way of identifying the subscriber interface that merits digressing a bit:

  HRESULT IEventSubscription:: put_SubscribeCLSID
(BSTR bstrSubscriberCLSID);

When specifying transient subscriptions, the first method is always used. The CLSID must be NULL; the second call must never be made. You can also set up a persistent subscription by calling the second method and specifying the CLSID. In this case, the subscriber interface must be set to NULL. In my sample code for this article, the transient subscription is established using the first method.
      That's all there is to it! Once the IEventSubscription object has been prepared, it is passed to the Store method of the EventSystem object introduced earlier. If all HRESULTs along the way return success, you've set up a transient subscription and are ready to start receiving ISensLogon notifications.
      Exactly the same procedure is followed for each of the other two interfaces available in SENS. The GUID constants for the remaining two interfaces defined in Sens.h are SENSGUID_EVENTCLASS_NETWORK and SENSGUID_EVENTCLASS_ONNOW.
      My sample files RegisterSensEvents.cpp and RegisterSensEvents.h (available for download at the link at the top of this article) contain the source code for registering all the SENS notifications.

Problems with ISensNetwork

      Unfortunately, ISensNetwork behaves so oddly that many people believe the notifications don't work at all. They do indeed work; it's just that they are expected to take a long time to fire, up to five minutes. The notifications are actually nearly immediate and very usable when the connection is a dial-up connection. For LAN and WAN connections, the wait is too long to be useful in a program, since mobile users won't take too kindly to waiting that long before being told that their connection is gone.
      When I tested ISensNetwork notifications, I got mixed results. More often than not, after five minutes the notification hadn't fired. In fact, after half an hour it still hadn't fired. Starting Internet Explorer 5.5, which causes ISensNetwork events to be installed on the user's computer, always caused the notifications to be fired.
      I would get one ConnectionLost notification if Internet Explorer was running, but not subsequent ones, and I wouldn't get the ConnectionMade notification at all. For ConnectionMade, just having Internet Explorer open wasn't enough. I had to start it each time I wanted to get a notification. Needless to say, a workaround would be impossible.
      Well, if ISensNetwork is critical and it doesn't work, what other option do you have? There is another option but it requires digging into the Microsoft Windows Device Driver Kit (DDK) .

Windows Management Instrumentation

      WBEM is an open initiative that specifies how components can provide unified enterprise management. WBEM is essentially a set of standards that specify how the data is defined (using the Common Information Model—CIM), how it is encoded (xmlCIM), and how it is transported (CIM Operations over HTTP). Together, these Web-enabled specifications allow a management tool to request and obtain data from a variety of Internet-enabled devices in a standard way and present it to a user.
      WMI is the Microsoft implementation of WBEM. Device drivers can use this service to make all kinds of data available to user-mode applications. WMI provides a standard way to discover and obtain the data. The data exposed in this way falls into three major categories: information, configuration, and notifications. I will focus on the third category.
      How exactly is this data exposed? Drivers define WMI data blocks that contain data items. The data blocks are defined in Managed Object Format (MOF) and are compiled into the resource section of the driver's binary. Each WMI data block is uniquely identified by a GUID.
      When a driver initializes, all its data blocks are registered with WMI. WMI adds the registered blocks to the Common Information Model Object Manager (CIMOM) database. User-mode WMI clients can access data blocks from CIMOM. The format of each data block is known only to the driver and the WMI client. WMI passes the data transparently from the driver to the client. Clients can register for events conditionally as well.
      Since the data format is not known, how does a client register for a particular event or a particular condition of an event? The answer can be found in the WMI Query Language (WQL). WQL is based on ANSI SQL.
      For example, to be notified when an adapter is enabled in a computer, the following WQL statement may be used:

  SELECT * FROM MSNdis_NotifyAdapterArrival

This is an event query. WQL also supports data queries and schema queries. Data queries are used to retrieve class instances and their data, while schema queries are used to retrieve class definitions and associated schema.
      Event queries can be streamlined further using the WHERE clause. Using WHERE, you can tell the publisher to notify you when an item in a class assumes a particular value. Consider MSNdis_StatusLinkSpeedChange, which is delivered every time the link speed of the network connection changes. A typical characteristic of WLAN networks is that the data rate drops as the distance between two synchronized nodes increases. An application may want to stop transferring a large file if the link speed falls below a fixed speed. For example, you could write a query to notify you when the inbound link falls below 10KB:

  SELECT * FROM MSNdis_StatusLinkSpeedChange WHERE Inbound < 10000

You can combine several clauses using the OR and AND operators. You can also use the NOT operator; for comparisons you can use =, <, <=, >, >=, and !=. Check the WQL documentation for additional options.
      It's always a good idea to filter queries. Remember that a call from a publisher to a subscriber goes through at least one level of indirection introduced by the system. An app that receives WMI notifications can act as a publisher and decide that you are not interested in a certain flavor of an event that you have registered for and prevent the notification from a trip to the subscriber.

Performing a WQL Query

      Submitting a query to WMI is done via WMI services, which are programmatically wrapped in the IWbemServices interface. One of the methods of this interface allows a user to register for an asynchronous event notification using a WQL query:

  HRESULT IWbemServices::ExecNotificationQueryAsync(
const BSTR strQueryLanguage,
const BSTR strQuery,
long lFlags,
IWbemContext* pCtx,
IWbemObjectSink* pResponseHandler);

      The IWbemObjectSink is a generic sink interface that can be used to receive WMI notifications. Typically, your sink will have IWbemObjectSink as its base class. You will need to include basetsd.h from the DDK include directories. The basetsd.h that comes with Visual Studio 6.0 or is included in the Platform SDK does not contain definitions for IWbemObjectSink. The linker will need to include Wbemuuid.lib to resolve this interface.
      In the following code, I make a call and tell it to expect a query in WQL that will register for all adapter arrival events. The adapter arrival events are routed to the m_pWMISink object:

  HRESULT hr = m_ifpWbemServices->ExecNotificationQueryAsync
((_bstr_t) _T("WQL"), bstrQuery, 0, 0, m_pWMISink);

The m_pWMISink object is of type CWMISink, which inherits from IWbemObjectSink:

    class CWMISink : public IWbemObjectSink

      Executing a badly formed query returns an HRESULT of 0x80041058 (WBEM_E_UNPARSABLE_QUERY). Executing a query with a bad class name will return an HRESULT of 0x80041002 (WBEM_E_NOT_FOUND). The error codes can be found in WBEMCLI.H, which ships with the DDK. If you execute a query with a condition that will never be set to true, you won't get any errors. There is no way for WBEM services to know what might happen at run time.
      My sample files RegisterNDISEvents.cpp and RegisterNDISEvents.h contain the source code for registering for NDIS WMI notifications. The files WMISink.cpp and WMISink.h implement the sink interface.
      Figure 5 lists the events that you can receive from NDIS. They are listed in the WMICORE.MOF file that ships with the Platform SDK. In this file, you'll find a wealth of WMI information delivered by NDIS. By looking for all classes that inherit from WMIEvent, you can find NDIS notifications. Before I show you how to read and interpret the file, I'll explain what a MOF file is.

Managed Object Format

      MOF stands for Managed Object Format. Drivers can expose data blocks, which describe the information that will be returned when a WMI request associated with that data block is made. Drivers can also expose event blocks, which describe the data blocks that will be sent to the subscriber when a specific event occurs. These blocks together form the driver's WMI schema. MOF is the language used to describe this schema.
      The MOF description of the schema for a driver is written into a text file that is then compiled into the driver's resource section using the MOF compiler, mofcomp.exe. The output file has a .bmf extension and can be included in the resource script file for the driver, like so:

  MofResource MOFDATA mymofschema.bmf

      A driver with MOF data in its resource section then registers as a provider of WMI data, first by calling IoWMIRegistrationControl and then handling the IRP_MN_REGINFO that is sent by WMI. I want to go into how this works here. As a subscriber, the good news is that once this is done, you are free to receive NDIS events in any user mode application.
      Understanding how the MOF file is scripted is essential because it allows you to delve into the depths of WMICORE.MOF and figure out what kind of WMI data is exposed. This data is not documented anywhere that I could find, so I'll arm you with the enough information to make yourself useful.
      Inside a MOF file, WMI data blocks are described by the following class definition:

  Class qualifiers [,Class qualifiers]
class MOFClass : [BaseClass]
[key, read]
string InstanceName;
Boolean Active;
[Property qualifiers]

      Each class can inherit from a base class. For example, NDIS notifications inherit from the WMIEvent class. Class names must be unique in the entire WMI namespace. Abstract classes are permitted with empty bodies, which can then be used for separating a class into a specific namespace by making it inherit from the abstract class. WMIEvent is an example of an abstract class.
      The class name and inheritance information must be followed by two mandatory items in the class description. InstanceName and Active are both items that must be defined by the MOF author. Drivers should leave them alone as these items are used internally by WMI. For all practical purposes, you can ignore them.
      Let's look at the MSNdis_AdapterArrival data block in MOF (see Figure 6) as an example. The first line indicates a dynamic WMI class that provides WMI data. Classes can also be static, in which case the data block is extracted directly from the WMI database. Events, by definition, must be dynamic classes.
      The GUID field associates a unique GUID with the data block. Each data block must have its own unique ID so that WMI can identify its definition in the MOF area of the driver's resource section. The locale field contains 0x409, the locale for American English, since data blocks may have locale-specific information.
      The WmiExpense field states the number of CPU cycles required to provide the data in the block. This value can be used by applications to determine the cost they will incur by making certain WMI calls. Finally, the description field is a string that is used to describe the purpose of the data block.
      The MSNdis_NotifyAdapterArrival class inherits from WMIEvent, marking it as a notification. Within the class, there is a single data item called DeviceName that returns the name of the device that the notification is related to.
      Several other property qualifiers can be used to embellish each data item. For example, you can use a qualifier called Values to specify a list of possible data values for the item. Refer to the WMI property qualifiers documentation for more details.
      Finally, the DeviceName item itself is defined as a string. If the MaxLen property qualifier is not specified, as in this case, the length of the string is not fixed. MOF data items can be Booleans, strings, signed and unsigned integers of 8, 16, 32, and 64 bits, or date/time variables. Look up driver-defined WMI data items on MSDN (MOF Data Types) for a full complement of data types available under MOF.
      For example, the MSNdis_AdapterArrival notification sets the DeviceName to the service name associated with the adapter or network card. The service name is a GUID assigned internally when the NIC is configured in the system. On my laptop, this is \Device\ {9EB6E23E-BCD3-4AD4-8CF3-9239EC52DDB2}. Figure 7 shows this information in the registry. You can now read through the MOF description of each NDIS event and understand how the data blocks are defined.

Figure 7 MOF Info in the Registry
Figure 7 MOF Info in the Registry

      Combining the notifications that can be obtained from SENS and NDIS, you can monitor many of the conditions that affect mobile computing. With this information, your application will stand out as a model citizen of the mobile world.

For related articles see:
The COM+ Event Service Eases the Pain of Publishing and Subscribing to Data
Say Goodbye to Quirky APIs: Building a WMI Provider to Expose Your Object Info
Windows Management Instrumentation: Create WMI Providers to Notify Applications of System Events
WMI and .NET: System.Management Lets You Take Advantage of WMI APIs within Managed Code
Aspi Havewala is a Software Engineering Manager at Motorola, developing n-tier applications. He is the coauthor of Building Powerful Platforms with Windows CE (Addison-Wesley, 2001) and follows cricket everywhere it is played in the world.