Building a Enum that Supports Bit Fields in PowerShell

I was working on a project recently that required me to have an Enum that allowed bit fields as opposed to the normal fields that we might deal with in our day to day endeavors with PowerShell.

If you aren’t quite sure what I mean by this, here is a quick example that shows the difference between the two.

First, the regular Enum that we are used to:

([DayOfWeek]0..6).ForEach({[pscustomobject]@{Number=$_;Data=[DayOfWeek]$_}})

image

Notice that each number matches up to a single value: 0 = Sunday, 3 = Wednesday and so on.

Now for an Enum with bit fields:

([System.IO.FileAttributes]1..7).ForEach({
[pscustomobject]@{Number=$_;Data=[System.IO.FileAttributes]$_}
})

image

You can see here that depending on the value, you may end up for more than 1 item. This typically will follow a 1,2,4,8,16…etc… approach for the single items and then they perform a bitwise XOR to combine values for more items.

Now that we have covered this topic in a very basic form, we still need to understand how to build one so we can use it in PowerShell. In PowerShell V2..4 we would have to settle on creating the enum by creating a C# here string and loading it up into the session like so:

Add-Type -TypeDefinition @"
[System.Flags]
public enum Animals
{
Dog = 1,
Cat = 2,
Bird = 4,
Lizard = 8
}
"@

All we have to do is ensure that the [System.Flags] attribute is used within our code.

Now we can verify that this actually worked like we wanted:

(1..15).ForEach{[pscustomobject]@{Number=$_;Animal=[Animals]$_}}

image

I’d say it passes the test!

Next up is building one dynamically using reflection. A little more complex but not something that we can’t do..right?

#region Module Builder
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('Random')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Random', $False)
#endregion Module Builder

#region Animals
$EnumBuilder = $ModuleBuilder.DefineEnum('Animals', 'Public', [int32])
[void]$EnumBuilder.DefineLiteral('Dog', [int32] 0x00000001)
[void]$EnumBuilder.DefineLiteral('Cat', [int32] 0x00000002)
[void]$EnumBuilder.DefineLiteral('Bird', [int32] 0x00000004)
[void]$EnumBuilder.DefineLiteral('Lizard', [int32] 0x00000008)
$EnumBuilder.SetCustomAttribute(
[FlagsAttribute].GetConstructor([Type]::EmptyTypes),
@()
)
[void]$EnumBuilder.CreateType()
#endregion Animals

The trick here is to create a custom attribute where we supply the default constructor for the Flags.Attribute type add that attribute to the enum.

We can test using the same code to see if everything matches up:

image

Success! Ok, so I have covered the old ways of doing it and even the reflection approach is still valid in V5 if your goal is keeping everything in memory and not writing to disk.

If you are interested in an even easier way of doing this, then check out fellow MVP Matt Graeber’s PSReflect module that gives you the PSEnum function that makes this whole thing a snap!

$Mod = New-InMemoryModule -ModuleName Win32
psenum -FullName Animals -Type Int32 -Bitfield -Module $Mod -EnumElements @{
Dog = 1
Cat = 2
Bird = 4
Lizard = 8
}

If you run the usual code, you can easily see that it works!

In PowerShell V5, we are greeted with an easier way to natively build an Enum in PowerShell with the…Enum keyword! But we don’t quite care about that as we really need to have support for Flags so we can have our bit field support.

Luckily we have that available albeit not documented anywhere yet. We just have to define that it will have the Flags attribute before calling the Enum keyword and we are ready to go!

[Flags()] enum Animals {
Dog = 1
Cat = 2
Bird = 4
Lizard = 8
}

How simple was that? We just create the enum and it is ready to go with not much code or having to compile C# (not that it was that tough anyways) and we can verify once again just to make sure it works like we want it to.

image

No oddness here! It worked like a champ and we have yet another way to accomplish our goal! Just remember that this approach only works with PowerShell V5.

Enjoy!

About Boe Prox

Microsoft Cloud and Datacenter MVP working as a SQL DBA.
This entry was posted in powershell and tagged , , , , . Bookmark the permalink.

4 Responses to Building a Enum that Supports Bit Fields in PowerShell

  1. Pingback: RoboCopy ExitCodes the PowerShell way | pshirwin

  2. Reblogged this on Programming, PowerShell and Pants and commented:

    I found this post extremely useful – definitely worth a read.

  3. Cody says:

    No big deal but your blog has rewritten ” as " in the Add-Type code sample. Cool post though.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s