---
title: "PowerShell Form Menu: Quick Access to Your Scripts"
description: "Create a handy PowerShell form menu in your system tray to kick off scripts with ease and improve your workflow."
canonical: "https://adamtheautomator.com/powershell-form/"
---

# PowerShell Form Menu: Quick Access to Your Scripts

> Create a handy PowerShell form menu in your system tray to kick off scripts with ease and improve your workflow.

Source: https://adamtheautomator.com/powershell-form/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![PowerShell Form Menu: Quick Access to Your Scripts](https://adamtheautomator.com/wp-content/uploads/2019/10/operating-system-1995430_1280.png)

# PowerShell Form Menu: Quick Access to Your Scripts

[![](https://secure.gravatar.com/avatar/06ab692f58fa7256ef14d0c099c863b6a12b9692e9c117a0337cc6468f359714?s=192&d=mm&r=g)Nathan Kasco](https://adamtheautomator.com/author/nate/)3 October 20197 min. read

Categories: [IT Ops](/category/it-ops/)

Tags:[PowerShell](/tag/powershell/)

Table of Contents

*   [Environment and Knowledge Requirements](#environment-and-knowledge-requirements)
*   [Show/Hide Console Window](#show-hide-console-window)
*   [Create Menu Options](#create-menu-options)
*   [Creating A Launcher Form](#creating-a-launcher-form)
*   [Show the Launcher Form](#show-the-launcher-form)
*   [Your Takeaways](#your-takeaways)

It’s weekend project time again and today you will learn how to build a lightweight system tray PowerShell form menu where you can quickly and easily launch your most coveted PowerShell scripts. You can see below the end result.

![Launching PowerShell scripts via a system tray menu icon](/wp-content/uploads/2019/10/image1.gif)

Launching PowerShell scripts via a system tray menu icon

In this article, you’ll learn how to build your own [PowerShell menu](https://adamtheautomator.com/powershell-form/ "PowerShell menu") GUI by breaking the process down step-by-step.

## Environment and Knowledge Requirements

Before you dive in, please be sure you meet the following minimum requirements:

*   Windows 7 or later
*   Windows PowerShell 3 or later – The latest version of .NET Core 3.0 with PowerShell 7 preview may work on Windows due to [recent added support for WPF and WinForm](https://devblogs.microsoft.com/powershell/powershell-7-road-map/), but is untested.
*   .NET Framework 4.5 or later
*   A familiarity with [Windows Forms (WinForms)](https://docs.microsoft.com/en-us/dotnet/desktop/winforms/?view=netframeworkdesktop-4.8/)  You can, however, due this with [WPF](https://docs.microsoft.com/en-us/visualstudio/designers/getting-started-with-wpf?view=vs-2019) too though.

For this project, the good news is that you won’t really need to rely on [Visual Studio](https://visualstudio.microsoft.com/), [PoshGUI](https://poshgui.com/), or any other UI development tool as the primary components that this project will rely on the following:

*   [NotifyIcon](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.notifyicon?view=netframework-4.8) – This will represent our customizable system tray icon for the user to interact with.
*   [ContextMenu](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.contextmenu?view=netframework-4.8) – Container for when the user right-clicks on the tray icon.
*   [MenuItem](https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.menuitem?view=netframework-4.8) – Individual objects for each option within the right-click menu.

Open up your favorite PowerShell script editor and let’s get started!

For this project you are going to build three functions: two functions to show/hide the console to provide a cleaner user experience and one to add items to your systray menu. These functions will serve as a foundation for later use to make your life much easier as you will learn a bit later in this article.

## Show/Hide Console Window

Unless hidden, when you launch a PowerShell script, the familiar PowerShell console will come up. Since the menu items in the PowerShell form you’ll create will launch scripts, you should ensure the console doesn’t up. You just want it to execute.

When a script is executed, you can toggle the [PowerShell](https://adamtheautomator.com/tag/powershell/) console window showing or not using a little .NET.

First add the _Window_ .NET type into the current session. To do this, you’ll use some C# as you’ll see below. The two methods you need to load into context are [GetConsoleWindow](https://docs.microsoft.com/en-us/windows/console/getconsolewindow) and [ShowWindow](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow). By loading these DLLs into memory you are exposing certain parts of the API, this allows you to use them in the context of your PowerShell script:

```powershell
 #Load dlls into context of the current console session
 Add-Type -Name Window -Namespace Console -MemberDefinition '
    [DllImport("Kernel32.dll")]
    public static extern IntPtr GetConsoleWindow();
 
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
```

Create two functions using the loaded above using the `GetConsoleWindow()` and `ShowWindow()` method as shown below.

```powershell
 function Start-ShowConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 5)
 }
 
 function Start-HideConsole {
    $PSConsole = [Console.Window]::GetConsoleWindow()
    [Console.Window]::ShowWindow($PSConsole, 0)
 }
```

With these two functions you now have created a way in which you can show or hide the console window at will.

> _Note: If you’d like to see output from the scripts executed via the menu, you can use [PowerShell transcripts](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.host/start-transcript) or other text-based logging features. This allows you to maintain control versus only running the PowerShell session with the WindowStyle parameter to hide._

Now begin building script code by calling `Start-HideConsole`. When the PowerShell form menu-driven script executes, this will ensure the PowerShell console window doesn’t come up.

```powershell
<# 
	Initialization of functions and objects loading into memory
	Display a text-based loading bar or Write-Progress to the host
#>
 
Start-HideConsole
 
<# 
	Code to display your form/systray icon
	This will hold the console here until closed
 #>
```

## Create Menu Options

Now it’s time to create the menu options. Ensuring you can easily create new options later on, create another function this time called `New-MenuItem`. When you call this function, it will create a new MenuItem .NET object which you can then add to the menu later.

Each menu option will launch another script or exit the launcher. To accommodate for this functionality,  the `New-MenuItem` function has three parameters:

*   `Text` – The label the user will click on
*   `MyScriptPath` – The path to the PowerShell script to execute
*   `ExitOnly` – The option to exit the launcher.

Add the below function snippet to the menu script.

```powershell
 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
```

Continuing on building the `New-MenuItem` function, create a MenuItem object by assigning it to a variable.

```powershell
 #Initialization
 $MenuItem = New-Object System.Windows.Forms.MenuItem
```

Next, assign the text label to the menu item.

```powershell
 # Apply desired text
 if($Text) {
 	$MenuItem.Text = $Text
 }
```

Now add a custom property to the MenuItem called `MyScriptPath`. This path will be called upon when the item is clicked in the menu.

```powershell
 #Apply click event logic
 if($MyScriptPath -and !$ExitOnly){
 	$MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
```

Add a click event to the MenuItem that launches the desired script. Start-Process provides a clean way to do this within a [try/catch](https://adamtheautomator.com/powershell-try-catch/ "try/catch") block so that you can make sure any errors launching the script (such as PowerShell not being available or the script not existing at the provided path) fall to your catch block.

```powershell
   $MenuItem.Add_Click({
        try{
            $MyScriptPath = $This.MyScriptPath #Used to find proper path during click event
            
            if(Test-Path $MyScriptPath){
                Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
            } else {
                throw "Could not find at path: $MyScriptPath"
            }
        } catch {
          $Text = $This.Text
          [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
        }
  })
```

Sdd the remaining logic to provide an exit condition for the launcher followed by returning your newly created MenuItem back to be assigned to another variable at runtime.

```powershell
    #Provide a way to exit the launcher
    if($ExitOnly -and !$MyScriptPath){
        $MenuItem.Add_Click({
            $Form.Close()
    
            #Handle any hung processes
            Stop-Process $PID
        })
    }
 
 	 #Return our new MenuItem
    $MenuItem
 }
```

You should now have the `New-MenuItem` function created! The final function should look like this:

```powershell
 function New-MenuItem{
     param(
         [string]
         $Text = "Placeholder Text",
 
         $MyScriptPath,
         
         [switch]
         $ExitOnly = $false
     )
 
     #Initialization
     $MenuItem = New-Object System.Windows.Forms.MenuItem
 
     #Apply desired text
     if($Text){
         $MenuItem.Text = $Text
     }
 
     #Apply click event logic
     if($MyScriptPath -and !$ExitOnly){
         $MenuItem | Add-Member -Name MyScriptPath -Value $MyScriptPath -MemberType NoteProperty
     }
 
     $MenuItem.Add_Click({
             try{
                 $MyScriptPath = $This.MyScriptPath #Used to find proper path during click event
             
                 if(Test-Path $MyScriptPath){
                     Start-Process -FilePath "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -ArgumentList "-NoProfile -NoLogo -ExecutionPolicy Bypass -File `"$MyScriptPath`"" -ErrorAction Stop
                 } else {
                     throw "Could not find at path: $MyScriptPath"
                 }
             } catch {
                 $Text = $This.Text
                 [System.Windows.Forms.MessageBox]::Show("Failed to launch $Text`n`n$_") > $null
             }
         })
 
     #Provide a way to exit the launcher
     if($ExitOnly -and !$MyScriptPath){
         $MenuItem.Add_Click({
                 $Form.Close()
    
                 #Handle any hung processes
                 Stop-Process $PID
             })
     }
 
     #Return our new MenuItem
     $MenuItem
 }
```

Test the `New-MenuItem` function by copying and pasting the above code into your PowerShell console and running the function providing some fake parameter values. You’ll see that a .NET MenuItem object is returned.

```powershell
 PS51> (New-MenuItem -Text "Test" -MyScriptPath "C:\test.ps1").GetType()
 
 IsPublic IsSerial Name                                     BaseType
 -------- -------- ----                                     --------
 True     False    MenuItem                                 System.Windows.Forms.Menu
```

## Creating A Launcher Form

> _Want more tips like this? Check out my personal PowerShell blog at: [https://nkasco.com/FriendsOfATA](https://www.nkasco.com/FriendsOfATA)_

Now that you can easily create new menu items, it’s time to create a system tray launcher which will display the menu.

Create a basic form object to add components to. This doesn’t need to be anything fancy as it will be hidden to the end user and will keep the console running in the background as well.

```powershell
 #Create Form to serve as a container for our components
 $Form = New-Object System.Windows.Forms.Form
 ​
 #Configure our form to be hidden
 $Form.BackColor = "Magenta" #Match this color to the TransparencyKey property for transparency to your form
 $Form.TransparencyKey = "Magenta"
 $Form.ShowInTaskbar = $false
 $Form.FormBorderStyle = "None"
```

Next, create the icon that will show up in the system tray. Below I’ve chosen to use the PowerShell icon. At runtime, the below code creates an actual system tray icon. This icon can be customized to your liking by setting the `SystrayIcon` variable to your desired icon.

Check out the [documentation for the System.Drawing.Icon class](https://docs.microsoft.com/en-us/dotnet/api/system.drawing.icon?view=netframework-4.8) to see other methods in which you can load an icon into memory.

```powershell
 #Initialize/configure necessary components
 $SystrayLauncher = New-Object System.Windows.Forms.NotifyIcon
 $SystrayIcon = [System.Drawing.Icon]::ExtractAssociatedIcon("C:\windows\system32\WindowsPowerShell\v1.0\powershell.exe")
 $SystrayLauncher.Icon = $SystrayIcon
 $SystrayLauncher.Text = "PowerShell Launcher"
 $SystrayLauncher.Visible = $true
```

When the script is run, you should then see a PowerShell icon show up in your system tray as you can see below.

Now, create a container for your menu items with a new ContextMenu object and create all of your menu items. For this example, the menu will have two scripts to run and an exit option.

```powershell
 $ContextMenu = New-Object System.Windows.Forms.ContextMenu
 ​
 $LoggedOnUser = New-MenuItem -Text "Get Logged On User" -MyScriptPath "C:\scripts\GetLoggedOn.ps1"
 $RestartRemoteComputer = New-MenuItem -Text "Restart Remote PC" -MyScriptPath "C:\scripts\restartpc.ps1"
 $ExitLauncher = New-MenuItem -Text "Exit" -ExitOnly
```

Next, add all of the menu items just created to the context menu. This will ensure each menu option shows up in the form context menu.

```powershell
 #Add menu items to context menu
 $ContextMenu.MenuItems.AddRange($LoggedOnUser)
 $ContextMenu.MenuItems.AddRange($RestartRemoteComputer)
 $ContextMenu.MenuItems.AddRange($ExitLauncher)
 ​
 #Add components to our form
 $SystrayLauncher.ContextMenu = $ContextMenu
```

## Show the Launcher Form

Now that the form is complete, the last thing to do is to show it while ensuring the PowerShell console window doesn’t come up. Do this by using your `Start-HideConsole` , displaying the launcher form and then showing the console again with`Start-ShowConsole` to prevent a hung _powershell.exe_ process.

```powershell
#Launch
Start-HideConsole
$Form.ShowDialog() > $null
Start-ShowConsole
```

> _Want more tips like this? Check out my personal PowerShell blog at: [https://nkasco.com/FriendsOfATA](https://nkasco.com/FriendsOfATA)_

The full code in its entirety can be found here: [https://github.com/nkasco/PSSystrayLauncher](https://github.com/nkasco/PSSystrayLauncher)

## Your Takeaways

Congrats, you’ve finished this project! In this article you learned:

1.  How to expose components of the Windows API.
2.  How to work with context menus via WinForms and add subsequent menu items.
3.  How to create a system tray icon in PowerShell.

This Project should give you enough understanding and experience to create your own systray menu for your PowerShell scripts!

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-form%2F&text=PowerShell%20Form%20Menu%3A%20Quick%20Access%20to%20Your%20Scripts)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-form%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fpowershell-form%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/2026/06/26995-troubleshoot-dns-issues-powershell-codex.webp)

### [Troubleshoot DNS Issues with PowerShell](/troubleshoot-dns-issues-powershell/)

Troubleshoot DNS issues with PowerShell by testing name resolution, DNS client settings, cache entries, and network connectivity in a repeatable workflow.

![](https://adamtheautomator.com/wp-content/uploads/2025/10/image_2025-10-24_095322075.png)

### [Migrating from PowerShell 6 to 7.5: Breaking Changes/New Features](/migrating-powershell-6-to-7-5/)

Migrate from PowerShell Core 6 to 7.5: breaking changes, features, and testing tips.

![](https://adamtheautomator.com/wp-content/uploads/2025/06/featured-image-6.png)

### [How to Add Timeouts to Pester Tests with PowerShell Runspaces](/pester-test-timeout-runspaces/)

Prevent Pester tests from hanging indefinitely using PowerShell runspaces. Learn to handle variable scoping, module loading, TestDrive access, and stream capture challenges with timeout protection.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
