Home PowerShell Internals Debugger Hiding Code From ISE Debugger
 

Hiding Code From ISE Debugger

Today, let’s talk about an awesome secret feature built right into PowerShell V3 (it does not work in Powershell V2). I’ve been looking for this for years, and this morning I stumbled across it. This secret feature allows script authors to hide code from the ISE debugger.

So when you apply it to a function you wrote, then suddenly the function behaves like a cmdlet or external command. During debugging, the debugger will no longer step through this function (of course, the function still executes).

Why Hiding Code From The Debugger Is Important

When you write PowerShell code just for yourself, then hiding code from the debugger isn’t particularly exciting. However, the moment you start writing code for other people, it becomes a crucial thing. Let’s assume you write a couple of functions for your colleagues. Let’s even assume you wrap them nicely in a PowerShell module. Now your colleagues or customers can use your functions or module just like any other cmdlet or command. Great!

However, eventually your colleagues or customers will start using your functions inside their own scripts. And as their own scripts grow, they will at some point start debugging their code. So they set a breakpoint and start stepping through the code by pressing F11.

At this point, it suddenly becomes evident that your functions are made of PowerShell code and not compiled: the debugger will not differentiate between the code your colleagues or customers wrote, and the code found in your own extension functions or modules. To the debugger, it is all the same: a bunch of PowerShell code.

As a result, the debugger will happily jump into your function or module code and step through it. This exposes all of your code to the customer. Even worse, it can be severely confusing when the customer suddenly sees code that wasn’t part of his script.

How To Hide Code From The Debugger

Now let’s check out how you can shield code from the debugger, effectively distingushing private from public code. Here’s a piece of simple code to start with:


function test {
    param($a)

    Get-Service

}

“Hello”
test
“Hurray”

Save this code, then set a breakpoint in the line “Hello”. Huh? You can set a breakpoint in the ISE editor by clicking the line, then pressing F9. Remember that this requires the code to be saved. Unsaved scripts cannot be debugged. Now run the code, for example, by pressing F5. The code execution is halted in the line with the breakpoint. Now step through the code by repeatedly pressing F11. As you see, the debugger will step through the entire code, including the function test and its internal code. Now, to mark the function test as “private” and not visible to the debugger, simply add a secret attribute. Here’s the adjusted code:

function test {
    [System.Diagnostics.DebuggerHidden()]
    param($a)

    Get-Service

}

“Hello”
test
“Hurray”

Repeat the steps, and check out how the debugger treats this code. As you discover, the debugger now ignores the internals of the function test. The function test is still executed, but the debugger will no longer step through it.

Conclusions

Adding the attribute [System.Diagnostics.DebuggerHidden()] to a function marks the function content as hidden. You can apply this attribute to as many functions as you like. When you publish your own PowerShell modules, you may want to apply it to all function inside your module, so end users will no longer step into your code.

In fact, there are two more attributes as Jason from MSFT pointed out: DebuggerStepThrough DebuggerNonUserCode They both are equivalent and also prevent the debugger from stepping into.

The difference is when you set a breakpoint inside a function decorated with any of these. The latter two attributes will allow you to continue stepping in a hidden function.

DebuggerHidden in contrast will stop at the breakpoint but not step through the remainder of the function. This is truly awesome. These attributes won’t prevent curious end users from spying on your code, but it does make sure end users get a good debugging experience, and won’t accidentally step into your own code.

Note that PowerShell V2 will not honor the attribut. Note also that the attribute is not so secret in the c# arena. Googling “[System.Diagnostics.DebuggerHidden()]” powershell however yielded – nothing…!

Ah, and Steve Murawski, another Powershell MVP friend who likes shotgun approaches, after reading about this, added a clever logic to his modules that sets these attributes dynamically:

He used Get-ChildItem with the function: drive to enumerate all functions, then used Set-Item to add the attribute to the beginning code of all functions. This way, you can even set a flag to turn debugger hiding on or off, depending on your needs. Ok, that’s it for today, hope to see you soon!

And as always, if you live in Europe and would like to set up a great PowerShell training, contact me: tobias.weltner(AT)email.de. I do PowerShell trainings all the time, beginners and advanced. Would love to bring you and your team up to speed as well! Best regards, Tobias Microsoft MVP Windows PowerShell