PowerShell and bit field attributes

A while ago I wrote a script, which helps me troubleshooting calendar issues:

Troubleshooting calendar items

Lately I wanted to improve the script and needed to translate two properties. These properties reflect what action a user has taken on and how a meeting object has changed:

Both properties are specified by a bit field.

The challenge was to convert the value to a human readable string to make life easier.

Introduction

Here is an example:

This is the property PidLidChangeHighlight of a modified item with the decimal value of 3. I used MFCMAPI to open the item and property:

BitFiel01.PNG

As described on MSDN the field is described as follows:

0 1 2 3 4 5 6 7 8 9 1
0
1 2 3 4 5 6 7 8 9 2
0
1 2 3 4 5 6 7 8 9 3
0
1
A B C D E F G H I J K L M unused N

A – BIT_CH_START (1 bit): The PidLidAppointmentStartWhole property (section 2.2.1.5) has changed.

B – BIT_CH_END (1 bit): The PidLidAppointmentEndWhole property (section 2.2.1.6) has changed.

C – BIT_CH_RECUR (1 bit): The recurrence pattern has changed. For details about recurrence patterns, see the section 2.2.1.44.

D – BIT_CH_LOCATION (1 bit): The PidLidLocation property (section 2.2.1.4) has changed.

E – BIT_CH_SUBJECT (1 bit): The PidTagNormalizedSubject property ([MS-OXCMSG] section 2.2.1.10) has changed.

F – BIT_CH_REQATT (1 bit): One or more required attendees were added.

G – BIT_CH_OPTATT (1 bit): One or more optional attendees were added.

H – BIT_CH_BODY (1 bit): The body was modified.

I – unused (1 bit): These bits are not used. MUST be zero and MUST be ignored.

J – BIT_CH_RESPONSE (1 bit): Either the PidTagResponseRequested property ([MS-OXOMSG] section 2.2.1.46) or the PidTagReplyRequested property ([MS-OXOMSG] section 2.2.1.45) has changed.

K – BIT_CH_ALLOWPROPOSE (1 bit): The PidLidAppointmentNotAllowPropose property (section 2.2.1.26) has changed.

L – Deprecated (1 bit): This flag is deprecated. This value is neither read nor written to.

M – Reserved (1 bit): This flag is reserved and MUST NOT be set.

unused (18 bits): These bits are not used. MUST be zero and MUST be ignored.

N – Reserved (1 bit): This flag is reserved and MUST NOT be set.

When you convert 3 to binary format the field looks like this:

Decimal 2048 1024 512 256 128 64 32 16 8 4 2 1
Bit 0 0 0 0 0 0 0 0 0 0 1 1

Note: Don’t get confused. The description of the field is read from left to right. Binary wise you start from right to left order.

In PowerShell you can use the .NET class Convert:

[convert]::ToInt32('000000000011',2)
3

This works also vise-versa:

[convert]::ToString('3',2)
11

Note: The leading zeros are not shown!

Another example, which might explains it better:

[convert]::ToInt32('100000000000',2)
2048
[convert]::ToString('2048',2)
100000000000

In this case bit #12 is set, which represents the decimal number 2048.

Now that we have an understanding, which bit represents what number we can check if a bit is set or not. For this we can use the Bitwise Operators in PowerShell. In our case I’m using -bAnd:

Let’s check if the bit #12 is set. When I use -bAnd and the bit is set I will get the decimal value for this bit returned. If the bit is not set I will get 0 returned:

'100000000000' -band 2048
2048
'100000000000' -band 30
0

In the first example the bit is set and I get 2048 returned, but the bit #12 is not set in the binary value, which represents the decimal number 30. This number is represented by the following binary:

[convert]::ToString('30',2)
11110

The leading zeros are missing. When you add them it looks like this:

Decimal 2048 1024 512 256 128 64 32 16 8 4 2 1
Bit 0 0 0 0 0 0 0 1 1 1 1 0

2+4+8+16=30

That said we just have to check if the comparison returns a 0 or not:

('100000000000' -band 2048) -ne '0'
True
('100000000000' -band 30) -ne '0'
False

Use case

Now that we now how to check if a bit is set, we can use a hashtable and check a decimal value against this hashtable. In the case of the property PidLidChangeHighlight, we need to create the following hashtable:

$ChangeHighlightHash = @{
    START        = 1
    END          = 2
    RECUR        = 4
    LOCATION     = 8
    SUBJECT      = 16
    REQATT       = 32
    OPTATT       = 64
    BODY         = 128
    RESPONSE     = 512
    ALLOWPROPOSE = 1024
}

Hashtable sort by Value

$ChangeHighlightHash.GetEnumerator() | Sort-Object -Property Value

BitFiel02.PNG

Let’s check the example above, where the property PidLidChangeHighlight has a value of 3:

[int]$Value='3'
foreach ($Bit in ($ChangeHighlightHash.GetEnumerator() | Sort-Object -Property Value )){
    if (($Value -band $Bit.Value) -ne 0){
        $Bit.Key
    }
}

BitFiel03.PNG

We got START and END returned as MFCMAPI also showed.

Build function

With the methode above I created the following two functions to convert the value for these fields into a human readable string:

ConvertFrom-ClientIntent

function ConvertFrom-ClientIntent
{
  [CmdletBinding()]
  [Alias()]
  [OutputType([string])]
  Param
  (
    [Parameter(Mandatory=$true,
               ValueFromPipelineByPropertyName=$false,
               Position=0)]
               [int]$ClientIntentValue
  )

  Begin
  {
    [string]$RetunValue =  ''
    $ClientIntentHash = @{
      Manager                        = 1
      Delegate                       = 2
      DeletedWithNoResponse          = 4
      DeletedExceptionWithNoResponse = 8
      RespondedTentative             = 16
      RespondedAccept                = 32
      RespondedDecline               = 64
      ModifiedStartTime              = 128
      ModifiedEndTime                = 256
      ModifiedLocation               = 512
      RespondedExceptionDecline      = 1024
      Canceled                       = 2048
      ExceptionCanceled              = 4096
    }

  }
  Process
  {
    foreach ($Bit in ($ClientIntentHash.GetEnumerator() | Sort-Object -Property Value )){
      if (($ClientIntentValue -band $Bit.Value) -ne 0){
        $RetunValue += $Bit.Key +'|'
      }
    }
  }
  End
  {
    Write-Verbose "Bit mask:$([Convert]::ToString($ClientIntentValue,2))"
    return ($RetunValue.TrimEnd("|"))
  }
}

ConvertFrom-ChangeHighlight

function ConvertFrom-ChangeHighlight
{
[CmdletBinding()]
[Alias()]
[OutputType([string])]
Param
(
    [Parameter(Mandatory=$true,
               ValueFromPipelineByPropertyName=$false,
               Position=0)]
               [int]$ChangeHighlightValue
)

Begin
{
  #$Value= [Convert]::ToString($ChangeHighlightValue,2)
  [string]$RetunValue =  ''
  $ChangeHighlightHash = @{
    START        = 1
    END          = 2
    RECUR        = 4
    LOCATION     = 8
    SUBJECT      = 16
    REQATT       = 32
    OPTATT       = 64
    BODY         = 128
    RESPONSE     = 512
    ALLOWPROPOSE = 1024
  }

}
Process
{
foreach ($Bit in ($ChangeHighlightHash.GetEnumerator() | Sort-Object -Property Value )){
  if (($ChangeHighlightValue -band $Bit.Value) -ne 0){
    $RetunValue += $Bit.Key +'|'
  }
}
}
End
{
  Write-Verbose "Bit mask:$([Convert]::ToString($ChangeHighlightValue,2))"
  return ($RetunValue.TrimEnd("|"))
}
}

With this you can easily convert those values:

BitFiel04.PNG

Conclusion

The above example shows how to check bit defined field for a specific bit. I hope this helps you, just in case you have a similar need.

1 thought on “PowerShell and bit field attributes

  1. Pingback: History of a calendar item | The clueless guy

Leave a comment