↘search↙



↗search↖

Some messy C# code.

Ever been asked FizzBuzz in a programming interview, and thought to yourself, what could I do here that would really throw them off? Well, this. Do this.

                            
private static void FizzBuzz1()
{              
	// Declare all of our variables right here at the top.
	int i, i2;
	string buff;
	Action<string> fb = null;
	goto assignVars;
	loop:
	{
		if (0 == i2 % 3)
		goto dofizz;
		if (0 == i2 % 5)
		goto dobuzz;
		goto donumber;
	}
	endoftheloop:
	if (i --> 0 && (i2 = 100 - i) > 0)
	goto loop;
	goto end;
	dofizz:
	buff += "fizz";
	if (0 == i2 % 5)
	goto dobuzz;
	goto writer;
	dobuzz:
	buff += "buzz";
	goto writer;
	donumber:
	buff += i2;
	writer:
	fb(buff + Environment.NewLine);
	goto justBuff;
	// Do this safely at the end of the function.
	assignVars:
	i = 100;
	fb = Console.Write;
	justBuff:
	buff = string.Empty;
	goto endoftheloop;
	end: ;
}
                            
                        
Nobody can be tooooo mad, since it really is a completely working fizzbuzz. Some may question who taught you that goto works in c#, and why you dont seem to know how to use a for-loop. Some may even question why you assigned Console.Write to an action, or why you didn't just use Console.WriteLine. All good questions. Seems like you would have a lot to talk about during your interview.

Theres nothing like a single method chain which does all of Fizz Buzz. So here's some awesome.

                            
private static void FizzBuzz2()
{
  Console.WriteLine(
    string.Join(
      Environment.NewLine,
      Enumerable.Range(1, 100)
        .Select(
          i =>
            string.Join(
              string.Empty,
              i % 3 == 0 ? "fizz" : null,
              i % 5 == 0 ? "buzz" : null,
              !(i % 5 == 0 || i % 3 == 0) ? $"{i}" : null))));
}
                            
                        



Test to see if images work

This post is to check if if images work! Will see what happens. This may be a mess.

If this picture isn't currently ruining the entire page, then this is a success.




Powershell Mount a VHD to a Folder

Here is one to get the people going. Have you ever had an issue, where you tried to mount too many disks on your machine at one time, and ran out of driver letters? Well have no fear. It is a totally normal problem. Instead of mounting to drive letters... of which there are only 26. We can add access paths to partitions on the disk. You know what the best news is? The limit on access paths is totally crazy. I have once mounted 50 disks at the same time, and everything still seemed to work.

So here is the rub. This example is going to use a bunch of powershell cmdlets. But all of these are just wrappers around the root\microsoft\windows\storage CIM namespace. We could do this the badass way using only CIM, but it would be much longer, and I need something to write about tomorrow.

Anyway, this is what the code looks like.

                            
#
# Mount a virtual hard disk to a folder
#
function Mount-VHDToFolder
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$true)][String]$Image,
        [Parameter(Mandatory=$true)][String]$MountFolder
    )
    process
    {
        try
        {
            # Gets a mounted disk image
            $mountedDisk = Mount-DiskImage -ImagePath $Image -NoDriveLetter -PassThru -ErrorAction Stop | Get-DiskImage
        }
        catch
        {
            Write-Error "Failed to mount disk"
            return
        }

        # Get all of the partitions
        $partitions = Get-Partition -DiskNumber $mountedDisk.Number

        foreach($partition in $partitions)
        {
            $partFolder = Join-Path $MountFolder $partition.PartitionNumber

            # Clean up this folder if it exists
            if(Test-Path $partFolder)
            {
                rmdir -Force $partFolder
            }

            # Make the new folder
            mkdir $partFolder | Out-Null

            try
            {
                # Add the access path for the disk
                Add-PartitionAccessPath -InputObject $partition -AccessPath $partFolder -ErrorAction Stop
            }
            catch
            {
                Write-Warning "Could not add access path '$($partFolder)' for partition '$($partition.PartitionNumber)'"
            }
        }
    }
}
                            
                        
This is going to create a folder for each partition number in the mount folder which you provide. It will try to add an access path for each partition. FYI, the reason I check if the folder exists, and delete it, is because AddAccessPath will fail if there is a broken symbolic link on the folder. AKA, you dismounted a disk which was using that folder as an access path without removing the access path first.

And there you have it! It is pretty simple really. Mounting the disk image gives us a MSFT_Disk cim object. We can use that disk to get all of the related MSFT_Partition cim objects. The MSFT_Partition object has a method called "AddAccessPath". Powershell wraps all of this stuff up for us super nicely so we won't need to mess with it.




Returning Custom Error Pages in IIS

So here is the solution to an annoying one. Have you ever made an asp.net website and tried to make custom error pages? So here is the issue. I could not, for the longest time, figure out how to make my custom error pages actually show up for the user. Seriously, everything worked fantastic, until my HTTP response was anything other than 200. My solution was to return a "soft" 404 page. Which web crawlers really hate.

Soft Error - When you catch the error on the server, and return an error page with a success error code. For example a user browses to a link on your site which does not exist, causing a 404. You serve up a 404 page, but with a "200 OK" response code. From a user's perspective this is fine, but bots have no idea what is going on.

To be entirely honest, I made up most of that definition. Wikipedia refers to it as a "phony 404 error". Realistically you can do the same thing for any type of error on the server side. Actually, you can throw response codes out the window and return whatever crap you want all the time. Do it... Return a 500 for every single page. Users will never notice, but bots will lose their minds. (Actually you'll just never make it anywhere near the top on any search engine).

Ok So let's get back to the point here. Instead of a "Soft Error", you should always return the proper error code. IIS doesn't like to make this easy for you by default, so here is what you need to configure to make sure everything works properly.

                            
  <system.webServer>
    <httpErrors existingResponse="PassThrough"></httpErrors>
  </system.webServer>
                            
                        
Just stick this in the System.webServer section in your web.config. "existingResponse=PassThrough" pretty much means that you are going to take matters into your own hands when it comes to errors. If an error response comes down the pipe (aka: http code >= 400) then IIS will leave the message untouched if a response exists.

Hope this helps. My ones of users have been consistently complaining about poor error handling issues, so I dedicated my night (after this glorious Donald vs. Hillary debate) to fixing the issue. Thanks for your feedback Daniel.




Powershell IPv4 to IPv6 and Expand IPv6

So here we are again. A long adventure to get some food has been completed. The TV blaring Season 5 of New Girl. I think to myself, you know what would go really well with this Shawarma? If I could finally read an IPv6 address without looking like an idiot. So here goes. IPv4 is simple, 32 bits of easy to understand goodness. Every single IPv4 address is made up of 4 bytes. For example "192.168.1.1". There is no shorthand either, that is the address, that's it. There is no better way to write it. Ok, so now that that is out of the picture, what is IPv6.

IPv6 is 128 bits of a little bit more confusing. Well actually, it's not the address that is confusing, it's the notation. When you see IPv6, a lot of times you see things like "::1" and "FE80::1". What does that even mean. How is that 128 bits. How will we ever know. So the secret is in the "::", which is pretty much just shorthand for "a lot of zeros go here".

For example: if we take the address "::1", it actually expands out to "0000:0000:0000:0000:0000:0000:0000:0001". Literally just means fill in this space with zeros. Another example: "FE80::1" becomes "FE80:0000:0000:0000:0000:0000:0000:0001". Just FYI, you are only allowed to use the "::" once, otherwise addresses would be unreadable. ("::1::") So before anything, here is a function to automate that, because it drives me nuts.

                            
#
# Expand an IPv6 address. For example ::1 becomes 0000:0000:0000:0000:0000:0000:0000:0001
#
function Expand-IPV6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
                   Position = 0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        $count = 0
        $loc = -1

        # Count the number of colons, and keep track of the double colon
        for($i = 0; $i -lt $IPv6.Length; $i++) 
        { 
            if($IPv6[$i] -eq ':') 
            {
                $count++
                if(($i - 1) -ge 0 -and $IPv6[$i-1] -eq ':')
                {
                    $loc = $i
                }
            }
        }

        # If we didnt find a double colon and the count isn't 7, then throw an exception
        if($loc -lt 0 -and $count -ne 7)
        {
            throw "Invalid IPv6 Address"
        }

        # Add in any missing colons if we had a double
        $cleaned = $IPv6
        if($count -lt 7)
        {
            $cleaned = $IPv6.Substring(0, $loc) + (':'*(7-$count)) + $IPv6.Substring($loc)
        }

        # Parse current values in fill in new IP with hex numbers padded to 4 digits
        $result = @()
        foreach($splt in $cleaned -split ':')
        {
            $val = 0
            $r = [int]::TryParse($splt, [System.Globalization.NumberStyles]::HexNumber, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$val)
            $result += ('{0:X4}' -f $val)
        }

        return $result -join ':'
    }
}
                            
                        
So this does the expansions work for us, because it’s a pain in the butt to deal with these addresses in their short form. Also, I know the code is gross and I apologize for that, but there is more to come. Ok, so now, what happens if we have an IPv4 address and we need to turn it into IPv6. Boom, its easy. The last 32 bits of the IPv6 address are equal to our IPv4 address, and the 16 bits preceding those are all set to 1. Easy enough! That means, the IP address 1.1.1.1 would turn into "::FFFF:0101:0101" or "0000:0000:0000:0000:0000:FFFF:0101:0101" in full form. So how do we make that happen?

                            
# Map an IPv4 address to IPv6
function Map-IPv4ToIPv6
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv4
    )
    process
    {
        # Split on the dots
        $split = $IPv4 -split '\.'
        if($split.Length -ne 4)
        {
            throw 'Not a valid IPv4 Address'
        }

        # Parse into numbers
        $vals = @()
        foreach($v in $split)
        {
            $vals += [int]::Parse($v)
        }

        # Return as shorthand ipv6
        return "::FFFF:{0:X2}{1:X2}:{2:X2}{3:X2}" -f $vals
    }
}
                            
                        
BOOM, mission accomplished. Hopefully (it worked in a few tests). Also, for completeness, here is a function which turns an IPv4 address which is represented in IPv6 back into an IPv4 address.

                            
#
# Transforms an IPv4 address which is represented in IPv6 back into IPv4
#
function Map-IPv6ToIPv4
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, 
            Position = 0,
            ValueFromPipeline=$true,
            ValueFromPipelineByPropertyName=$true)][String]$IPv6
    )
    process
    {
        # Get the expanded form
        $expanded = Expand-IPV6 $IPv6
        $split = $expanded.Split(':')

        # Make sure this is valid...
        $is4MappedTo6 = $split[5] -match 'FFFF'
        if(-not $is4MappedTo6)
        {
            throw 'This is not an IPv4 Address mapped to IPv6'
        }

        # Parse each byte as an integer
        $addr = @()
        for($i = 0; $i -lt 8; $i+=2)
        {
            $addr += [int]::Parse(($split[6 + [math]::Floor(($i / 4))][($i % 4)..(($i%4)+1)] -join ''), [System.Globalization.NumberStyles]::HexNumber)
        }

        return $addr -join '.'
    }
}
                            
                        
The classes built in to handle IP addresses are definitely much smarter, and the way to go. But this was a sad attempt at a way to do it in Powershell!




SHA1 in F#

So, Season 5 of New Girl just came out on Netflix. I think you know where this is going, but effectively I sat in the Lovesac and watched TV all day. To feel a little bit better, I thought I should exercise my fingers a bit. Anyway here goes, back by popular demand, an F# implementation of SHA1. Now, before you look at my crap code and start asking questions. This is pretty much my first time ever using F#. Anyway, it compiles, and I've tested it a few times, so it seems 100% ready to ship.

                            
// Converts an ascii string into a byte sequence
let getBytes (input: string) = seq {for c in input -> (byte)c}

// Get a uint32 from a sequence of 4 bytes in big endian
let bytesToUi (input: seq<byte>) = 
    let i = ref 3

    // My decrementor function because I didnt realize value assignment don't return a value
    let dec value =
        let t = !value
        value := (!value - 1)
        t
    // Fold the 4 bytes into a uint
    Seq.fold (fun (acc : uint32) (b : byte) -> acc ||| (uint32(b) <<< (dec i) * 8)) 0u input

// Rotate left
let rotl (input: uint32) (amount) =
    (input <<< amount) ||| (input >>> (32 - amount))

// Runs SHA1 on a string
let sha1 (input: string) =
    // Get length of input in bytes
    let messageLen = uint32(input.Length)

    // Get length of input in bits
    let bitlen = uint64(8u * messageLen)

    // Get the amount we need to pad. Include the bit (0x80) we append to the message and the ulong at the end of the message for length
    let padlen = 
        match ((messageLen + 9u) % 64u) with
        | 0u -> 0
        | _ -> int(64u - ((messageLen + 9u) % 64u))

    // Append the bit to the end of the message
    let asciibytes = Seq.append (getBytes(input)) (Seq.singleton 0x80uy)

    // Pad with 0s. Assume the length is not going to be larger than uint32, so set the highest order 4 bytes to zero as well
    let asciibytes = Seq.append asciibytes (seq {for i in 1.. (padlen+4) -> 0uy})

    // Append 64 bits of message length
    let messageBytes = Seq.append asciibytes (seq {for i in (List.rev [0..7]) -> ((byte)(bitlen >>> (i * 8)))})

    // Constants
    let mutable h0 = 0x67452301u
    let mutable h1 = 0xEFCDAB89u
    let mutable h2 = 0x98BADCFEu
    let mutable h3 = 0x10325476u
    let mutable h4 = 0xC3D2E1F0u

    // Get the total message length
    let totalBytes = Seq.length messageBytes

    // For each 64 byte chunk
    for chunk = 0 to ((totalBytes / 64)-1) do
        let w = [| for i in 1 .. 80 -> 0u|]

        // Populate the array
        for i = 0 to 15 do
            w.[i] <- bytesToUi (Seq.take 4 (Seq.skip (i*4 + chunk * 64) messageBytes))
        for i = 16 to 79 do
            w.[i] <- rotl ((w.[i-3]) ^^^ (w.[i-8]) ^^^ (w.[i-14]) ^^^ (w.[i-16])) 1

        // Set up the locals
        let mutable a, b, c, d, e = h0, h1, h2, h3, h4
        let mutable k = 0u
        let mutable f = 0u

        for i in 0 .. 79 do
            let round = match i with
                        | m when m < 20 -> (0x5A827999u, ((b &&& c) ||| ((~~~b) &&& d)))
                        | m when m < 40 -> (0x6ED9EBA1u, (b ^^^ c ^^^ d))
                        | m when m < 60 -> (0x8F1BBCDCu, ((b &&& c) ||| (b &&& d) ||| (c &&& d)))
                        | _ -> (0xCA62C1D6u, (b ^^^ c ^^^ d))
            let temp = (rotl a 5) + (snd round) + e + (fst round) + w.[i]
            e <- d
            d <- c
            c <- rotl b 30
            b <- a
            a <- temp

        h0 <- h0 + a
        h1 <- h1 + b
        h2 <- h2 + c
        h3 <- h3 + d
        h4 <- h4 + e

    [h0; h1; h2; h3; h4]
                            
                        



Make a huge file in PowerShell

Every once in a while, you end up on a friend's computer and you think to yourself, what is the most annoying thing I can do RIGHT NOW. Well don't even worry, here is a super quick way to make some gigantic files. Of course, it starts with opening up PowerShell. (You can do the same thing on a Unix machine with 'cat'). Ok let's do it.

                            
'a' * 1000 > temp.txt
Get-Content temp.txt >> temp.txt
                            
                        
  1. Writes the letter 'a' to a file named temp.txt 1000 times.
  2. Reads temp.txt line by line, and appends it to the end of temp.txt

Pretty simple right!? Just a never ending loop of file writing. You can event do it in a single line.

                            
'a' * 1000 > temp.txt; gc temp.txt >> temp.txt
                            
                        
The real question is, how fast does this baby go. So here we go, a test which lets this run for 30 seconds, then kills it.

                            
$job = Start-Job -ScriptBlock {'a' * 1000 > temp.txt; Get-Content temp.txt >> temp.txt}
Wait-Job -Job $job -Timeout 30
Stop-Job -Job $job
                            
                        
This made me a nice 1.4GB text file. Also tried for 120 seconds, and ended up with a 5.2GB file. So there you have it. Fun time with files.




Powershell Get Free Space on Drive

Here is a quick one. I couldn't find a great way to find the amount of free space which exists on a disk with powershell. I am sure there is a good option, but anway, here is a great solution with uses P/Invoke. This will uses the kernel32 function: GetDiskFreeSpaceEx to figure out how much free space is on a drive. It seems to work pretty well. One catch, P/Invoke in powershell is weird. You pretty much need to go compile the C#.

                            
#
# Gets the amount of free disk space.
#
function Get-DiskFreeSpace
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $false)]$Directory = $pwd
    )
    process
    {
        # Name of the P/Invoke we are going to use
        $name = 'GetDiskFreeSpaceEx'

        # The dll the funciton is in
        $dll = 'kernel32.dll'

        # The P/Invoke function definition
        $def = "[DllImport(`"$dll`")]
        public static extern bool $name(string lpDirectoryName,
           out ulong lpFreeBytesAvailable, 
           out ulong lpTotalNumberOfBytes, 
           out ulong lpTotalNumberOfFreeBytes);"

        # First try to see if this already exists
        $type = 'Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes.GetStorageFreeType' -as [type]
        
        # If not, new up a runtime type with this function definition
        if(-not $type)
        {
            $type = Add-Type -MemberDefinition $def -Name "GetStorageFreeType" -PassThru
        }

        $freeBytes = [uint64]0
        $totalBytes = [uint64]0
        $totalFreeBytes = [uint64]0

        # Do the P/Invoke
        $res = $type::GetDiskFreeSpaceEx($Directory, [ref] $freeBytes, [ref] $totalBytes, [ref] $totalFreeBytes)

        # Get the total free bytes
        return $totalFreeBytes
    }
}
                            
                        



Powershell Sha1 Implementation

Have you ever thought to yourself, "How does a deprecated standard like SHA1 work, and how could it be implemented in powershell?". Well look no further. Actually, it was a litte harder than it seems, since powershell REALLY loves signed numbers. Lets take for example: 0xFFFFFFFF, which one would assume is ?equal to 4294967295?. Well.. Check out this simple line of code.

                            
#This wont throw an exception right?
[uint32]0xFFFFFFFF
                            
                        
Well, in powershell this just throws an exception. You know why!? Because under the hood, PShelly goes OH "0xFFFFFFFF", that's 32 bits. Let me stick it in a 32 bit signed integer. But wait... "0xFFFFFFFF" is just 32 1s. But oh crap, in 2s complement, that's -1. So in powershell 0xFFFFFFFF == -1. So now its pretty clear why the above code throws an exception, there is no unsigned value to represent -1. Seriously, just go type "0xFFFFFFFF" into powershell. So.. because of that, everything here has to be done in a 64 bit integer.

                            
#
# Implemenation of SHA1
#
function SHA1
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory = $false, Position=1)][string]$Str
    )
    process
    {
        #
        # Truncate a value to UInt32
        #
        function TUI
        {
            param($val)
            process
            {
                [uint64]($val -band (((-bnot [uint64]0)) -shr 32))
            }
        }

        #
        # Get a 32 bit value from a byte array
        #
        function GetBigEndianUInt
        {
            param
            (
                [byte[]]$bytes,
                [int]$index
            )
            process
            {
                return ([uint64]$bytes[$index] -shl 24) -bor ([uint64]$bytes[$index+1] -shl 16) -bor ([uint64]$bytes[$index+2] -shl 8) -bor ([uint64]$bytes[$index+3])
            }
        }

        #
        # Left rotate
        #
        function LeftRotate
        {
            param
            (
                [uint64]$val,
                [int]$amount
            )
            process
            {
                $res = TUI ([uint64]$val -shr (32 - $amount))
                $res = $res -bor ($val -shl $amount)
                return TUI $res
            }
        }

        # Initialize constants
        $h0 = TUI 0x67452301
        $h1 = TUI 0xEFCDAB89
        $h2 = TUI 0x98BADCFE
        $h3 = TUI 0x10325476
        $h4 = TUI 0xC3D2E1F0
        
        # Get the message bytes
        $message = [System.Text.ASCIIEncoding]::ASCII.GetBytes($Str)

        # Get the length in bytes which we need. Message length + 0x80 + (64bit message len)
        $len = ($message.Length + 9)
        
        # Get the padded length of our our byte array
        if($len % 64 -ne 0){
            $len += (64 - ($len % 64))
        }

        # Copy the bytes in the message to our byte array
        $bytes = ([byte[]]0 * $len)
        for($i = 0; $i -lt $message.Length; $i++){
            $bytes[$i] = [byte]$message[$i]
        }

        # Pad the message with 1000 0000
        $bytes[$i] = 128

        # The message length in bits
        $bitLen = $message.Length * 8

        # Set the last [uint64] as the messsage length. (We only do 32 bits)
        $bytes[$len-1] = [byte]($bitLen -band 0xFF)
        $bytes[$len-2] = [byte](($bitLen -shr 8) -band 0xFF)
        $bytes[$len-3] = [byte](($bitLen -shr 16) -band 0xFF)
        $bytes[$len-4] = [byte](($bitLen -shr 24) -band 0xFF)

        # Divide the message into 512 bit chunks
        for($chunk = 0; $chunk -lt $bytes.Length; $chunk += 64)
        {
            $w = ([uint64[]]0 * 80)

            # Copy the chunk into our working array as uints
            for($i = 0; $i -lt 16; $i++){
                $w[$i] = GetBigEndianUInt -bytes $bytes -index ($i*4 + $chunk)
            }

            for($i = 16; $i -lt 80; $i++){
                $w[$i] = LeftRotate -val (TUI ($w[$i-3] -bxor $w[$i-8] -bxor $w[$i-14] -bxor $w[$i-16])) -amount 1
            }

            $a = TUI $h0
            $b = TUI $h1
            $c = TUI $h2
            $d = TUI $h3
            $e = TUI $h4

            # A bunch of crazy stuff
            for($i = 0; $i -lt 80; $i++){
                $k=0
                if($i -lt 20){
                    $f = TUI (($b -band $c) -bor ((-bnot $b) -band $d))
                    $k = TUI 0x5A827999
                }
                elseif($i -lt 40){
                    $f = TUI ($b -bxor $c -bxor $d)
                    $k = TUI 0x6ED9EBA1
                }
                elseif($i -lt 60){
                    $f = TUI (($b -band $c) -bor ($b -band $d) -bor ($c -band $d))
                    $k = TUI 0x8F1BBCDC
                }
                else{
                    $f = TUI ($b -bxor $c -bxor $d)
                    $k = TUI 0xCA62C1D6
                }
            
                $temp = TUI ((LeftRotate -val $a -amount 5) + $f + $e + $k + $w[$i])

                $e = $d
                $d = $c
                $c = LeftRotate -val $b -amount 30
                $b = $a
                $a = $temp
            }

            $h0 = TUI ($h0 + $a)
            $h1 = TUI ($h1 + $b)
            $h2 = TUI ($h2 + $c)
            $h3 = TUI ($h3 + $d)
            $h4 = TUI ($h4 + $e)   
        }

        '{0:X8}{1:X8}{2:X8}{3:X8}{4:X8}' -f $h0, $h1, $h2, $h3, $h4
    }
}
                            
                        
There it is. I may have overused my TUI(To uint) function a little bit, since values larger than 32 bits could actually cause problems. I got most of the implementation details from the wikipedia artical on SHA1.




Powershell Cim Tree

Sometimes you are just bored. And all you really want to do is list out all of the cim namespaces on your machine. Cool right? YEA. So here is a powershell script which produces output similar to the "tree" command, except for all of your cim namespaces and classes. This should help figure out what actually exists. This should probably be run as administrator, depends on which stuff you are trying to look at. Anyway, here it is!

                            
#
# Generate a tree from cim namespaces and classes
#
function Get-CimTree
{
    [CmdLetBinding()]
    param
    (
        [Parameter(Mandatory=$false, Position=0)][string]$NameSpace = 'Root',
        [switch]$IncludeClasses = $false
    )
    process
    {
        # Constants
        $tbend = '├'
        $cbend = '└'
        $pipe = '│'
        $flat = '─'
        $tabWidth = 4

        # Function to write a namespace. Takes the tabs stack, and the namespace name
        function writeNamespace($tabStack, $ns)
        {
            # Write the list of tabs
            function writeTabStack($ts)
            {
                $output = ''
                for($t = 0; $t -lt $ts.Length; $t++)
                {
                    $val = $ts[$t]
                    
                    # Transform previous tabs, to make our tree look more like a tree
                    if($t -lt $ts.Length - 1)
                    {
                        switch($val)
                        {
                            2 { $val = 1 }
                            3 { $val = 0 } 
                        }
                    }

                    # Get the starting and sperator characters for the tab
                    switch($val)
                    {
                        0 { $start = ' '; $sep = ' ' }
                        1 { $start = $pipe; $sep = ' ' }
                        2 { $start = $tbend; $sep = $flat }
                        3 { $start = $cbend; $sep = $flat }
                    }

                    # Add to the outptu line
                    $output += $start + ($sep * ($tabWidth - 1))
                }

                Write-Host $output -NoNewline
            }

            # Write the tab stack
            writeTabStack $tabStack

            # Write the namespace name
            Write-Host $ns

            $classes = @()

            # Get all of the classes in the namespace if we need them
            if($IncludeClasses)
            {
                $classes = Get-CimClass -Namespace $ns
            }

            # Get all of the namespaces in the current namespace
            $namespaces = Get-CimInstance -Namespace $ns -ClassName __NameSpace -ErrorAction Ignore

            # If the query failed, just continue
            if(-not $namespaces)
            {
                return
            }
           
            for($i = 0; $i -lt $namespaces.Length; $i++)
            {
                # If it's the last element, use an angled branch
                $tsAdd = 2
                if($i -eq $namespaces.Length -1 -and $classes.Length -eq 0)
                {
                    $tsAdd = 3
                }

                writeNamespace -tabStack ($tabStack + $tsAdd) -ns ($ns + '/' + $namespaces[$i].Name)
            }

            # Write all of the classes
            for($i = 0; $i -lt $classes.Length; $i++)
            {
                # If it's the last element, use an angled branch
                $tsAdd = 2
                if($i -eq $classes.Length - 1)
                {
                    $tsAdd = 3
                }

                writeTabStack ($tabStack + $tsAdd)
                Write-Host $classes[$i].CimClassName
            }
        }

        # Write the initial namespace to kick things off
        writeNamespace -tabStack @() -ns $NameSpace
    }
}
                            
                        
When you run this thing, you will get output in this form:

Root
├───Root/subscription
├───Root/DEFAULT
├───Root/CIMV2
│   ├───Root/CIMV2/mdm
│   │   ├───Root/CIMV2/mdm/dmmap
│   │   └───Root/CIMV2/mdm/MS_409
│   ├───Root/CIMV2/Security
│   │   ├───Root/CIMV2/Security/MicrosoftTpm
│   │   └───Root/CIMV2/Security/MicrosoftVolumeEncryption
│   ├───Root/CIMV2/power
│   ├───Root/CIMV2/ms_409
│   ├───Root/CIMV2/TerminalServices
│   └───Root/CIMV2/Applications
├───Root/msdtc
├───Root/Cli
├───Root/SECURITY
├───Root/HyperVCluster
├───Root/SecurityCenter2
├───Root/RSOP
│   └───Root/RSOP/Computer
├───Root/PEH
├───Root/StandardCimv2
│   ├───Root/StandardCimv2/MS_409
│   └───Root/StandardCimv2/embedded
├───Root/WMI
├───Root/directory
├───Root/Policy
├───Root/virtualization
├───Root/Interop
├───Root/Hardware
├───Root/ServiceModel
├───Root/SecurityCenter
├───Root/Microsoft
│   ├───Root/Microsoft/HomeNet
│   ├───Root/Microsoft/protectionManagement
│   ├───Root/Microsoft/Windows
│   │   ├───Root/Microsoft/Windows/RemoteAccess
│   │   ├───Root/Microsoft/Windows/Dns
│   │   ├───Root/Microsoft/Windows/Powershellv3
│   │   ├───Root/Microsoft/Windows/Hgs
│   │   ├───Root/Microsoft/Windows/WindowsUpdate
│   │   ├───Root/Microsoft/Windows/DeviceGuard
│   │   ├───Root/Microsoft/Windows/TaskScheduler
│   │   ├───Root/Microsoft/Windows/DesiredStateConfigurationProxy
│   │   ├───Root/Microsoft/Windows/SmbWitness
│   │   ├───Root/Microsoft/Windows/Wdac
│   │   ├───Root/Microsoft/Windows/winrm
│   │   ├───Root/Microsoft/Windows/AppBackgroundTask
│   │   ├───Root/Microsoft/Windows/PS_MMAgent
│   │   ├───Root/Microsoft/Windows/Storage
│   │   │   ├───Root/Microsoft/Windows/Storage/PT
│   │   │   ├───Root/Microsoft/Windows/Storage/MS_409
│   │   │   └───Root/Microsoft/Windows/Storage/Providers_v2
│   │   ├───Root/Microsoft/Windows/HardwareManagement
│   │   ├───Root/Microsoft/Windows/SMB
│   │   ├───Root/Microsoft/Windows/EventTracingManagement
│   │   ├───Root/Microsoft/Windows/DesiredStateConfiguration
│   │   ├───Root/Microsoft/Windows/CI
│   │   └───Root/Microsoft/Windows/Defender
│   ├───Root/Microsoft/SecurityClient
│   └───Root/Microsoft/Uev
├───Root/aspnet
└───Root/Appv

Gosh I know, the code is a total mess... That's because I am really hungry. You can also print out all of the classes, but the output gets HUGE. To be honest, the whole reason for this, is that I needed to figure out how to mount an iso to a folder from the command line, and I hate constantly searching online for these things. Its way easier to find out what is really going on when you can see the whole tree. Also trees are cool...




© 2017 - Peter Sulucz | disclaimer

log in