As you've probably gathered, I've been spending a lot of time working with the State and Notifications Broker lately. When you have the opportunity to spend this much time with a technology, one is bound to come across some of the unpolished edges.
One such unpolished edge relates to some significant inconsistencies in the way the SystemState class reports bitmask-based state values; a bitmask-based state value is a state value that requires the use of a bitmask to derive the state value from the registry value. These bitmask-based values are useful both in saving space in the registry and, as you saw in last week's post, optimizing value comparisons.
Converting a registry value to a bitmask-based state value is usually a 2-step process.
- Apply the bitmask to the registry value - usually using a bitwise-and
- Convert the result of the bitwise-and to the appropriate data type (often an enumeration value)
As you may recall, there are 3 ways to access a state value with the SystemState class.
- The most common way is to use one of the SystemState static properties
- You can also access the value from the ChangeEventArgs.NewValue property in the SystemState.Changed event handler
- When you create an instance of the SystemState class with the SystemProperty enumeration, you can access the state value using the SystemState class' CurrentValue instance property
The issue with the SystemState class is that the 3 ways to retrieve the state value do not apply the 2 conversion steps consistently.
- The SystemState static properties report the value correctly – no problem on these
- The ChangeEventArgs.NewValue property is not quite correct – It applies the bitmask but no type conversion.
- The SystemState class's CurrentValue instance property is the most concerning – it does not apply the bitmask (and therefore no type conversion). It returns the entire contents of the registry value.
As an example, checkout the following code.
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);
}
In the code you can see that the _batteryStrength instance of the SystemState class is created using the SystemProperty.PowerBatteryStrength enum value and therefore represents the same state value that the SystemState.PowerBatteryStrength static property represents. As I mentioned above, although they represent the same state value, they report the state value differently.
Take a look at the figure below which shows the output from the 3 DebugWriteLine method calls in the PowerBatteryStrength_Changed event handler.

As you can see, the SystemState.PowerBatteryStrength static property reports the state value as the appropriate enum value. The NewValue property has the correct numeric value (41 is the integer representation of the BatteryLevel.Medium enum value).
It's the CurrentValue instance property that is most concerning; the value is not actually the PowerBatteryStrength state value but rather the entire contents of the registry value that contains the PowerBatteryStrength state value. For the CurrentValue instance property to be useful, you would need to explicitly apply the bitmask in your code.
The good news is, in practice the differences in the reported state values isn't as bad as it might look. For example, the ChangeEventArgs class' NewValue property is of type Object; therefore, you need to cast it to a PowerBatteryStrength enum type anyway. This means that for most values, you would write the same line of code to convert the state value from type Object to the appropriate enum value.
The following is the same line of code you write to retrieve the NewValue property as type BatteryLevel whether it contains the integer 41 or BatteryLevel.Medium.
BatteryLevel level = (BatteryLevel)e.NewValue;
As for the SystemState class' CurrentValue instance property, the CurrentValue instance property just isn't very useful in most cases where the SystemState instance is associated with a bitmask-based value. In the current SystemState class implementation, the CurrentValue property doesn't include any special optimization (such as caching the value' registry key handle); therefore, the CurrentValue instance property is no more efficient then using the corresponding SystemState class' static property. With this being the case, you can just use the SystemState class' static properties in most cases.
Posted
Apr 27 2007, 04:55 PM
by
jim-wilson