3.8 KiB
3.8 KiB
PowerShell & WPF Standards
General Rules
- Always include
#Requires -Version 5.1at the top of every.ps1file - Use
$script:scope for module-level variables — never$Global:unless absolutely necessary - Keep credential and sensitive values out of code — load from config files or environment variables only
- Use
.psd1files for all external configuration (not JSON, not XML, not hardcoded)
Code Structure
- Organize code using
#region/#endregionblocks with descriptive names - Group related functions within their own region
- One function per logical task — keep functions focused
#region Configuration Functions
function Initialize-Config {
...
}
#endregion
#region UI Functions
function Show-MainWindow {
...
}
#endregion
Naming Conventions
- Functions: PascalCase Verb-Noun (
Get-UserConfig,Show-MainWindow,Initialize-App) - Variables: PascalCase (
$UserConfig,$ScriptRoot,$AppVersion) - Script-scoped:
$script:VariableName - Use approved PowerShell verbs (
Get-,Set-,Show-,New-,Remove-,Initialize-,Invoke-, etc.)
Error Handling
- Use
try/catchfor all operations that can fail (file I/O, network, registry, etc.) - Use
Write-Warningfor non-fatal errors that should be logged but allow execution to continue - Use
Write-Errorfor failures that should stop the current operation - Create centralized helper functions for user-facing messages:
function Show-Error { param([string]$Message, [string]$Title = "Error") ... }
function Show-Warning { param([string]$Message, [string]$Title = "Warning") ... }
function Show-Info { param([string]$Message, [string]$Title = "Information") ... }
WPF Specifics
- Always ensure STA thread mode for WPF — include an STA re-launch block at the top of
.ps1entry points - Load all WPF assemblies at the top of the file:
Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName System.Windows.Forms
- Define XAML as a here-string and load it via
[System.Windows.Markup.XamlReader]::Parse() - Use
$script:variables to hold window/control references so they're accessible across functions
Modules
- Shared/reusable functions belong in a
.psm1module under aModules/folder - The main
.ps1entry point imports the module at startup:
Import-Module "$script:AppRoot\Modules\ProjectName.psm1" -Force
- Module files follow the same naming, scoping, and region conventions
Configuration Files
- Use
.psd1(PowerShell Data Files) for all config — they're native, typed, and easy to version - Always provide a default config creation block if the file doesn't exist
- Config keys should use PascalCase nested hashtables:
@{
Application = @{
WindowWidth = 800
WindowHeight = 600
}
Email = @{
SMTPServer = ''
From = ''
To = ''
}
}
- Never put actual credentials in
.psd1config — use placeholder strings with comments
Testing
- Use Pester for unit testing
- Always provide a Pester test file alongside new functions
- At minimum, test: happy path, expected failure cases, and edge cases
- Run tests with:
Invoke-Pester -Path .\Tests\ -Output Detailed
# Tests/Get-UserConfig.Tests.ps1
Describe "Get-UserConfig" {
It "Returns default config when file does not exist" { ... }
It "Loads config from valid .psd1 file" { ... }
}
Comments
- Comment the why, not the what — code should be readable on its own
- Use inline comments for non-obvious logic
- Use block comments (
<# ... #>) for function documentation (.SYNOPSIS,.DESCRIPTION,.PARAMETER,.EXAMPLE)