Repository: dan-osull/PowerShell-Script-Menu-Gui Branch: master Commit: b30bc361bc84 Files: 17 Total size: 29.5 KB Directory structure: gitextract_c2swp2db/ ├── .gitignore ├── LICENSE ├── PSScriptMenuGui/ │ ├── PSScriptMenuGui.psd1 │ ├── PSScriptMenuGui.psm1 │ ├── examples/ │ │ ├── PSScriptMenuGui.ps1 │ │ ├── PSScriptMenuGui_all_options.ps1 │ │ ├── example_data.csv │ │ ├── example_target.cmd │ │ ├── example_target.ps1 │ │ └── example_text_file.txt │ ├── private/ │ │ └── functions.ps1 │ ├── public/ │ │ └── functions.ps1 │ └── xaml/ │ ├── end.xaml │ ├── heading.xaml │ ├── item.xaml │ └── start.xaml └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .vscode/ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Dan O'Sullivan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: PSScriptMenuGui/PSScriptMenuGui.psd1 ================================================ # # Module manifest for module 'PSScriptMenuGui' # # Generated by: Dan O'Sullivan # # Generated on: 10/7/2019 # @{ # Script module or binary module file associated with this manifest. RootModule = 'PSScriptMenuGui.psm1' # Version number of this module. ModuleVersion = '1.0.1' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module GUID = '103460b8-4f8b-471c-8f70-41e493070656' # Author of this module Author = "Dan O'Sullivan" # Company or vendor of this module # CompanyName = '' # Copyright statement for this module Copyright = "(c) Dan O'Sullivan. Released under the MIT License." # Description of the functionality provided by this module Description = 'Use a CSV file to make a graphical menu of PowerShell scripts. Easy to customise and fast to launch. You can also add Windows programs and files to the menu. See the Project Site on GitHub for full documentation.' # Minimum version of the PowerShell engine required by this module PowerShellVersion = '5.1' # Name of the PowerShell host required by this module # PowerShellHostName = '' # Minimum version of the PowerShell host required by this module # PowerShellHostVersion = '' # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # DotNetFrameworkVersion = '' # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # CLRVersion = '' # Processor architecture (None, X86, Amd64) required by this module # ProcessorArchitecture = '' # Modules that must be imported into the global environment prior to importing this module # RequiredModules = @() # Assemblies that must be loaded prior to importing this module # RequiredAssemblies = @() # Script files (.ps1) that are run in the caller's environment prior to importing this module. # ScriptsToProcess = @() # Type files (.ps1xml) to be loaded when importing this module # TypesToProcess = @() # Format files (.ps1xml) to be loaded when importing this module # FormatsToProcess = @() # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @( 'Show-ScriptMenuGui', 'New-ScriptMenuGuiExample' ) # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() # Variables to export from this module VariablesToExport = '*' # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. AliasesToExport = @() # DSC resources to export from this module # DscResourcesToExport = @() # List of all modules packaged with this module # ModuleList = @() # List of all files packaged with this module # FileList = @() # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. PrivateData = @{ PSData = @{ # Tags applied to this module. These help with module discovery in online galleries. Tags = @('PSEdition_Desktop','PSEdition_Core','Windows') # A URL to the license for this module. LicenseUri = 'https://github.com/weebsnore/PowerShell-Script-Menu-Gui/blob/master/LICENSE' # A URL to the main website for this project. ProjectUri = 'https://github.com/weebsnore/PowerShell-Script-Menu-Gui' # A URL to an icon representing this module. # IconUri = '' # ReleaseNotes of this module # ReleaseNotes = '' # Prerelease string of this module # Prerelease = '' # Flag to indicate whether the module requires explicit user acceptance for install/update/save RequireLicenseAcceptance = $false # External dependent modules of this module # ExternalModuleDependencies = @() } # End of PSData hashtable } # End of PrivateData hashtable # HelpInfo URI of this module # HelpInfoURI = '' # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # DefaultCommandPrefix = '' } ================================================ FILE: PSScriptMenuGui/PSScriptMenuGui.psm1 ================================================ if ($PSEdition -eq 'Core') { if (-not $IsWindows) { throw 'This module only runs on Windows' } if ($PSVersionTable.PSVersion.Major -eq 6) { throw 'This module is not compatible with PowerShell Core 6' } } # Get public and private function definition files # Based on: https://github.com/RamblingCookieMonster/PSStackExchange/blob/db1277453374cb16684b35cf93a8f5c97288c41f/PSStackExchange/PSStackExchange.psm1 $scripts = @() $scripts += Get-ChildItem -Path $PSScriptRoot\public\*.ps1 -ErrorAction SilentlyContinue $scripts += Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -ErrorAction SilentlyContinue # Dot source the files ForEach ($script in $scripts) { try { . $script.FullName } catch { throw } } # Used to get files from xaml and examples subfolders $moduleRoot = $PSScriptRoot ================================================ FILE: PSScriptMenuGui/examples/PSScriptMenuGui.ps1 ================================================ #region Setup Set-Location $PSScriptRoot Remove-Module PSScriptMenuGui -ErrorAction SilentlyContinue try { Import-Module PSScriptMenuGui -ErrorAction Stop } catch { Write-Warning $_ Write-Verbose 'Attempting to import from parent directory...' -Verbose Import-Module '..\' } #endregion Show-ScriptMenuGui -csvPath '.\example_data.csv' -Verbose ================================================ FILE: PSScriptMenuGui/examples/PSScriptMenuGui_all_options.ps1 ================================================ #region Setup Set-Location $PSScriptRoot Remove-Module PSScriptMenuGui -ErrorAction SilentlyContinue try { Import-Module PSScriptMenuGui -ErrorAction Stop } catch { Write-Warning $_ Write-Verbose 'Attempting to import from parent directory...' -Verbose Import-Module '..\' } #endregion $params = @{ csvPath = '.\example_data.csv' windowTitle = 'Example with all options' buttonForegroundColor = 'Azure' buttonBackgroundColor = '#C00077' iconPath = '.\pwsh7.ico' hideConsole = $true noExit = $true Verbose = $true } Show-ScriptMenuGui @params ================================================ FILE: PSScriptMenuGui/examples/example_data.csv ================================================ Section,Method,Command,Arguments,Name,Description Old school,cmd,.\example_target.cmd,,Example 1:cmd,.cmd file Old school,cmd,taskmgr.exe,,Example 2:cmd,External executable Old school,cmd,notepad.exe,example_text_file,Example 3:cmd,External executable with arguments Less old,powershell_file,.\example_target.ps1,,Example 4:powershell_file,.ps1 file called with powershell.exe Less old,powershell_inline,"""Inline script"";$PSVersionTable;Read-Host ""Press Enter to continue""",,Example 5:powershell_inline,Inline script called with powershell.exe Less old,powershell_inline,$PSVersionTable, -NoExit -WindowStyle Maximized,Example 6:powershell_inline,Additional powershell.exe arguments The future,pwsh_file,.\example_target.ps1,,Example 7:pwsh_file,.ps1 file called with pwsh.exe The future,pwsh_inline,"""Inline script"";$PSVersionTable;Read-Host ""Press Enter to continue""",,Example 8:pwsh_inline,Inline script called with pwsh.exe The future,pwsh_inline,"& .\example_target.ps1 -Message ""passed in via param""",,Example 9:pwsh_inline,.ps1 file called with parameter ================================================ FILE: PSScriptMenuGui/examples/example_target.cmd ================================================ @echo off echo CMD script called from PSScriptMenuGui.ps1 pause ================================================ FILE: PSScriptMenuGui/examples/example_target.ps1 ================================================ param ($message) "PowerShell script called from PSScriptMenuGui.ps1" if ($message) { "`$message = $message" } $PSVersionTable Read-Host "Press Enter to continue" ================================================ FILE: PSScriptMenuGui/examples/example_text_file.txt ================================================ hello world ================================================ FILE: PSScriptMenuGui/private/functions.ps1 ================================================ function Hide-Console { Write-Verbose 'Hiding PowerShell console...' # .NET method for hiding the PowerShell console window # https://stackoverflow.com/questions/40617800/opening-powershell-script-and-hide-command-prompt-but-not-the-gui 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); ' $consolePtr = [Console.Window]::GetConsoleWindow() [Console.Window]::ShowWindow($consolePtr, 0) # 0 = hide } Function New-GuiHeading { param( [Parameter(Mandatory)][string]$name ) $string = Get-Content "$moduleRoot\xaml\heading.xaml" $string = $string.Replace('INSERT_SECTION_HEADING',(Get-XamlSafeString $name) ) $string = $string.Replace('INSERT_ROW',$row) $script:row++ return $string } Function New-GuiRow { [CmdletBinding()] param( [Parameter(Mandatory)][PSCustomObject]$item ) Write-Verbose $item $string = Get-Content "$moduleRoot\xaml\item.xaml" $string = $string.Replace('INSERT_BACKGROUND_COLOR',$buttonBackgroundColor) $string = $string.Replace('INSERT_FOREGROUND_COLOR',$buttonForegroundColor) $string = $string.Replace('INSERT_BUTTON_TEXT',(Get-XamlSafeString $item.Name) ) # Description is optional if ($item.Description) { $string = $string.Replace('INSERT_DESCRIPTION',(Get-XamlSafeString $item.Description) ) } else { $string = $string.Replace('INSERT_DESCRIPTION','') } $string = $string.Replace('INSERT_BUTTON_NAME',$item.Reference) $string = $string.Replace('INSERT_ROW',$row) $script:row++ return $string } Function Get-XamlSafeString { param( [Parameter(Mandatory)][string]$string ) # https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-use-special-characters-in-xaml # Order matters: & first $string = $string.Replace('&','&').Replace('<','<').Replace('>','>').Replace('"','"') # Restore line breaks $string = $string -replace '<\s*?LineBreak\s*?\/\s*?>','' return $string } Function New-GuiForm { # Based on: https://foxdeploy.com/2015/05/14/part-iii-using-advanced-gui-elements-in-powershell/ param ( [Parameter(Mandatory)][array]$inputXml # XML has not been converted to object yet ) # Process raw XML $inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^ [CmdletBinding()] param( [string][Parameter(Mandatory)]$csvPath, [string]$windowTitle = 'PowerShell Script Menu', [string]$buttonForegroundColor = 'White', [string]$buttonBackgroundColor = '#366EE8', [string]$iconPath, [switch]$hideConsole, [switch]$noExit ) Write-Verbose 'Show-ScriptMenuGui started' # -Verbose value, to pass to select cmdlets $verbose = $false try { if ($PSBoundParameters['Verbose'].ToString() -eq 'True') { $verbose = $true } } catch {} $csvData = Import-CSV -Path $csvPath -ErrorAction Stop Write-Verbose "Got $($csvData.Count) CSV rows" # Add unique Reference to each item # Used as x:Name of button and to look up action on click $i = 0 $csvData | ForEach-Object { $_ | Add-Member -Name Reference -MemberType NoteProperty -Value "button$i" $i++ } # Begin constructing XAML $xaml = Get-Content "$moduleRoot\xaml\start.xaml" $xaml = $xaml.Replace('INSERT_WINDOW_TITLE',$windowTitle) if ($iconPath) { # TODO: change taskbar icon? # WPF wants the absolute path $iconPath = (Resolve-Path $iconPath).Path $xaml = $xaml.Replace('INSERT_ICON_PATH',$iconPath) } else { # No icon specified $xaml = $xaml.Replace('Icon="INSERT_ICON_PATH" ','') } # Add CSV data to XAML # Row counter $script:row = 0 # Not using Group-Object as PS7-preview4 does not preserve original order $sections = $csvData.Section | Where-Object {-not [string]::IsNullOrEmpty($_) } | Get-Unique # Generate GUI rows ForEach ($section in $sections) { Write-Verbose "Adding GUI Section: $section..." # Section Heading $xaml += New-GuiHeading $section $csvData | Where-Object {$_.Section -eq $section} | ForEach-Object { # Add items $xaml += New-GuiRow $_ } } Write-Verbose 'Adding any items with blank Section...' $csvData | Where-Object { [string]::IsNullOrEmpty($_.Section) } | ForEach-Object { $xaml += New-GuiRow $_ # TODO: spacing at top of window is untidy with no Sections (minor) } Write-Verbose "Added $($row) GUI rows" # Finish constructing XAML $xaml += Get-Content "$moduleRoot\xaml\end.xaml" Write-Verbose 'Creating XAML objects...' $form = New-GuiForm -inputXml $xaml Write-Verbose "Found $($buttons.Count) buttons" Write-Verbose 'Adding click actions...' ForEach ($button in $buttons) { $button.Add_Click( { # Use object in pipeline to identify script to run Invoke-ButtonAction $_.Source.Name } ) } if ($hideConsole) { if ($global:error[0].Exception.CommandInvocation.MyCommand.ModuleName -ne 'PSScriptMenuGui') { # Do not hide console if there have been errors Hide-Console | Out-Null } } Write-Verbose 'Showing dialog...' $Form.ShowDialog() | Out-Null } Function New-ScriptMenuGuiExample { <# .SYNOPSIS Creates an example set of files for PSScriptMenuGui .PARAMETER path Path of output folder .EXAMPLE New-ScriptMenuGuiExample -path 'PSScriptMenuGui_example' .LINK https://github.com/weebsnore/PowerShell-Script-Menu-Gui #> [CmdletBinding()] param ( [string]$path = 'PSScriptMenuGui_example' ) # Ensure folder exists if (-not (Test-Path -Path $path -PathType Container) ) { New-Item -Path $path -ItemType 'directory' -Verbose | Out-Null } Write-Verbose "Copying example files to $path..." -Verbose Copy-Item -Path "$moduleRoot\examples\*" -Destination $path } ================================================ FILE: PSScriptMenuGui/xaml/end.xaml ================================================ ================================================ FILE: PSScriptMenuGui/xaml/heading.xaml ================================================ ================================================ FILE: PSScriptMenuGui/xaml/item.xaml ================================================ INSERT_DESCRIPTION ================================================ FILE: PSScriptMenuGui/xaml/start.xaml ================================================ ================================================ FILE: README.md ================================================ # PSScriptMenuGui [![PSGallery Version](https://img.shields.io/powershellgallery/v/PSScriptMenuGui.png?style=for-the-badge&logo=powershell&label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) [![PSGallery Downloads](https://img.shields.io/powershellgallery/dt/PSScriptMenuGui.png?style=for-the-badge&label=Downloads)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) [![PSGallery Platform](https://img.shields.io/powershellgallery/p/PSScriptMenuGui.png?style=for-the-badge&label=Platform)](https://www.powershellgallery.com/packages/PSScriptMenuGui/) Do you have favourite scripts that go forgotten? Does your organisation have scripts that would be useful to frontline staff who are not comfortable with the command line? This module uses a CSV file to make a graphical menu of PowerShell scripts. It's easy to customise and fast to launch. You can also add Windows programs and files to the menu. Just a few minutes to setup and - *click! click!* - you're away! ![](demo.gif) ## Try it out ### Tutorial Looking for a step-by-step introduction? [**See this blog post.**](https://blog.osull.com/2019/11/06/tutorial-use-a-csv-file-to-make-a-graphical-menu-of-powershell-scripts/) ### Install from the PowerShell Gallery Install-Module PSScriptMenuGui -Scope CurrentUser New-ScriptMenuGuiExample cd PSScriptMenuGui_example .\PSScriptMenuGui.ps1 ### Clone from GitHub git clone https://github.com/weebsnore/PowerShell-Script-Menu-Gui cd PowerShell-Script-Menu-Gui\PSScriptMenuGui\examples .\PSScriptMenuGui.ps1 ## Problems and feedback How are you finding the module? Is it useful? *(Please share a screenshot!)* Are you stuck? Do you want a feature? Please [open a GitHub issue](https://github.com/weebsnore/PowerShell-Script-Menu-Gui/issues) or get in touch. [@dan_osull.com](https://twitter.com/dan_osull_com/) *- follow me for updates!* https://blog.osull.com powershell@osull.com ## Compatibility Tested with **PowerShell 5.1 x64** and **PowerShell 7 x64** on Windows 10. ## Basic usage Show-ScriptMenuGui -csvPath '.\example_data.csv' -Verbose ## Show-ScriptMenuGui options Parameter | What is it? :--- |:--- `-csvPath` | Path to CSV file that defines the menu. See [CSV reference](#csv-reference), below. `-windowTitle` *(optional)* | Custom title for the menu window `-buttonForegroundColor` *(optional)* | Custom button foreground (text) color. Hex codes (e.g. `#C00077`) and color names (e.g. `Azure`) are valid. See [.NET Color Class](https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.colors). `-buttonBackgroundColor` *(optional)* | Custom button background color `-iconPath` *(optional)* | Path to .ico file for use in menu `-hideConsole` *(optional)* | Hide the PowerShell console that the menu is called from. **Note:** This means you won't be able to see any errors from button clicks. If things aren't working, this should be the first thing you stop using. `-noExit` *(optional)* | Start all PowerShell instances with `-NoExit` *("Does not exit after running startup commands.")*. **Note:** You can set `-NoExit` on individual menu items by using the *Arguments* column. See [CSV reference](#csv-reference), below. See [`PSScriptMenuGui_all_options.ps1`](PSScriptMenuGui/examples/PSScriptMenuGui_all_options.ps1) for an example using every option. ## CSV reference This table details how to lay out the CSV file for your menu. The top row of your CSV should contain the column headers. Each row after this defines a menu item. Column header | What is it? :--- |:--- Section *(optional)* | Text for heading Method | What happens when you click the button? Valid options: `cmd` \| `powershell_file` \| `powershell_inline` \| `pwsh_file` \| `pwsh_inline` Command | Path to target script/executable (`cmd` or `_file` methods) ***or*** PowerShell commands (`_inline` methods) Arguments *(optional)* | Arguments to pass to target executable (`cmd` method) ***or*** to the PowerShell exe (other methods) Name | Text for button Description *(optional)* | Text for description ### Some examples Section | Method | Command | Arguments | Name | Description :---|:---|:---|:---|:---|:--- Old school | `cmd` | `taskmgr.exe` | | Example 2: cmd | External executable Old school | `cmd` | `notepad.exe` | `example_text_file` | Example 3: cmd | External executable with arguments Less old | `powershell_file` | `example_target.ps1` | | Example 4: powershell_file | .ps1 file called with powershell.exe Less old | `powershell_inline` | `$PSVersionTable` | `-NoExit -WindowStyle Maximized` | Example 6: powershell_inline | Additional powershell.exe arguments The future | `pwsh_file` | `example_target.ps1` | | Example 7: pwsh_file | .ps1 file called with pwsh.exe The future | `pwsh_inline` | `& .\example_target.ps1 -Message "passed in via param"` | |Example 9: pwsh_inline | .ps1 file called with parameter See [`example_data.csv`](PSScriptMenuGui/examples/example_data.csv) for further examples in CSV format. ### Tips - Relative paths, network paths and paths in your environment should work. - `` is supported in text fields. - You can add multiple `_inline` commands by separating with a semi-colon (`;`) - Excel makes a good editor! - But watch out for Excel turning e.g. `-NoExit` into a formula. Best workaround is to prefix with a space. ![](excel.png)