-
Notifications
You must be signed in to change notification settings - Fork 160
Basic PowerShell tricks and notes Part 5
The following PowerShell series is designed for newcomers to PowerShell who want to quickly learn the essential basics, the most frequently used syntaxes, elements and tricks. It can also be used by advanced users as a quick reference or those who want to sharpen their skills.
The main source for learning PowerShell is Microsoft Learn websites. There are extensive and complete guides about each command/cmdlet with examples.
PowerShell core at Microsoft Learn
You can also use the Windows Copilot for asking any PowerShell related questions, code examples etc.
This is part 5 of this series, find other parts here:
When you are working with .NET methods in PowerShell, you should never use relative paths. Always use the full path to the file or directory. The reason is that the following command:
[System.Environment]::CurrentDirectory
Always remembers the first directory the PowerShell instance was started in. If you use cd
or Set-Location
to change the current working directory, it will not change that environment variable, which is what .NET methods use when you pass in a relative path such as .\file.txt
. That means .NET methods always consider that environment variable when you pass in a relative path from PowerShell, not the current working directory in PowerShell.
If you download your PowerShell scripts or module files from a Low Integrity source, such as a sandboxed browser, they will be deemed Untrusted. These files will possess Mark Of The Web (MotW) Zone Identifiers, marking them as such. Consequently, you must unblock them before utilization.
Failure to unblock these files, thereby removing their MotW designation, can result in complications and errors within PowerShell. For instance, they may generate errors such as AuthorizationManager check failed
, a situation particularly prevalent when incorporating C# code in PowerShell via Add-Type
.
Another issue arising from executing PowerShell files from Untrusted sources is the necessity for a more permissive execution policy such as Bypass
; otherwise, you will encounter incessant prompts for confirmation.
Indeed, you can execute PowerShell cmdlets within C#
directly inside PowerShell. By leveraging Add-Type
, you can seamlessly integrate C#
code into PowerShell, enabling it to run PowerShell cmdlets. This can be particularly useful in various scenarios, so here is an illustrative example.
Consider the following code snippet, which demonstrates how to create a PowerShell instance:
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.AddScript("Write-Verbose 'Hello World!'");
var results = powerShell.Invoke();
}
The version of the PowerShell instance created will correspond to the version in which you run the C#
code via Add-Type
. For instance, if you execute the C#
code within Windows PowerShell, the .Create()
method will instantiate a PowerShell instance using the Windows PowerShell assemblies. Conversely, if you execute the same code within PowerShell Core (pwsh.exe
), it will instantiate a PowerShell Core instance.
This behavior ensures that your PowerShell instance is consistent with the environment in which your C#
code is executed, providing seamless integration and execution across different PowerShell versions.
The Compiled option in Regex is beneficial when you need to reuse the same pattern multiple times, especially within loops. This option improves performance by compiling the regex pattern into a more efficient, executable form. Here, we'll explore the technical advantages and provide a practical example in PowerShell to demonstrate its efficacy.
When working with regular expressions in tight loops, the overhead of interpreting the pattern each time can significantly impact performance. The Compiled option mitigates this by converting the regex pattern into an intermediate language, which the .NET runtime can execute more swiftly.
-
Pattern Reuse: Utilize the Compiled option when the same regex pattern is used repeatedly.
-
Defined Outside Loops: Ensure the regex pattern is defined outside the loop.
$Pattern = [regex]::new('Insert Regex Pattern', [System.Text.RegularExpressions.RegexOptions]::Compiled)
$Pattern.IsMatch($Data)
Use the following command to determine the .NET version of the current PowerShell core instance:
[System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
If you install or uninstall a PSReadLine version, totally exit the Terminal or VS Code if using the IDE. That's one of those modules that can't be unloaded normally.
You need to first have the following DLL files in one directory:
-
Microsoft.Toolkit.Uwp.Notifications.dll
-
found in
microsoft.toolkit.uwp.notifications.7.1.3\lib\net5.0-windows10.0.17763\Microsoft.Toolkit.Uwp.Notifications.dll
-
Microsoft.Win32.SystemEvents.dll
-
found in
microsoft.win32.systemevents.8.0.0\lib\net8.0\Microsoft.Win32.SystemEvents.dll
-
Microsoft.Windows.SDK.NET.dll
-
found in
microsoft.windows.sdk.net.ref.10.0.26100.42\lib\net8.0\Microsoft.Windows.SDK.NET.dll
-
WinRT.Runtime.dll
-
found in
microsoft.windows.sdk.net.ref.10.0.26100.42\lib\net8.0\WinRT.Runtime.dll
-
System.Drawing.Common.dll
-
found in
system.drawing.common.8.0.8\lib\net8.0\System.Drawing.Common.dll
Then you can use the following PowerShell code to natively display the toast notifications
Note
Change D:\notifications
to the correct folder in your computer where the DLLs exist.
# Load the required assemblies
Add-Type -Path 'D:\notifications\Microsoft.Toolkit.Uwp.Notifications.dll'
Add-Type -Path 'D:\notifications\System.Drawing.Common.dll'
# Create an instance of the ToastContentBuilder class
$toastContentBuilderType = [Type]::GetType('Microsoft.Toolkit.Uwp.Notifications.ToastContentBuilder, Microsoft.Toolkit.Uwp.Notifications')
$toastContentBuilder = [Activator]::CreateInstance($toastContentBuilderType)
# Add text elements
$toastContentBuilder.AddText('Main Notification Title') | Out-Null
$toastContentBuilder.AddText('This is the first line of content with summary details.') | Out-Null
$toastContentBuilder.AddText('Additional line of content.') | Out-Null
# Add Attribution Text
$toastContentBuilder.AddAttributionText('Brought to you by Your Company') | Out-Null
# Add Header
$toastContentBuilder.AddHeader('6289', 'Camping!!', 'action=openConversation&id=6289') | Out-Null
# Add Hero Image
$heroImagePath = 'D:\notifications\2.jpg'
$toastContentBuilder.AddHeroImage([Uri]::new($heroImagePath)) | Out-Null
# Add Inline Image
$inlineImagePath = 'D:\notifications\1.jpg'
$toastContentBuilder.AddInlineImage([Uri]::new($inlineImagePath)) | Out-Null
# Show the notification
$toastContentBuilder.Show() | Out-Null
You can also use the following C# code in PowerShell to do the same
[System.String[]]$Assemblies = @(
'D:\notifications\Microsoft.Toolkit.Uwp.Notifications.dll',
'D:\notifications\Microsoft.Win32.SystemEvents.dll',
'D:\notifications\Microsoft.Windows.SDK.NET.dll',
'D:\notifications\System.Drawing.Common.dll',
'D:\notifications\WinRT.Runtime.dll'
)
Add-Type -TypeDefinition @'
using System;
using Microsoft.Toolkit.Uwp.Notifications;
public class Notification {
public static void ShowNotif()
{
new ToastContentBuilder()
.AddAppLogoOverride(new Uri("file:///D:/notifications/2.jpg"), ToastGenericAppLogoCrop.Circle)
.AddText("Main Notification Title")
.AddText("This is the first line of content with summary details.")
.AddText("Main Notification Title")
.AddHeroImage(new Uri("file:///D:/notifications/1.jpg"))
.AddInlineImage(new Uri("file:///D:/notifications/3.jpg"))
.AddButton(new ToastButton()
.SetContent("View Details")
.AddArgument("action", "viewDetails")
.SetImageUri(new Uri("file:///D:/notifications/view_icon.jpg")))
.AddButton(new ToastButton()
.SetContent("Dismiss")
.AddArgument("action", "dismiss")
.SetImageUri(new Uri("file:///D:/notifications/view_icon.jpg")))
.AddButton(new ToastButton()
.SetContent("Open App")
.AddArgument("action", "openApp")
.SetImageUri(new Uri("file:///D:/notifications/view_icon.jpg")))
.AddButton(new ToastButton()
.SetContent("Open App")
.AddArgument("action", "openApp")
.SetImageUri(new Uri("file:///D:/notifications/view_icon.jpg")))
.AddButton(new ToastButton()
.SetContent("Open App")
.AddArgument("action", "openApp")
.SetImageUri(new Uri("file:///D:/notifications/view_icon.jpg")))
.AddAudio(new Uri("ms-winsoundevent:Notification.SMS"))
.AddAttributionText("Brought to you by Your Company")
.AddHeader("6289", "Camping!!", "action=openConversation&id=6289")
.Show();
}
}
'@ -ReferencedAssemblies $Assemblies -CompilerOptions '/nowarn:1701'
$Assemblies | ForEach-Object {Add-Type -Path $_}
[Notification]::ShowNotif()
In PowerShell, New
is reserved for constructors, and constructors have specific handling. When you call a method such as [NameSpace.Class]::New($SomeArgument)
in PowerShell, it's looking for a constructor with the given argument, not a static method named New
, so that will fail and throw an error.
This is not a problem if you want to use the C# method called New
in C# itself and not import it via Add-Type
in PowerShell.
Let's say you have a C# method that accepts a nullable string
using System;
#nullable enable
public class MyClass
{
public static void MethodName(string? Path)
{
if (Path is not null)
{
Console.WriteLine("Path is not null");
}
else
{
Console.WriteLine("Path is null");
}
}
}
In C# itself, you can call that method normally and pass null to it when calling it
MyClass.MethodName(null)
And you will see Path is null
on the console.
However, in PowerShell, if you used Add-Type
to import that method and called it like this
[MyClass]MethodName($null)
You will see Path is not null
on the console.
The reason is that powerShell converts $null
to an empty string ""
and not the true null
. So the C# method will get ""
and since it's an empty string and not an actual null, it will fail the null check.
In this situation you have at least 2 options:
-
In PowerShell, instead of passing
$null
, use[NullString]::Value
:[MyClass]::MethodName([NullString]::Value)
. -
In C#, instead of checking for null, check for
IsNullOrEmpty
orIsNullOrWhiteSpace
.
- Create AppControl Policy
- Create Supplemental Policy
- System Information
- Configure Policy Rule Options
- Simulation
- Allow New Apps
- Build New Certificate
- Create Policy From Event Logs
- Create Policy From MDE Advanced Hunting
- Merge App Control Policies
- Deploy App Control Policy
- Get Code Integrity Hashes
- Get Secure Policy Settings
- Update
- Introduction
- App Control for Lightly Managed Devices
- App Control for Fully managed device - Variant 1
- App Control for Fully managed device - Variant 2
- App Control for Fully managed device - Variant 3
- App Control for Fully managed device - Variant 4
- App Control Notes
- How to Create and Deploy a Signed App Control Policy
- Fast and Automatic Microsoft Recommended Driver Block Rules updates
- App Control policy for BYOVD Kernel mode only protection
- EKUs in App Control for Business Policies
- App Control Rule Levels Comparison and Guide
- Script Enforcement and PowerShell Constrained Language Mode in App Control Policies
- How to Use Microsoft Defender for Endpoint Advanced Hunting With App Control
- App Control Frequently Asked Questions (FAQs)
- New-WDACConfig
- New-SupplementalWDACConfig
- Remove-WDACConfig
- Edit-WDACConfig
- Edit-SignedWDACConfig
- Deploy-SignedWDACConfig
- Confirm-WDACConfig
- New-DenyWDACConfig
- Set-CommonWDACConfig
- New-KernelModeWDACConfig
- Get-CommonWDACConfig
- Remove-CommonWDACConfig
- Assert-WDACConfigIntegrity
- Test-CiPolicy
- Get-CiFileHashes
- Get-CIPolicySetting
- Create Bootable USB flash drive with no 3rd party tools
- Event Viewer
- Group Policy
- How to compact your OS and free up extra space
- Hyper V
- Overrides for Microsoft Security Baseline
- Git GitHub Desktop and Mandatory ASLR
- Signed and Verified commits with GitHub desktop
- About TLS, DNS, Encryption and OPSEC concepts
- Things to do when clean installing Windows
- Comparison of security benchmarks
- BitLocker, TPM and Pluton | What Are They and How Do They Work
- How to Detect Changes in User and Local Machine Certificate Stores in Real Time Using PowerShell
- Cloning Personal and Enterprise Repositories Using GitHub Desktop
- Only a Small Portion of The Windows OS Security Apparatus
- Rethinking Trust: Advanced Security Measures for High‐Stakes Systems
- Clean Source principle, Azure and Privileged Access Workstations
- How to Securely Connect to Azure VMs and Use RDP
- Basic PowerShell tricks and notes
- Basic PowerShell tricks and notes Part 2
- Basic PowerShell tricks and notes Part 3
- Basic PowerShell tricks and notes Part 4
- Basic PowerShell tricks and notes Part 5
- How To Access All Stream Outputs From Thread Jobs In PowerShell In Real Time
- PowerShell Best Practices To Follow When Coding
- How To Asynchronously Access All Stream Outputs From Background Jobs In PowerShell
- Powershell Dynamic Parameters and How to Add Them to the Get‐Help Syntax
- RunSpaces In PowerShell
- How To Use Reflection And Prevent Using Internal & Private C# Methods in PowerShell