4 min read
Bulk Testing PowerShell Scripts with the Tokenizer
In Part 1, we explored the internal PowerShell parser to see how it analyzes code and breaks it down into individual...
Unlocking the Power of PowerShell: Tips for Success
In the previous two parts, we looked at a few .NET types and ways to “look inside” and find their hidden methods. This provided you with granular control, such as when rounding values.
Any .NET type follows these same rules, so now you’re ready to explore more with .NET types. In this part, we’ll look at GUI dialogs and case-correcting text.
When a script emits data, it typically surfaces as text in the console, which can be easily overlooked by the user.
If your script needs to catch attention, a message box dialog may be a more effective way to do so. Here’s how to invoke such a dialog:
# loading the required .NET type
Add-Type -AssemblyName PresentationFramework
$result = [System.Windows.MessageBox]::Show("I need to restart your server`r`n`r`nMay I proceed?", 'Attention', 'YesNo', 'Question')
if ($result -eq 'Yes')
{
Write-Warning 'I am restarting...'
}
else
{
Write-Warning 'Ok, I am aborting...'
}
The .NET type we are using this time is [System.Windows.MessageBox]. For the first time in this series, this is a type that is not used by PowerShell. It is still present in the .NET (on Windows), but it is not loaded by default.
That’s why you need to run Add-Type first and instruct it to load the assembly (DLL) that defines this type. If you forget this, PowerShell will complain that it cannot find the type.
Let’s once again look at the overload definitions for the Show() method, just like we did in the past two parts:
PS C:\> [System.Windows.MessageBox]::Show
OverloadDefinitions
-------------------
static System.Windows.MessageBoxResult Show(string messageBoxText, string caption, System.Windows.MessageBoxButton button, System.Windows.MessageBoxImage icon,
System.Windows.MessageBoxResult defaultResult, System.Windows.MessageBoxOptions options)
static System.Windows.MessageBoxResult Show(string messageBoxText, string caption, System.Windows.MessageBoxButton button, System.Windows.MessageBoxImage icon,
System.Windows.MessageBoxResult defaultResult)
static System.Windows.MessageBoxResult Show(string messageBoxText, string caption, System.Windows.MessageBoxButton button, System.Windows.MessageBoxImage icon)
static System.Windows.MessageBoxResult Show(string messageBoxText, string caption, System.Windows.MessageBoxButton button)
static System.Windows.MessageBoxResult Show(string messageBoxText, string caption)
static System.Windows.MessageBoxResult Show(string messageBoxText)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption, System.Windows.MessageBoxButton button,
System.Windows.MessageBoxImage icon, System.Windows.MessageBoxResult defaultResult, System.Windows.MessageBoxOptions options)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption, System.Windows.MessageBoxButton button,
System.Windows.MessageBoxImage icon, System.Windows.MessageBoxResult defaultResult)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption, System.Windows.MessageBoxButton button,
System.Windows.MessageBoxImage icon)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption, System.Windows.MessageBoxButton button)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption)
static System.Windows.MessageBoxResult Show(System.Windows.Window owner, string messageBoxText)
Don’t let the overload definitions overwhelm you—once again, they simply show different ways to call Show() to make your life easier.
What’s much more important is identifying the true type names for the arguments. In the example above, we submitted strings and let PowerShell automatically convert them into the appropriate types.
For example, we passed the string "Question" for the argument System.Windows.MessageBoxImage icon. As you know from the previous parts, the message box icon is supposed to be of type System.Windows.MessageBoxImage, not a string. We got away with using "Question" because this string can be converted to System.Windows.MessageBoxImage:
PS C:\> 'Question' -as [System.Windows.MessageBoxImage]
Question
The correct approach would have been to pass the System.Windows.MessageBoxImage type directly, like this:
[System.Windows.MessageBoxImage]::Question
This way, your PowerShell editor and its IntelliSense would have displayed all the other valid icon options. As explained in the previous parts, you can always list these options using Get-Member:
PS C:\> [System.Windows.MessageBoxImage] | Get-Member -Static
TypeName: System.Windows.MessageBoxImage
Name MemberType Definition
---- ---------- ----------
Equals Method static bool Equals(System.Object objA, System.Object objB)
Format Method static string Format(type enumType, System.Object value, string format)
GetName Method static string GetName(type enumType, System.Object value)
GetNames Method static string[] GetNames(type enumType)
GetUnderlyingType Method static type GetUnderlyingType(type enumType)
GetValues Method static array GetValues(type enumType)
IsDefined Method static bool IsDefined(type enumType, System.Object value)
Parse Method static System.Object Parse(type enumType, string value), static System.Object Parse(type enumType, string value, bool ignoreCase)
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
ToObject Method static System.Object ToObject(type enumType, System.Object value), static System.Object ToObject(type enumType, sbyte value), static System.Object...
TryParse Method static bool TryParse[TEnum](string value, [ref] TEnum result), static bool TryParse[TEnum](string value, bool ignoreCase, [ref] TEnum result)
Asterisk Property static System.Windows.MessageBoxImage Asterisk {get;}
Error Property static System.Windows.MessageBoxImage Error {get;}
Exclamation Property static System.Windows.MessageBoxImage Exclamation {get;}
Hand Property static System.Windows.MessageBoxImage Hand {get;}
Information Property static System.Windows.MessageBoxImage Information {get;}
None Property static System.Windows.MessageBoxImage None {get;}
Question Property static System.Windows.MessageBoxImage Question {get;}
Stop Property static System.Windows.MessageBoxImage Stop {get;}
Warning Property static System.Windows.MessageBoxImage Warning {get;}
Here is the example script from above, this time using the correct types instead of strings:
# loading the required .NET type
Add-Type -AssemblyName PresentationFramework
$Caption = 'Attention'
$Message = "I need to restart your server`r`n`r`nMay I proceed?"
$Buttons = [System.Windows.MessageBoxButton]::'YesNo'
$Icon = [System.Windows.MessageBoxImage]::Question
[System.Windows.MessageBoxResult]$result = [System.Windows.MessageBox]::Show($Message, $Caption, $Buttons, $Icon)
if ($result -eq [System.Windows.MessageBoxResult]::Yes)
{
Write-Warning 'I am restarting...'
}
else
{
Write-Warning 'Ok, I am aborting...'
}
This code is much easier to edit in your PowerShell editor because you can now easily see the available options for dialog buttons and icons.
In part 1, we explored the relationship between PowerShell cmdlets and .NET types. By now, you’ve seen that most cmdlets internally utilize existing .NET types and that you can call .NET types directly as well.
Now, we’ve reached the perfect point to illustrate why cmdlets exist and why using .NET methods directly isn’t always the best choice. The message box dialog works well but requires dealing with fairly complex code and unfamiliar types.
Let’s wrap a cmdlet around this code and see what happens. Of course, instead of a binary cmdlet wrapper, we’ll use a PowerShell function:
#region reusable code
Add-Type -AssemblyName PresentationFramework
function Show-MessageBox
{
param
(
[Parameter(Mandatory)]
[string]
$Message,
[string]
$Caption = 'Attention',
[System.Windows.MessageBoxButton]
$Buttons = [System.Windows.MessageBoxButton]::'YesNo',
[System.Windows.MessageBoxImage]
$Icon = [System.Windows.MessageBoxImage]::Question
)
return [System.Windows.MessageBox]::Show($Message, $Caption, $Buttons, $Icon)
}
#endregion reusable code
$result = Show-MessageBox -Message "I need to restart your server`r`n`r`nMay I proceed?"
if ($result -eq [System.Windows.MessageBoxResult]::Yes)
{
Write-Warning 'I am restarting...'
}
else
{
Write-Warning 'Ok, I am aborting...'
}
In the code region "reusable code," the new function Show-MessageBox is defined. It specifies the parameters with the correct types that the underlying .NET method requires.
In your script, you can now simply call Show-MessageBox whenever you need a dialog. What’s even better is that when you use parameters like -Buttons or -Icon, your PowerShell editor will display an IntelliSense list showing all the available options.
This is how most cmdlets work: they encapsulate complex .NET code so you can call a simple command. They define parameters using the exact types required by .NET, which provides you with convenient IntelliSense options.
Our little journey illustrated that knowing .NET types (and how to directly call their methods) provides a lot of additional control. It is perfectly fine to directly call .NET types provided (a) there is no cmdlet for the same purpose already, and (b) calling .NET directly does not involve complex types and code.
If it does, as in the last example with the message box, you now understand why cmdlets exist: they are not in competition with .NET. Instead, they are a great way to simplify the user experience when .NET becomes too complex.
Unleash the full potential of PowerShell with our handy poster. Whether you're a beginner or a seasoned pro, this cheat sheet is designed to be your go-to resource for the most important and commonly used cmdlets.
The poster is available for download and in paper form.
Apr 16, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
In Part 1, we explored the internal PowerShell parser to see how it analyzes code and breaks it down into individual...
Apr 11, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
The internal PowerShell parser processes any code before execution. With full access to this parser, you can analyze...
Apr 3, 2025 by Aleksandar Nikolić and Dr. Tobias Weltner
In part 3, we identified a useful .NET method to display system dialogs and then wrapped it inside a new PowerShell...
Tobias Weltner and Aleksandar Nikolić joinly wrote the blog post series 'Tobias&Aleksandar's PowerShell tips'. So we introduce both of them here:
----------------------------
Aleksandar Nikolić is a Microsoft Azure MVP and co-founder of PowerShellMagazine.com, the ultimate online source for PowerShell enthusiasts. With over 18 years of experience in system administration, he is a respected trainer and speaker who travels the globe to share his knowledge and skills on Azure, Entra, and PowerShell. He has spoken at IT events such as Microsoft Ignite, ESPC, NIC, CloudBrew, NTK, and PowerShell Conference Europe.
----------------------------
Tobias is a long-time Microsoft MVP and has been involved with the development of PowerShell since its early days. He invented the PowerShell IDE "ISESteroids", has written numerous books on PowerShell for Microsoft Press and O'Reilly, founded the PowerShell Conference EU (psconf.eu), and is currently contributing to the advancement of PowerShell as member in the "Microsoft Cmdlet Working Group". Tobias shares his expertise as a consultant in projects and as a trainer in in-house trainings for numerous companies and agencies across Europe.