header1   header
header
header Register : : Login header
header
connector   connector
menuleft menuright
submenu   submenu
left
Cmdlet Extension Library
Category: Windows general
Contributed by Kirk Munro (Poshoholic) on 2007-11-27, last updated 2008-01-15 • user rating: 5


Description:
This script contains a library of useful functions and will grow over time. In its current revision its purpose is as follows: 1. To extend Get-Help so that the cmdlet help documentation for core cmdlets includes all dynamic parameter information as well. 2. To provide Get-PSResourceString to help support script internationalization. 3. To provide Invoke-Member to help ease the invocation of members within a pipeline. 4. To provide a version of Should-Process that supports localized PowerShell. Note that when extending cmdlets I have tried to ensure that the original cmdlet and the corresponding cmdlet extension function can be used interchangeably in scripts without requiring any modifications. The only known exception to this is when scripts are modified to explicitly use some information that is only exposed by the function extending the cmdlet. Also I have made an effort to support localized versions of PowerShell in these functions as well by using the resource strings that come with PowerShell for the current culture through the Get-PSResourceString cmdlet. There are a few strings that will still only be available in English though, such as variable and alias descriptions.

Non-core snapins required to run this:
None

Formatted code:
1 ######################################################################################################################## 2 #                                                                                                                      # 3 # File:             CmdletExtensionLibrary.ps1                                                                         # 4 # Author:           Kirk Munro                                                                                         # 5 # Author's Blog:    http://poshoholic.com                                                                              # 6 # Revision:         1.0.3                                                                                              # 7 # Contents:         A collection of functions designed with two purposes in mind:                                      # 8 #                   1. To seamlessly extend published cmdlet functionality transparently to the end user               # 9 #                   2. To wrap common operations in fully documented, well named functions designed to extend          # 10 #                      PowerShell's core set of features                                                               # 11 #                   In the first case, every effort is made to ensure that the original cmdlet and the corresponding   # 12 #                   function can be used interchangeably in scripts without requiring any modifications. The only      # 13 #                   known exception to this is when scripts are modified to explicitly use some information that is    # 14 #                   only exposed by the function extending the cmdlet.                                                 # 15 #                   In the second case, functionality is being exposed as functions to solicit community feedback. It  # 16 #                   is my intention to publish these sorts of functions as cmdlets in the future.                      # 17 # History:          v1.0.0 - Initial release designed to enhance support for dynamic parameters in the PowerShell help # 18 #                            system                                                                                    # 19 #                   v1.0.1 - Addition of Invoke-Member function to allow users to invoke any member on a data set      # 20 #                            without having to use ForEach-Object                                                      # 21 #                   v1.0.2 - Fixed grs alias for Get-PSResourceString                                                  # 22 #                          - Added ... alias for Invoke-Member                                                         # 23 #                          - Added pipeline support to Invoke-Cmdlet, Add-PSSnapin, Remove-PSSnapin and Get-Help       # 24 #                   v1.0.3 - Added retrieval of Microsoft.PowerShell.ConsoleHost strings to Get-PSResourceString       # 25 #                          - Added -list parameter to Get-PSResourceString                                             # 26 #                          - Added more examples to Get-PSResourceString documentation (comments)                      # 27 #                          - Added Should-Process function to provide -whatif, -confirm and -verbose support to other  # 28 #                            functions                                                                                 # 29 #                          - Added -whatif, -confirm and -verbose support to Invoke-Member function                    # 30 #                                                                                                                      # 31 ######################################################################################################################## 32 33 ######################################################################################################################## 34 # NAME 35 #     Get-PSResourceString 36 # 37 # SYNOPSIS 38 #     Returns a resource string that is looked up in the System.Management.Automation namespace or the 39 #     Microsoft.PowerShell.ConsoleHost namespace, or a list of resource root names or resource identifiers that are 40 #     available. 41 # 42 # SYNTAX 43 #     Get-PSResourceString [-baseName] <string> [-resourceId] <string> [[-defaultValue] <string>] 44 #     [[-culture] <System.Globalization.CultureInfo>] 45 #     Get-PSResourceString [[-baseName] <string>] -list 46 # 47 # DETAILED DESCRIPTION 48 #     The Get-PSResourceString function returns a resource string that is looked up in the System.Management.Automation 49 #     namespace or the Microsoft.PowerShell.ConsoleHost namespace, or a list of resource root names or resource 50 #     identifiers that are available. If a resource string was requested and it is not found, the default value (if 51 #     present) will be returned. 52 # 53 # PARAMETERS 54 #     -baseName <string> 55 #         Specifies the root name of the resources. 56 # 57 #         Required?                    true 58 #         Position?                    1 59 #         Default value 60 #         Accept pipeline input?       false 61 #         Accept wildcard characters?  false 62 # 63 #     -resourceId <string> 64 #         Specifies the identifier of the resource that is being retrieved. 65 # 66 #         Required?                    true 67 #         Position?                    2 68 #         Default value 69 #         Accept pipeline input?       false 70 #         Accept wildcard characters?  false 71 # 72 #     -defaultValue <string> 73 #         Specifies the default value for the resource string. If the string is not found, the default value will be 74 #         returned. 75 # 76 #         Required?                    false 77 #         Position?                    3 78 #         Default value                null 79 #         Accept pipeline input?       false 80 #         Accept wildcard characters?  false 81 # 82 #     -culture <System.Globalization.CultureInfo> 83 #         Specifies the culture to use when looking up the resource string. 84 # 85 #         Required?                    false 86 #         Position?                    4 87 #         Default value                $host.CurrentCulture 88 #         Accept pipeline input?       false 89 #         Accept wildcard characters?  false 90 # 91 #     -list <Switch> 92 #         When this parameter is used by itself, this function outputs the root names that are available. When this 93 #         parameter is used in conjunction with the baseName parameter, this function outputs the resource identifiers 94 #         that are availab.e 95 # 96 #         Required?                    false 97 #         Position?                    named 98 #         Default value                false 99 #         Accept pipeline input?       false 100 #         Accept wildcard characters?  false 101 # 102 # INPUT TYPE 103 #     String,System.Globalization.CultureInfo,Switch 104 # 105 # RETURN TYPE 106 #     String,String[] 107 # 108 # NOTES 109 #     For more information the System.Globalization.CultureInfo type consult the relevant MSDN documentation. 110 # 111 #     -------------------------- EXAMPLE 1 -------------------------- 112 # 113 #     C:\PS>get-psresourcestring -list 114 # 115 # 116 #     This command retrieves the list of resource root names that are available. 117 # 118 # 119 #     -------------------------- EXAMPLE 2 -------------------------- 120 # 121 #     C:\PS>get-psresourcestring -basename helpdisplaystrings -list 122 # 123 # 124 #     This command retrieves the list of resource strings in the resource root called 'helpdisplaystrings' using the 125 #     current culture. 126 # 127 # 128 #     -------------------------- EXAMPLE 3 -------------------------- 129 # 130 #     C:\PS>get-psresourcestring -list | foreach-object { get-psresourcestring -basename $_ -list } 131 # 132 # 133 #     This command retrieves all resource strings that are available using the current culture. 134 # 135 # 136 #     -------------------------- EXAMPLE 4 -------------------------- 137 # 138 #     C:\PS>get-psresourcestring -basename helpdisplaystrings -resourceid falseshort 139 # 140 # 141 #     This command retrieves the string associated with the 'falseshort' resource id using the current culture. 142 # 143 # 144 145 Function Get-PSResourceString { 146 param( 147 [string]$baseName = $null, 148 [string]$resourceId = $null, 149 [string]$defaultValue = $null, 150 [System.Globalization.CultureInfo]$culture = $host.CurrentCulture, 151 [Switch]$list 152 ) 153 154 if ($list -and ($resourceId -or $defaultValue)) { 155 throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'AmbiguousParameterSet') 156 } 157 158 if ($list) { 159 $engineAssembly = [System.Reflection.Assembly]::GetExecutingAssembly() 160 $hostAssembly = [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.PowerShell.ConsoleHost') 161 if ($baseName) { 162 $engineAssembly.GetManifestResourceNames() | Where-Object { $_ -eq "$baseName.resources" } | ForEach-Object { 163 $resourceManager = New-Object -TypeName System.Resources.ResourceManager($baseName, $engineAssembly) 164 $resourceManager.GetResourceSet($host.CurrentCulture,$true,$true) | Add-Member -Name BaseName -MemberType NoteProperty -Value $baseName -Force -PassThru | ForEach-Object { 165 $_.PSObject.TypeNames.Clear() 166 $_.PSObject.TypeNames.Add('ResourceString') 167 $_ | Write-Output 168 } 169 } 170 $hostAssembly.GetManifestResourceNames() | Where-Object { $_ -eq "$baseName.resources" } | ForEach-Object { 171 $resourceManager = New-Object -TypeName System.Resources.ResourceManager($baseName, $hostAssembly) 172 $resourceManager.GetResourceSet($host.CurrentCulture,$true,$true) | Add-Member -Name BaseName -MemberType NoteProperty -Value $baseName -Force -PassThru | ForEach-Object { 173 $_.PSObject.TypeNames.Clear() 174 $_.PSObject.TypeNames.Add('ResourceString') 175 $_ | Write-Output 176 } 177 } 178 } else { 179 $engineAssembly.GetManifestResourceNames() | Where-Object { $_ -match '\.resources$' } | ForEach-Object { $_.Replace('.resources','') } 180 $hostAssembly.GetManifestResourceNames() | Where-Object { $_ -match '\.resources$' } | ForEach-Object { $_.Replace('.resources','') } 181 } 182 } else { 183 if (-not $baseName) { 184 throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'BaseName') 185 } 186 if (-not $resourceId) { 187 throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'ResourceId') 188 } 189 if (-not $global:PSResourceStringTable) { 190 $engineAssembly = [System.Reflection.Assembly]::GetExecutingAssembly() 191 $hostAssembly = [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.PowerShell.ConsoleHost') 192 if ($engineAssembly.GetManifestResourceNames() -contains "$baseName.resources") { 193 New-Variable -Scope Global -Name PSResourceStringTable -Value @{} -Description 'A cache of PowerShell resource strings. To access data in this table, use Get-ResourceString.' 194 $global:PSResourceStringTable['EngineAssembly'] = @{'Assembly'=$engineAssembly;'Cultures'=@{}} 195 $global:PSResourceStringTable['HostAssembly'] = @{'Assembly'=$hostAssembly;'Cultures'=@{}} 196 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly)); 197 $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}}; 198 } elseif ($hostAssembly.GetManifestResourceNames() -contains "$baseName.resources") { 199 New-Variable -Scope Global -Name PSResourceStringTable -Value @{} -Description 'A cache of PowerShell resource strings. To access data in this table, use Get-ResourceString.' 200 $global:PSResourceStringTable['EngineAssembly'] = @{'Assembly'=$engineAssembly;'Cultures'=@{}} 201 $global:PSResourceStringTable['HostAssembly'] = @{'Assembly'=$hostAssembly;'Cultures'=@{}} 202 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly)); 203 $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}}; 204 } 205 } elseif ($global:PSResourceStringTable.EngineAssembly.Assembly.GetManifestResourceNames() -contains "$baseName.resources") { 206 if (-not $global:PSResourceStringTable.EngineAssembly.Cultures.ContainsKey($culture.Name)) { 207 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly)); 208 $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}}; 209 } elseif (-not $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name].ContainsKey($baseName)) { 210 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly)); 211 $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name][$baseName] = @{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}; 212 } 213 } elseif ($global:PSResourceStringTable.HostAssembly.Assembly.GetManifestResourceNames() -contains "$baseName.resources") { 214 if (-not $global:PSResourceStringTable.HostAssembly.Cultures.ContainsKey($culture.Name)) { 215 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly)); 216 $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}}; 217 } elseif (-not $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name].ContainsKey($baseName)) { 218 $resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly)); 219 $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name][$baseName] = @{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}; 220 } 221 } 222 223 $resourceString = $null 224 if ($global:PSResourceStringTable) { 225 if ($global:PSResourceStringTable.EngineAssembly.Cultures -and $global:PSResourceStringTable.EngineAssembly.Cultures.ContainsKey($culture.Name) -and $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name].ContainsKey($baseName)) { 226 $resourceString = ($global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name][$baseName].Strings | Where-Object { $_.Name -eq $resourceId }).Value 227 } elseif ($global:PSResourceStringTable.HostAssembly.Cultures -and $global:PSResourceStringTable.HostAssembly.Cultures.ContainsKey($culture.Name) -and $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name].ContainsKey($baseName)) { 228 $resourceString = ($global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name][$baseName].Strings | Where-Object { $_.Name -eq $resourceId }).Value 229 } 230 } 231 if (-not $resourceString) { 232 $resourceString = $defaultValue 233 } 234 235 return $resourceString 236 } 237 } 238 239 if (-not (Get-Alias -Name grs -ErrorAction SilentlyContinue)) { 240 New-Alias -Name grs -Value Get-PSResourceString -Description 'Returns a resource string that is looked up in the System.Management.Automation namespace.' 241 } 242 243 ######################################################################################################################## 244 # NAME 245 #     Should-Process 246 # 247 # SYNOPSIS 248 #     Determines whether the specified operation should be performed on the target object. 249 # 250 # SYNTAX 251 #     Should-Process [-operation] <string> [-target] <string> [-whatIf] 252 #     Should-Process [-operation] <string> [-target] <string> [-confirmResponse] <REF> [[-confirmPrompt] <string>] 253 #     [-confirm] 254 #     Should-Process [-operation] <string> [-target] <string> [-verbose] 255 # 256 # DETAILED DESCRIPTION 257 #     Determines whether the specified operation should be performed on the target object. Returns true if the operation 258 #     should be performed. Should-Process never performs the operation; it just indicates whether or not it should be 259 #     performed to the caller. If the whatIf switch is used, Should-Process outputs strings indicating what it would do 260 #     if whatIf wasn't used. If the confirm switch is used, Should-Process asks the user to confirm that they want to 261 #     perform the operation. If the verbose switch is used, Should-Process writes verbose output for each operation that 262 #     will be performed on each target. 263 # 264 # PARAMETERS 265 #     -operation <string> 266 #         Specifies the name of the function that was invoked. 267 # 268 #         Required?                    true 269 #         Position?                    1 270 #         Default value 271 #         Accept pipeline input?       false 272 #         Accept wildcard characters?  false 273 # 274 #     -target <string> 275 #         Specifies the identifier of the object that is being used in the operation. 276 # 277 #         Required?                    true 278 #         Position?                    2 279 #         Default value 280 #         Accept pipeline input?       false 281 #         Accept wildcard characters?  false 282 # 283 #     -confirmResponse <REF> 284 #         Reference variable that retains any confirm question response that applies to all objects being processed. 285 # 286 #         Required?                    true 287 #         Position?                    3 288 #         Default value 289 #         Accept pipeline input?       false 290 #         Accept wildcard characters?  false 291 # 292 #     -confirmPrompt <string> 293 #         Specifies the string that will be displayed to the caller when the -confirm switch is used. 294 # 295 #         Required?                    false 296 #         Position?                    4 297 #         Default value                Are you sure you want to perform this action? 298 #         Accept pipeline input?       false 299 #         Accept wildcard characters?  false 300 # 301 #     -verbose <switch> 302 #         When this parameter is used, this function outputs verbose information to the host. 303 # 304 #         Required?                    false 305 #         Position?                    named 306 #         Default value 307 #         Accept pipeline input?       false 308 #         Accept wildcard characters?  false 309 # 310 #     -confirm <switch> 311 #         When this parameter is used, this function requests confirmation from the user for the actions it will take. 312 # 313 #         Required?                    false 314 #         Position?                    named 315 #         Default value 316 #         Accept pipeline input?       false 317 #         Accept wildcard characters?  false 318 # 319 #     -whatif <switch> 320 #         When this parameter is used, this function outputs what it would do without actually taking action. 321 # 322 #         Required?                    false 323 #         Position?                    named 324 #         Default value 325 #         Accept pipeline input?       false 326 #         Accept wildcard characters?  false 327 # 328 # INPUT TYPE 329 #     String,Ref,Switch 330 # 331 # RETURN TYPE 332 #     Boolean 333 # 334 # NOTES 335 #     Whatif takes precedence over confirm. Confirm takes precedence over verbose. If more than one switch parameter is 336 #     used, this precedence order will be followed and only the highest precedence switch will be used. 337 # 338 #     The ConfirmResponse reference parameter is used to determine if "YES to All" or "NO to all" has been previously 339 #     selected. 340 # 341 #     When using Should-Process inside of functions that can be used in a pipeline, you should initialize your reference 342 #     variable in the begin block and then call Should-Process in the process block. 343 # 344 #     -------------------------- EXAMPLE 1 -------------------------- 345 # 346 # 347 #  C:\PS>function Stop-Calc ([Switch]$Verbose, [Switch]$Confirm, [Switch]$Whatif) { 348 #         $ConfirmResponse = $null 349 #         foreach ($p in Get-Process calc) { 350 #             if (Should-Process Stop-Calc $p.Id ([REF]$ConfirmResponse) ` 351 #                 -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif) { 352 #                 Stop-Process $p.Id 353 #             } 354 #         } 355 #     } 356 #     C:\PS>stop-calc -confirm 357 # 358 # 359 #     This prompts you to confirm you want to stop all calc processes when using the simple Stop-Process function. 360 # 361 # 362 # RELATED LINKS 363 #     about_commonparameters 364 #     about_ref 365 # 366 367 Function Should-Process { 368 param( 369 [string]$Operation = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Operation')), 370 [string]$Target = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Target')), 371 [REF]$ConfirmResponse = ([REF]$null), 372 [string]$ConfirmPrompt = $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessWarningFallback') -f $null), 373 [Switch]$Verbose, 374 [Switch]$Confirm, 375 [Switch]$Whatif 376 ) 377 378 if ($ConfirmResponse.Value -eq $false) { 379 return $false 380 } elseif ($ConfirmResponse.Value -eq $true) { 381 return $true 382 } 383 384 if ($Whatif) { 385 Write-Host $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessWhatIfMessage') -f $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target)) 386 return $false 387 } elseif ($Confirm) { 388 $ConfirmText = @" 389 $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'InquireCaptionDefault') 390 $ConfirmPrompt 391 $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target) 392 "@ 393 Write-Host $ConfirmText 394 $Yes = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueOneLabel') 395 $All = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueAllLabel') 396 $No = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipOneLabel') 397 $None = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipAllLabel') 398 $Suspend = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'PauseLabel') 399 $Help = $(Get-PSResourceString -BaseName 'ConsoleHostUserInterfaceStrings' -ResourceId 'PromptForChoiceHelp') 400 401 $YesHotkey = $Yes.ToUpper()[$Yes.IndexOf('&') + 1] 402 $AllHotkey = $All.ToUpper()[$All.IndexOf('&') + 1] 403 $NoHotkey = $No.ToUpper()[$No.IndexOf('&') + 1] 404 $NoneHotkey = $None.ToUpper()[$None.IndexOf('&') + 1] 405 $SuspendHotkey = $Suspend.ToUpper()[$Suspend.IndexOf('&') + 1] 406 $HelpHotkey = $Help.ToUpper()[$Help.IndexOf('[') + 1] 407 408 $YesHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueOneHelpMessage') 409 $AllHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueAllHelpMessage') 410 $NoHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipOneHelpMessage') 411 $NoneHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipAllHelpMessage') 412 $SuspendHelp = $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'PauseHelpMessage') -f 'exit') 413 $DefaultValue = $($(Get-PSResourceString -BaseName 'ConsoleHostUserInterfaceStrings' -ResourceId 'DefaultChoicePrompt') -f $YesHotKey) 414 415 while ($true) { 416 $answer = Read-Host @" 417 [$YesHotkey] $($Yes.Replace('&',''))  [$AllHotkey] $($All.Replace('&',''))  [$NoHotkey] $($No.Replace('&',''))  [$NoneHotkey] $($None.Replace('&',''))  [$SuspendHotkey] $($Suspend.Replace('&',''))  $Help  $DefaultValue 418 "@ 419 switch ($answer) { 420 "$YesHotkey" { return $true } 421 "" { return $true } 422 "$AllHotkey" { $ConfirmResponse.Value = $true; return $true } 423 "$NoHotkey" { return $false } 424 "$NoneHotkey" { $ConfirmResponse.Value = $false; return $false } 425 "$SuspendHotkey" { $host.EnterNestedPrompt(); Write-Host $ConfirmText } 426 "$HelpHotkey" { Write-Host @" 427 $YesHotkey - $YesHelp 428 $AllHotkey - $AllHelp 429 $NoHotkey - $NoHelp 430 $NoneHotkey - $NoneHelp 431 $SuspendHotkey - $SuspendHelp 432 "@ 433 } 434 } 435 } 436 } elseif ($verbose) { 437 Write-Verbose $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target) 438 } 439 440 return $true 441 } 442 443 if (-not (Get-Alias -Name sps -ErrorAction SilentlyContinue)) { 444 New-Alias -Name sps -Value Should-Process -Description 'Determines whether the specified operation should be performed on the target object. Returns true if the operation should be performed.' 445 } 446 447 ######################################################################################################################## 448 # NAME 449 #     Invoke-Member 450 # 451 # SYNOPSIS 452 #     Invoke members of each of a set of input objects. 453 # 454 # SYNTAX 455 #     Invoke-Member [-name] <string[]> [[-inputObject] <psobject>] [-whatif] [-confirm] [-verbose] 456 # 457 # DETAILED DESCRIPTION 458 #     Invokes a member of each of a set of input objects. The input objects can be piped to the cmdlet or specified by 459 #     using the InputObject parameter. If an input object does not have the specified member, it will be skipped. The 460 #     results of the invocation of the members are passed down the pipeline. 461 # 462 # PARAMETERS 463 #     -name <string[]> 464 #         Specifies the name of the member to invoke. For members that accept parameters, pass the member name with the 465 #         parameters in parentheses inside of quotation marks. 466 # 467 #         Required?                    true 468 #         Position?                    1 469 #         Default value 470 #         Accept pipeline input?       false 471 #         Accept wildcard characters?  false 472 # 473 #     -inputObject <psobject> 474 #         Accepts an object for which the member will be invoked. Enter a variable that contains the objects or type a 475 #         command or expression that gets the objects. 476 # 477 #         Required?                    false 478 #         Position?                    2 479 #         Default value 480 #         Accept pipeline input?       true (ByValue) 481 #         Accept wildcard characters?  false 482 # 483 #     -whatIf 484 #         Describes what would happen if you executed the command without actually executing the command. 485 # 486 #         Required?                    false 487 #         Position?                    named 488 #         Default value 489 #         Accept pipeline input?       false 490 #         Accept wildcard characters?  false 491 # 492 #     -confirm 493 #         Prompts you for confirmation before executing the command. 494 # 495 #         Required?                    false 496 #         Position?                    named 497 #         Default value 498 #         Accept pipeline input?       false 499 #         Accept wildcard characters?  false 500 # 501 #     <CommonParameters> 502 #         This cmdlet supports the common parameters: -Verbose. For more information, type, 503 #         "get-help about_commonparameters". 504 # 505 # INPUT TYPE 506 #     String[],PSObject 507 # 508 # RETURN TYPE 509 #     Object 510 # 511 # NOTES 512 #     When a collection of objects is passed in via the InputObject parameter, the member will be invoked on the 513 #     collection object, not on the items in the collection. 514 # 515 # 516 #     -------------------------- EXAMPLE 1 -------------------------- 517 # 518 #     C:\PS>get-childitem -literalpath C:\ -force | invoke-member -name PSObject.typenames | select-object -unique 519 # 520 # 521 #     This command retrieves all unique base and derived object type names. 522 # 523 # 524 #     -------------------------- EXAMPLE 2 -------------------------- 525 # 526 #     C:\PS>get-help -category cmdlet | sort-object -property name | invoke-member -name syntax 527 # 528 # 529 #     This command retrieves a sorted list of the syntax for all cmdlets based on help information. 530 # 531 # 532 # RELATED LINKS 533 #     Get-Member 534 #     Invoke-Expression 535 #     ForEach-Object 536 # 537 538 Function Invoke-Member { 539 param ( 540 [string[]]$name = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Name')), 541 $inputObject = $null, 542 [Switch]$whatif, 543 [Switch]$confirm, 544 [Switch]$verbose 545 ) 546 547 BEGIN { 548 $confirmResponse = $null 549 } 550 PROCESS { 551 if ($inputObject -and $_) { 552 throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'InputObjectNotBound') 553 break 554 } elseif ($inputObject) { 555 foreach ($member in $name) { 556 if (Should-Process Invoke-Member $member ([REF]$confirmResponse) -Verbose:$verbose -Confirm:$confirm -Whatif:$whatif) { 557 Invoke-Expression "`$inputObject.$member" 558 } 559 } 560 } elseif ($_) { 561 foreach ($member in $name) { 562 if (Should-Process Invoke-Member $member ([REF]$confirmResponse) -Verbose:$verbose -Confirm:$confirm -Whatif:$whatif) { 563 Invoke-Expression "`$_.$member" 564 } 565 } 566 } else { 567 throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'AmbiguousParameterSet') 568 } 569 } 570 END { 571 } 572 } 573 574 if (-not (Get-Alias -Name im -ErrorAction SilentlyContinue)) { 575 New-Alias -Name im -Value Invoke-Member -Description 'Invokes a member of each of a set of input objects.' 576 } 577 578 if (-not (Get-Alias -Name ... -ErrorAction SilentlyContinue)) { 579 New-Alias -Name ... -Value Invoke-Member -Description 'Invokes a member of each of a set of input objects.' 580 } 581 582 ######################################################################################################################## 583 # NAME 584 #     Invoke-Cmdlet 585 # 586 # SYNOPSIS 587 #     Invokes a cmdlet by using its fully qualified name. 588 # 589 # SYNTAX 590 #     Invoke-Cmdlet [-cmdletName] <string> [<CmdletParameters>] 591 # 592 # DETAILED DESCRIPTION 593 #     The Invoke-Cmdlet function invokes a cmdlet directly by using its fully qualified name. This allows cmdlets to be 594 #     invoked even if there are functions or aliases with the same name. It follows Microsoft's house rules whereby the 595 #     core snapins take precedence over added snapins. 596 # 597 # PARAMETERS 598 #     -cmdletName <string> 599 #         Specifies the name of a cmdlet. When this parameter is used, this function invokes the cmdlet identified by 600 #         this parameter. 601 # 602 #         Required?                    true 603 #         Position?                    1 604 #         Default value 605 #         Accept pipeline input?       false 606 #         Accept wildcard characters?  false 607 # 608 #     <CmdletParameters> 609 #         This cmdlet supports all parameters that are supported by the cmdlet being invoked. Consult the cmdlet help 610 #         parameter list for more information. 611 # 612 # INPUT TYPE 613 #     String 614 # 615 # RETURN TYPE 616 #     Consult the cmdlet help return type for more information. 617 # 618 # NOTES 619 #     For more information, consult the help documentation for the cmdlet that you are invoking. 620 # 621 #     Cmdlets that have a cmdletName parameter are not supported by this cmdlet. 622 # 623 #     -------------------------- EXAMPLE 1 -------------------------- 624 # 625 #     C:\PS>invoke-cmdlet get-help * -parameter codesigningcert 626 # 627 # 628 #     This command gets all commands that have a CodeSigningCert parameter. This will only include those cmdlets whose 629 #     documentation specifically includes the CodeSigningCert parameter unless the CmdletExtensionLibrary is being used 630 #     and the Get-Help function has been called at least once. 631 # 632 # RELATED LINKS 633 #     Get-Command 634 #     Invoke-Expression 635 # 636 637 Function Invoke-Cmdlet { 638 param([string]$cmdletName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'CmdletName'))) 639 640 BEGIN { 641 $matchingCmdlets = Microsoft.PowerShell.Core\Get-Command -CommandType cmdlet -Name $cmdletName 642 if (@($matchingCmdlets).Count -eq 0) { 643 throw $($(Get-PSResourceString -BaseName 'DiscoveryExceptions' -ResourceId 'CmdletNotFoundException') -f 'CmdletName',$cmdletName) 644 } 645 $cmdletName = $null 646 foreach ($cmdlet in $matchingCmdlets) { 647 if ($cmdlet.PSSnapin.IsDefault) { 648 $cmdletName = "$($cmdlet.PSSnapin.Name)\$($cmdlet.Name)" 649 break 650 } elseif (-not $cmdletName) { 651 $cmdletName = "$($cmdlet.PSSnapin.Name)\$($cmdlet.Name)" 652 } 653 } 654 } 655 PROCESS { 656 if ($_) { 657 Microsoft.PowerShell.Utility\Invoke-Expression "`$_ | $cmdletName $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 658 } else { 659 Microsoft.PowerShell.Utility\Invoke-Expression "$cmdletName $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 660 } 661 } 662 END { 663 } 664 } 665 666 if (-not (Get-Alias -Name ic -ErrorAction SilentlyContinue)) { 667 New-Alias -Name ic -Value Invoke-Cmdlet -Description 'Invokes a cmdlet by using its fully qualified name.' 668 } 669 670 ######################################################################################################################## 671 # NAME 672 #     Get-DynamicParameterMap 673 # 674 # SYNOPSIS 675 #     Displays a mapping of cmdlet names to dynamic parameters. 676 # 677 # SYNTAX 678 #     Get-DynamicParameterMap [-refresh] 679 # 680 # DETAILED DESCRIPTION 681 #     The Get-DynamicParameterMap function extracts the dynamic parameter help information from the PSProvider help 682 #     documentation and builds a map of cmdlet names to dynamic parameters. If the map has already been built, it is 683 #     immediately returned. If the Refresh switch parameter is used, the map is rebuilt. 684 # 685 # PARAMETERS 686 #     -refresh <switch> 687 #         When this parameter is used, this function reloads the dynamic parameter map before returning it to the 688 #         caller. 689 # 690 #         Required?                    false 691 #         Position?                    named 692 #         Default value 693 #         Accept pipeline input?       false 694 #         Accept wildcard characters?  false 695 # 696 # INPUT TYPE 697 #     Switch 698 # 699 # RETURN TYPE 700 #     Associative Array 701 # 702 # NOTES 703 #     If multiple providers include a dynamic parameter with the same name but a different type or documentation, both 704 #     versions will be used. 705 # 706 #     -------------------------- EXAMPLE 1 -------------------------- 707 # 708 #     C:\PS>get-dynamicparametermap 709 # 710 # 711 #     This command retrieves all dynamic parameter help documentation from the PSProviders that are loaded and returns 712 #     an associative array mapping cmdlet names to dynamic parameters. 713 # 714 # RELATED LINKS 715 #     Invoke-Cmdlet 716 #     about_associative_array 717 # 718 719 Function Get-DynamicParameterMap { 720 param ([Switch]$Refresh) 721 722 if (($refresh) -or (-not $global:dynamicParameterMap)) { 723 Remove-Variable -Force -Scope Global -Name dynamicParameterMap -ErrorAction SilentlyContinue 724 New-Variable -Scope Global -Name dynamicParameterMap -Value @{} -Description 'Maps cmdlet names to dynamic parameter help information. This variable is managed via custom scripts that provide better support for dynamic parameters and should only be used by those custom scripts.' 725 Invoke-Cmdlet Get-Help -Category Provider -ErrorAction SilentlyContinue | ForEach-Object { 726 $providerHelp = $_ 727 $_.DynamicParameters.DynamicParameter | Where-Object { $_.CmdletSupported } | ForEach-Object { 728 $type = New-Object -TypeName System.Management.Automation.PSObject 729 $type.PSObject.TypeNames.Clear() 730 $type.PSObject.TypeNames.Add('MamlCommandHelpInfo#type') 731 $type | Add-Member -Name name -MemberType NoteProperty -Value $_.Type.Name 732 $type | Add-Member -Name uri -MemberType NoteProperty -Value $([String]$null) 733 $description = New-Object -TypeName System.Management.Automation.PSObject 734 $description.PSObject.TypeNames.Clear() 735 $description.PSObject.TypeNames.Add('MamlParaTextItem') 736 $description.PSObject.TypeNames.Add('MamlTextItem') 737 $description | Add-Member -Name Text -MemberType NoteProperty -Value $_.Description 738 $descriptionArray = @($description) 739 foreach ($cmdletSupported in $_.CmdletSupported.Split(', ', [StringSplitOptions]::RemoveEmptyEntries)) { 740 if (-not $global:dynamicParameterMap.ContainsKey($cmdletSupported)) { 741 $global:dynamicParameterMap[$cmdletSupported] = @{$providerHelp.Name=@{$_.Name=@{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues}}} 742 } elseif (-not $global:dynamicParameterMap[$cmdletSupported].ContainsKey($providerHelp.Name)) { 743 $global:dynamicParameterMap[$cmdletSupported][$providerHelp.Name] = @{$_.Name=@{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues}} 744 } else { 745 $global:dynamicParameterMap[$cmdletSupported][$providerHelp.Name][$_.Name] = @{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues} 746 } 747 } 748 } 749 } 750 foreach ($cmdlet in $global:dynamicParameterMap.Keys) { 751 Invoke-Cmdlet Get-Help -Name $cmdlet -Category Cmdlet -ErrorAction SilentlyContinue | ForEach-Object { 752 $_.Parameters.parameter = [System.Management.Automation.PSObject[]]($_.Parameters.parameter | Where-Object { $_.isDynamic -ne $true }) 753 $_.Syntax.syntaxItem = [System.Management.Automation.PSObject[]]($_.Syntax.syntaxItem | Where-Object { $_.name -notmatch "`r`n" }) 754 } 755 } 756 } 757 $global:dynamicParameterMap 758 } 759 760 ######################################################################################################################## 761 # NAME 762 #     Add-PSSnapin 763 # 764 # SYNOPSIS 765 #     Wraps the Add-PSSnapin cmdlet. Consult the Add-PSSnapin cmdlet help for more information. 766 # 767 # SYNTAX 768 #     Consult the Add-PSSnapin cmdlet help for the syntax. 769 # 770 # DETAILED DESCRIPTION 771 #     Wraps the Add-PSSnapin cmdlet. After the Add-PSSnapin cmdlet returns, it recreates the dynamic parameter map. 772 # 773 # NOTES 774 #     Consult the Add-PSSnapin cmdlet help for more information on syntax, detailed description, input types, return 775 #     types, notes, examples, and related links. 776 # 777 # RELATED LINKS 778 #     Add-PSSnapin (cmdlet) 779 # 780 781 Function Add-PSSnapin { 782 BEGIN { 783 } 784 PROCESS { 785 if ($_) { 786 Invoke-Expression "`$_ | Invoke-Cmdlet Add-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 787 } else { 788 Invoke-Expression "Invoke-Cmdlet Add-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 789 } 790 } 791 END { 792 Get-DynamicParameterMap -Refresh | Out-Null 793 } 794 } 795 796 ######################################################################################################################## 797 # NAME 798 #     Remove-PSSnapin 799 # 800 # SYNOPSIS 801 #     Wraps the Remove-PSSnapin cmdlet. Consult the Remove-PSSnapin cmdlet help for more information. 802 # 803 # SYNTAX 804 #     Consult the Remove-PSSnapin cmdlet help for the syntax. 805 # 806 # DETAILED DESCRIPTION 807 #     Wraps the Remove-PSSnapin cmdlet. After the Remove-PSSnapin cmdlet returns, it recreates the dynamic parameter 808 #     map. 809 # 810 # NOTES 811 #     Consult the Remove-PSSnapin cmdlet help for more information on syntax, detailed description, input types, return 812 #     types, notes, examples, and related links. 813 # 814 # RELATED LINKS 815 #     Remove-PSSnapin (cmdlet) 816 # 817 818 Function Remove-PSSnapin { 819 BEGIN { 820 } 821 PROCESS { 822 if ($_) { 823 Invoke-Expression "`$_ | Invoke-Cmdlet Remove-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 824 } else { 825 Invoke-Expression "Invoke-Cmdlet Remove-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 826 } 827 } 828 END { 829 Get-DynamicParameterMap -Refresh | Out-Null 830 } 831 } 832 833 ######################################################################################################################## 834 # NAME 835 #     Get-Help 836 # 837 # SYNOPSIS 838 #     Wraps the Get-Help cmdlet and integrates dynamic parameter help information. Consult the Get-Help cmdlet help for 839 #     more information. 840 # 841 # SYNTAX 842 #     Consult the Get-Help cmdlet help for the syntax. 843 # 844 # DETAILED DESCRIPTION 845 #     Wraps the Get-Help cmdlet and integrates dynamic parameter help information. By default, dynamic parameters are 846 #     not included in the help information for cmdlets. Consult the Get-Help cmdlet help or the provider specific help 847 #     for more information. 848 # 849 # INPUT TYPE 850 #     Consult the Get-Help cmdlet help for more information. 851 # 852 # RETURN TYPE 853 #     Consult the Get-Help cmdlet help for more information. 854 # 855 # NOTES 856 #     For more information, consult the help information for the Get-Help cmdlet. 857 # 858 #     If multiple providers include a dynamic parameter with the same name but a different type or documentation, the 859 #     documentation for the last one found will be used. 860 # 861 #     -------------------------- EXAMPLE 1 -------------------------- 862 # 863 #     C:\PS>get-help * -parameter -codesigningcert 864 # 865 # 866 #     This command gets all commands that have a CodeSigningCert parameter. This will include all cmdlets that support 867 #     the dynamic CodeSigningCert parameter, including those cmdlets whose documentation specifically includes the 868 #     CodeSigningCert parameter as well as those whose documentation does not specifically include the CodeSigningCert 869 #     parameter but who support the parameter through a dynamic parameter that is included in a specific provider. 870 # 871 # RELATED LINKS 872 #     Get-Help (cmdlet) 873 # 874 875 Function Get-Help { 876 BEGIN { 877 $resourceManager = New-Object -TypeName System.Resources.ResourceManager('HelpDisplayStrings', [System.Reflection.Assembly]::GetExecutingAssembly()) 878 $falseString = $resourceManager.GetString('FalseShort') 879 $namedString = $resourceManager.GetString('NamedParameter') 880 881 $createDynamicParameterHelpInfoScriptBlock = { 882 param( 883 $dynamicParameterName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterName')), 884 $dynamicParameterInfo = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterInfo')) 885 ) 886 887 $shortTypeName = Invoke-Expression "[$($dynamicParameterInfo.Type.Name)].Name" 888 $dynamicParameterHelpInfoBase = New-Object -TypeName System.Management.Automation.PSObject 889 $dynamicParameterHelpInfoBase | Add-Member -Name type -MemberType NoteProperty -Value $dynamicParameterInfo.Type 890 $dynamicParameterHelpInfoBase | Add-Member -Name defaultValue -MemberType NoteProperty -Value $(if ($dynamicParameterInfo.Type.Name -eq 'System.Management.Automation.SwitchParameter') { $falseString } else { $null }) 891 $dynamicParameterHelpInfoBase | Add-Member -Name possibleValues -MemberType NoteProperty -Value $dynamicParameterInfo.PossibleValues 892 $dynamicParameterHelpInfoBase | Add-Member -Name name -MemberType NoteProperty -Value $dynamicParameterName 893 $dynamicParameterHelpInfoBase | Add-Member -Name parameterValue -MemberType NoteProperty -Value $shortTypeName 894 $dynamicParameterHelpInfoBase | Add-Member -Name description -MemberType NoteProperty -Value $dynamicParameterInfo.Description 895 $dynamicParameterHelpInfo = New-Object -TypeName System.Management.Automation.PSObject($dynamicParameterHelpInfoBase) 896 $dynamicParameterHelpInfo.PSObject.TypeNames.Clear() 897 $dynamicParameterHelpInfo.PSObject.TypeNames.Add('MamlCommandHelpInfo#parameter') 898 $dynamicParameterHelpInfo | Add-Member -Name required -MemberType NoteProperty -Value $falseString 899 $dynamicParameterHelpInfo | Add-Member -Name variableLength -MemberType NoteProperty -Value ($dynamicParameterInfo.Type.Name -eq 'System.String').ToString().ToLower() 900 $dynamicParameterHelpInfo | Add-Member -Name globbing -MemberType NoteProperty -Value $falseString 901 $dynamicParameterHelpInfo | Add-Member -Name pipelineInput -MemberType NoteProperty -Value $falseString 902 $dynamicParameterHelpInfo | Add-Member -Name position -MemberType NoteProperty -Value $namedString 903 $dynamicParameterHelpInfo | Add-Member -Name isDynamic -MemberType NoteProperty -Value $true 904 $dynamicParameterHelpInfo 905 } 906 907 $updateParametersScriptBlock = { 908 param( 909 [REF]$parameters = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Parameters')), 910 $dynamicParameterName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterName')), 911 $dynamicParameterInfo = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterInfo')) 912 ) 913 914 if (@($parameters.Value.parameter | Where-Object { $_.Name -eq $dynamicParameterName }).Count -eq 0) { 915 $parameters.Value.parameter = [System.Management.Automation.PSObject[]]($parameters.Value.parameter + $(&$createDynamicParameterHelpInfoScriptBlock $dynamicParameterName $dynamicParameterInfo)) 916 } 917 } 918 919 $parameterMap = Get-DynamicParameterMap 920 921 foreach ($cmdlet in $parameterMap.Keys) { 922 Invoke-Cmdlet Get-Help -Name $cmdlet -Category Cmdlet -ErrorAction SilentlyContinue | ForEach-Object { 923 foreach ($provider in $parameterMap[$cmdlet].Keys) { 924 foreach ($dynamicParameter in $parameterMap[$cmdlet][$provider].Keys) { 925 &$updateParametersScriptBlock ([REF]($_.Parameters)) $dynamicParameter $parameterMap[$cmdlet][$provider][$dynamicParameter] 926 } 927 if (@($_.Syntax.syntaxItem | Where-Object { $_.Name -eq "[$provider]`r`n$cmdlet" }).Count -eq 0) { 928 $_.Syntax.syntaxItem += &{ 929 foreach ($parameterSet in $_.Syntax.syntaxItem | Where-Object { $_.name -notmatch "`r`n" }) { 930 $syntaxItem = New-Object -TypeName System.Management.Automation.PSObject 931 $syntaxItem.PSObject.TypeNames.Clear() 932 $syntaxItem.PSObject.TypeNames.Add('MamlCommandHelpInfo#syntaxItem') 933 $syntaxItem | Add-Member -Name name -MemberType NoteProperty -Value "[$provider]`r`n$cmdlet" 934 $syntaxItem | Add-Member -Name parameter -MemberType NoteProperty -Value $parameterSet.parameter 935 foreach ($dynamicParameter in $parameterMap[$cmdlet][$provider].Keys) { 936 &$updateParametersScriptBlock ([REF]($syntaxItem)) $dynamicParameter $parameterMap[$cmdlet][$provider][$dynamicParameter] 937 } 938 $syntaxItem 939 } 940 } 941 } 942 } 943 } 944 } 945 } 946 PROCESS { 947 if ($_) { 948 Invoke-Expression "`$_ | Invoke-Cmdlet Get-Help $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 949 } else { 950 Invoke-Expression "Invoke-Cmdlet Get-Help $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })" 951 } 952 } 953 END { 954 } 955 } 956 957 ######################################################################################################################## 958 959 if ($MyInvocation.InvocationName -eq '&') { 960 Write-Warning @" 961 The functions and aliases in '$(Split-Path -Path $MyInvocation.MyCommand.Definition -Leaf)' were not added to your PowerShell session properly. To add these to your PowerShell session or profile, you must dot-source the file. To do this, execute the following command: 962 . '$($MyInvocation.MyCommand.Definition)' 963 "@ 964 } 965 966 ########################################################################################################################


Plain-text code:
########################################################################################################################
#                                                                                                                      #
# File:             CmdletExtensionLibrary.ps1                                                                         #
# Author:           Kirk Munro                                                                                         #
# Author's Blog:    http://poshoholic.com                                                                              #
# Revision:         1.0.3                                                                                              #
# Contents:         A collection of functions designed with two purposes in mind:                                      #
#                   1. To seamlessly extend published cmdlet functionality transparently to the end user               #
#                   2. To wrap common operations in fully documented, well named functions designed to extend          #
#                      PowerShell's core set of features                                                               #
#                   In the first case, every effort is made to ensure that the original cmdlet and the corresponding   #
#                   function can be used interchangeably in scripts without requiring any modifications. The only      #
#                   known exception to this is when scripts are modified to explicitly use some information that is    #
#                   only exposed by the function extending the cmdlet.                                                 #
#                   In the second case, functionality is being exposed as functions to solicit community feedback. It  #
#                   is my intention to publish these sorts of functions as cmdlets in the future.                      #
# History:          v1.0.0 - Initial release designed to enhance support for dynamic parameters in the PowerShell help #
#                            system                                                                                    #
#                   v1.0.1 - Addition of Invoke-Member function to allow users to invoke any member on a data set      #
#                            without having to use ForEach-Object                                                      #
#                   v1.0.2 - Fixed grs alias for Get-PSResourceString                                                  #
#                          - Added ... alias for Invoke-Member                                                         #
#                          - Added pipeline support to Invoke-Cmdlet, Add-PSSnapin, Remove-PSSnapin and Get-Help       #
#                   v1.0.3 - Added retrieval of Microsoft.PowerShell.ConsoleHost strings to Get-PSResourceString       #
#                          - Added -list parameter to Get-PSResourceString                                             #
#                          - Added more examples to Get-PSResourceString documentation (comments)                      #
#                          - Added Should-Process function to provide -whatif, -confirm and -verbose support to other  #
#                            functions                                                                                 #
#                          - Added -whatif, -confirm and -verbose support to Invoke-Member function                    #
#                                                                                                                      #
########################################################################################################################

########################################################################################################################
# NAME
#     Get-PSResourceString
#
# SYNOPSIS
#     Returns a resource string that is looked up in the System.Management.Automation namespace or the
#     Microsoft.PowerShell.ConsoleHost namespace, or a list of resource root names or resource identifiers that are
#     available.
#
# SYNTAX
#     Get-PSResourceString [-baseName] <string> [-resourceId] <string> [[-defaultValue] <string>]
#     [[-culture] <System.Globalization.CultureInfo>]
#     Get-PSResourceString [[-baseName] <string>] -list
#
# DETAILED DESCRIPTION
#     The Get-PSResourceString function returns a resource string that is looked up in the System.Management.Automation
#     namespace or the Microsoft.PowerShell.ConsoleHost namespace, or a list of resource root names or resource
#     identifiers that are available. If a resource string was requested and it is not found, the default value (if
#     present) will be returned.
#
# PARAMETERS
#     -baseName <string>
#         Specifies the root name of the resources.
#
#         Required?                    true
#         Position?                    1
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -resourceId <string>
#         Specifies the identifier of the resource that is being retrieved.
#
#         Required?                    true
#         Position?                    2
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -defaultValue <string>
#         Specifies the default value for the resource string. If the string is not found, the default value will be
#         returned.
#
#         Required?                    false
#         Position?                    3
#         Default value                null
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -culture <System.Globalization.CultureInfo>
#         Specifies the culture to use when looking up the resource string.
#
#         Required?                    false
#         Position?                    4
#         Default value                $host.CurrentCulture
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -list <Switch>
#         When this parameter is used by itself, this function outputs the root names that are available. When this
#         parameter is used in conjunction with the baseName parameter, this function outputs the resource identifiers
#         that are availab.e
#
#         Required?                    false
#         Position?                    named
#         Default value                false
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
# INPUT TYPE
#     String,System.Globalization.CultureInfo,Switch
#
# RETURN TYPE
#     String,String[]
#
# NOTES
#     For more information the System.Globalization.CultureInfo type consult the relevant MSDN documentation.
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#     C:\PS>get-psresourcestring -list
#
#
#     This command retrieves the list of resource root names that are available.
#
#
#     -------------------------- EXAMPLE 2 --------------------------
#
#     C:\PS>get-psresourcestring -basename helpdisplaystrings -list 
#
#
#     This command retrieves the list of resource strings in the resource root called 'helpdisplaystrings' using the
#     current culture.
#
#
#     -------------------------- EXAMPLE 3 --------------------------
#
#     C:\PS>get-psresourcestring -list | foreach-object { get-psresourcestring -basename $_ -list }
#
#
#     This command retrieves all resource strings that are available using the current culture.
#
#
#     -------------------------- EXAMPLE 4 --------------------------
#
#     C:\PS>get-psresourcestring -basename helpdisplaystrings -resourceid falseshort
#
#
#     This command retrieves the string associated with the 'falseshort' resource id using the current culture.
#
#

Function Get-PSResourceString {
	param(
		[string]$baseName = $null,
		[string]$resourceId = $null,
		[string]$defaultValue = $null,
		[System.Globalization.CultureInfo]$culture = $host.CurrentCulture,
		[Switch]$list
	)
	
	if ($list -and ($resourceId -or $defaultValue)) {
		throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'AmbiguousParameterSet')
	}

	if ($list) {
		$engineAssembly = [System.Reflection.Assembly]::GetExecutingAssembly()
		$hostAssembly = [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.PowerShell.ConsoleHost')
		if ($baseName) {
			$engineAssembly.GetManifestResourceNames() | Where-Object { $_ -eq "$baseName.resources" } | ForEach-Object {
				$resourceManager = New-Object -TypeName System.Resources.ResourceManager($baseName, $engineAssembly)
				$resourceManager.GetResourceSet($host.CurrentCulture,$true,$true) | Add-Member -Name BaseName -MemberType NoteProperty -Value $baseName -Force -PassThru | ForEach-Object {
					$_.PSObject.TypeNames.Clear()
					$_.PSObject.TypeNames.Add('ResourceString')
					$_ | Write-Output
				}
			}
			$hostAssembly.GetManifestResourceNames() | Where-Object { $_ -eq "$baseName.resources" } | ForEach-Object {
				$resourceManager = New-Object -TypeName System.Resources.ResourceManager($baseName, $hostAssembly)
				$resourceManager.GetResourceSet($host.CurrentCulture,$true,$true) | Add-Member -Name BaseName -MemberType NoteProperty -Value $baseName -Force -PassThru | ForEach-Object {
					$_.PSObject.TypeNames.Clear()
					$_.PSObject.TypeNames.Add('ResourceString')
					$_ | Write-Output
				}
			}
		} else {
			$engineAssembly.GetManifestResourceNames() | Where-Object { $_ -match '\.resources$' } | ForEach-Object { $_.Replace('.resources','') }
			$hostAssembly.GetManifestResourceNames() | Where-Object { $_ -match '\.resources$' } | ForEach-Object { $_.Replace('.resources','') }
		}
	} else {
		if (-not $baseName) {
			throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'BaseName')
		}
		if (-not $resourceId) {
			throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'ResourceId')
		}
		if (-not $global:PSResourceStringTable) {
			$engineAssembly = [System.Reflection.Assembly]::GetExecutingAssembly()
			$hostAssembly = [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.PowerShell.ConsoleHost')
			if ($engineAssembly.GetManifestResourceNames() -contains "$baseName.resources") {
				New-Variable -Scope Global -Name PSResourceStringTable -Value @{} -Description 'A cache of PowerShell resource strings. To access data in this table, use Get-ResourceString.'
				$global:PSResourceStringTable['EngineAssembly'] = @{'Assembly'=$engineAssembly;'Cultures'=@{}}
				$global:PSResourceStringTable['HostAssembly'] = @{'Assembly'=$hostAssembly;'Cultures'=@{}}
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly));
				$global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}};
			} elseif ($hostAssembly.GetManifestResourceNames() -contains "$baseName.resources") {
				New-Variable -Scope Global -Name PSResourceStringTable -Value @{} -Description 'A cache of PowerShell resource strings. To access data in this table, use Get-ResourceString.'
				$global:PSResourceStringTable['EngineAssembly'] = @{'Assembly'=$engineAssembly;'Cultures'=@{}}
				$global:PSResourceStringTable['HostAssembly'] = @{'Assembly'=$hostAssembly;'Cultures'=@{}}
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly));
				$global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}};
			}
		} elseif ($global:PSResourceStringTable.EngineAssembly.Assembly.GetManifestResourceNames() -contains "$baseName.resources") {
			if (-not $global:PSResourceStringTable.EngineAssembly.Cultures.ContainsKey($culture.Name)) {
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly));
				$global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}};
			} elseif (-not $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name].ContainsKey($baseName)) {
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.EngineAssembly.Assembly));
				$global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name][$baseName] = @{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)};
			}
		} elseif ($global:PSResourceStringTable.HostAssembly.Assembly.GetManifestResourceNames() -contains "$baseName.resources") {
			if (-not $global:PSResourceStringTable.HostAssembly.Cultures.ContainsKey($culture.Name)) {
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly));
				$global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name] = @{$baseName=@{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)}};
			} elseif (-not $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name].ContainsKey($baseName)) {
				$resourceManager = (New-Object -TypeName System.Resources.ResourceManager($baseName, $global:PSResourceStringTable.HostAssembly.Assembly));
				$global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name][$baseName] = @{'ResourceManager'=$resourceManager;'Strings'=$resourceManager.GetResourceSet($culture,$true,$true)};
			}
		}

		$resourceString = $null
		if ($global:PSResourceStringTable) {
			if ($global:PSResourceStringTable.EngineAssembly.Cultures -and $global:PSResourceStringTable.EngineAssembly.Cultures.ContainsKey($culture.Name) -and $global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name].ContainsKey($baseName)) {
				$resourceString = ($global:PSResourceStringTable.EngineAssembly.Cultures[$culture.Name][$baseName].Strings | Where-Object { $_.Name -eq $resourceId }).Value
			} elseif ($global:PSResourceStringTable.HostAssembly.Cultures -and $global:PSResourceStringTable.HostAssembly.Cultures.ContainsKey($culture.Name) -and $global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name].ContainsKey($baseName)) {
				$resourceString = ($global:PSResourceStringTable.HostAssembly.Cultures[$culture.Name][$baseName].Strings | Where-Object { $_.Name -eq $resourceId }).Value
			}
		}
		if (-not $resourceString) {
			$resourceString = $defaultValue
		}
		
		return $resourceString
	}
}

if (-not (Get-Alias -Name grs -ErrorAction SilentlyContinue)) {
	New-Alias -Name grs -Value Get-PSResourceString -Description 'Returns a resource string that is looked up in the System.Management.Automation namespace.'
}

########################################################################################################################
# NAME
#     Should-Process
#
# SYNOPSIS
#     Determines whether the specified operation should be performed on the target object.
#
# SYNTAX
#     Should-Process [-operation] <string> [-target] <string> [-whatIf]
#     Should-Process [-operation] <string> [-target] <string> [-confirmResponse] <REF> [[-confirmPrompt] <string>]
#     [-confirm]
#     Should-Process [-operation] <string> [-target] <string> [-verbose]
#
# DETAILED DESCRIPTION
#     Determines whether the specified operation should be performed on the target object. Returns true if the operation
#     should be performed. Should-Process never performs the operation; it just indicates whether or not it should be
#     performed to the caller. If the whatIf switch is used, Should-Process outputs strings indicating what it would do
#     if whatIf wasn't used. If the confirm switch is used, Should-Process asks the user to confirm that they want to
#     perform the operation. If the verbose switch is used, Should-Process writes verbose output for each operation that
#     will be performed on each target.
#
# PARAMETERS
#     -operation <string>
#         Specifies the name of the function that was invoked.
#
#         Required?                    true
#         Position?                    1
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -target <string>
#         Specifies the identifier of the object that is being used in the operation.
#
#         Required?                    true
#         Position?                    2
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -confirmResponse <REF>
#         Reference variable that retains any confirm question response that applies to all objects being processed.
#
#         Required?                    true
#         Position?                    3
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -confirmPrompt <string>
#         Specifies the string that will be displayed to the caller when the -confirm switch is used.
#
#         Required?                    false
#         Position?                    4
#         Default value                Are you sure you want to perform this action?
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -verbose <switch>
#         When this parameter is used, this function outputs verbose information to the host.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -confirm <switch>
#         When this parameter is used, this function requests confirmation from the user for the actions it will take.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -whatif <switch>
#         When this parameter is used, this function outputs what it would do without actually taking action.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
# INPUT TYPE
#     String,Ref,Switch
#
# RETURN TYPE
#     Boolean
#
# NOTES
#     Whatif takes precedence over confirm. Confirm takes precedence over verbose. If more than one switch parameter is
#     used, this precedence order will be followed and only the highest precedence switch will be used.
#
#     The ConfirmResponse reference parameter is used to determine if "YES to All" or "NO to all" has been previously
#     selected.
#
#     When using Should-Process inside of functions that can be used in a pipeline, you should initialize your reference
#     variable in the begin block and then call Should-Process in the process block.
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#
#	  C:\PS>function Stop-Calc ([Switch]$Verbose, [Switch]$Confirm, [Switch]$Whatif) {
#         $ConfirmResponse = $null
#         foreach ($p in Get-Process calc) {
#             if (Should-Process Stop-Calc $p.Id ([REF]$ConfirmResponse) `
#                 -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif) {
#                 Stop-Process $p.Id
#             }
#         }
#     }
#     C:\PS>stop-calc -confirm
#
#
#     This prompts you to confirm you want to stop all calc processes when using the simple Stop-Process function.
#
#
# RELATED LINKS
#     about_commonparameters
#     about_ref
#

Function Should-Process {
	param(
		[string]$Operation = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Operation')),
		[string]$Target = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Target')),
		[REF]$ConfirmResponse = ([REF]$null),
		[string]$ConfirmPrompt = $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessWarningFallback') -f $null),
		[Switch]$Verbose,
		[Switch]$Confirm,
		[Switch]$Whatif
	)

	if ($ConfirmResponse.Value -eq $false) {
		return $false
	} elseif ($ConfirmResponse.Value -eq $true) {
		return $true
	}

	if ($Whatif) {
		Write-Host $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessWhatIfMessage') -f $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target))
		return $false
	} elseif ($Confirm) {
		$ConfirmText = @"
$(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'InquireCaptionDefault')
$ConfirmPrompt
$($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target)
"@
		Write-Host $ConfirmText
		$Yes = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueOneLabel')
		$All = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueAllLabel')
		$No = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipOneLabel')
		$None = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipAllLabel')
		$Suspend = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'PauseLabel')
		$Help = $(Get-PSResourceString -BaseName 'ConsoleHostUserInterfaceStrings' -ResourceId 'PromptForChoiceHelp')

		$YesHotkey = $Yes.ToUpper()[$Yes.IndexOf('&') + 1]
		$AllHotkey = $All.ToUpper()[$All.IndexOf('&') + 1]
		$NoHotkey = $No.ToUpper()[$No.IndexOf('&') + 1]
		$NoneHotkey = $None.ToUpper()[$None.IndexOf('&') + 1]
		$SuspendHotkey = $Suspend.ToUpper()[$Suspend.IndexOf('&') + 1]
		$HelpHotkey = $Help.ToUpper()[$Help.IndexOf('[') + 1]

		$YesHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueOneHelpMessage')
		$AllHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ContinueAllHelpMessage')
		$NoHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipOneHelpMessage')
		$NoneHelp = $(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'SkipAllHelpMessage')
		$SuspendHelp = $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'PauseHelpMessage') -f 'exit')
		$DefaultValue = $($(Get-PSResourceString -BaseName 'ConsoleHostUserInterfaceStrings' -ResourceId 'DefaultChoicePrompt') -f $YesHotKey)

		while ($true) {
			$answer = Read-Host @"
[$YesHotkey] $($Yes.Replace('&',''))  [$AllHotkey] $($All.Replace('&',''))  [$NoHotkey] $($No.Replace('&',''))  [$NoneHotkey] $($None.Replace('&',''))  [$SuspendHotkey] $($Suspend.Replace('&',''))  $Help  $DefaultValue
"@
			switch ($answer) {
				"$YesHotkey"		{ return $true }
				""					{ return $true }
				"$AllHotkey"		{ $ConfirmResponse.Value = $true; return $true }
				"$NoHotkey"			{ return $false }
				"$NoneHotkey"		{ $ConfirmResponse.Value = $false; return $false }
				"$SuspendHotkey"	{ $host.EnterNestedPrompt(); Write-Host $ConfirmText }
				"$HelpHotkey"		{ Write-Host @"
$YesHotkey - $YesHelp
$AllHotkey - $AllHelp
$NoHotkey - $NoHelp
$NoneHotkey - $NoneHelp
$SuspendHotkey - $SuspendHelp
"@
				}
			}
		}
	} elseif ($verbose) {
		Write-Verbose $($(Get-PSResourceString -BaseName 'CommandBaseStrings' -ResourceId 'ShouldProcessMessage') -f $Operation,$Target)
	}

	return $true
}

if (-not (Get-Alias -Name sps -ErrorAction SilentlyContinue)) {
	New-Alias -Name sps -Value Should-Process -Description 'Determines whether the specified operation should be performed on the target object. Returns true if the operation should be performed.'
}

########################################################################################################################
# NAME
#     Invoke-Member
#
# SYNOPSIS
#     Invoke members of each of a set of input objects.
#
# SYNTAX
#     Invoke-Member [-name] <string[]> [[-inputObject] <psobject>] [-whatif] [-confirm] [-verbose]
#
# DETAILED DESCRIPTION
#     Invokes a member of each of a set of input objects. The input objects can be piped to the cmdlet or specified by
#     using the InputObject parameter. If an input object does not have the specified member, it will be skipped. The
#     results of the invocation of the members are passed down the pipeline.
#
# PARAMETERS
#     -name <string[]>
#         Specifies the name of the member to invoke. For members that accept parameters, pass the member name with the
#         parameters in parentheses inside of quotation marks.
#
#         Required?                    true
#         Position?                    1
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -inputObject <psobject>
#         Accepts an object for which the member will be invoked. Enter a variable that contains the objects or type a
#         command or expression that gets the objects.
#
#         Required?                    false
#         Position?                    2
#         Default value
#         Accept pipeline input?       true (ByValue)
#         Accept wildcard characters?  false
#
#     -whatIf
#         Describes what would happen if you executed the command without actually executing the command.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     -confirm
#         Prompts you for confirmation before executing the command.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     <CommonParameters>
#         This cmdlet supports the common parameters: -Verbose. For more information, type,
#         "get-help about_commonparameters".
#
# INPUT TYPE
#     String[],PSObject
#
# RETURN TYPE
#     Object
#
# NOTES
#     When a collection of objects is passed in via the InputObject parameter, the member will be invoked on the
#     collection object, not on the items in the collection.
#
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#     C:\PS>get-childitem -literalpath C:\ -force | invoke-member -name PSObject.typenames | select-object -unique 
#
#
#     This command retrieves all unique base and derived object type names.
#
#
#     -------------------------- EXAMPLE 2 --------------------------
#
#     C:\PS>get-help -category cmdlet | sort-object -property name | invoke-member -name syntax
#
#
#     This command retrieves a sorted list of the syntax for all cmdlets based on help information.
#
#
# RELATED LINKS
#     Get-Member
#     Invoke-Expression
#     ForEach-Object
#

Function Invoke-Member {
	param (
		[string[]]$name = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Name')),
		$inputObject = $null,
		[Switch]$whatif,
		[Switch]$confirm,
		[Switch]$verbose
	)

	BEGIN {
		$confirmResponse = $null
	}
	PROCESS {
		if ($inputObject -and $_) {
			throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'InputObjectNotBound')
			break
		} elseif ($inputObject) {
			foreach ($member in $name) {
				if (Should-Process Invoke-Member $member ([REF]$confirmResponse) -Verbose:$verbose -Confirm:$confirm -Whatif:$whatif) {
					Invoke-Expression "`$inputObject.$member"
				}
			}
		} elseif ($_) {
			foreach ($member in $name) {
				if (Should-Process Invoke-Member $member ([REF]$confirmResponse) -Verbose:$verbose -Confirm:$confirm -Whatif:$whatif) {
					Invoke-Expression "`$_.$member"
				}
			}
		} else {
			throw $(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'AmbiguousParameterSet')
		}
	}
	END {
	}
}

if (-not (Get-Alias -Name im -ErrorAction SilentlyContinue)) {
	New-Alias -Name im -Value Invoke-Member -Description 'Invokes a member of each of a set of input objects.'
}

if (-not (Get-Alias -Name ... -ErrorAction SilentlyContinue)) {
	New-Alias -Name ... -Value Invoke-Member -Description 'Invokes a member of each of a set of input objects.'
}

########################################################################################################################
# NAME
#     Invoke-Cmdlet
#
# SYNOPSIS
#     Invokes a cmdlet by using its fully qualified name.
#
# SYNTAX
#     Invoke-Cmdlet [-cmdletName] <string> [<CmdletParameters>]
#
# DETAILED DESCRIPTION
#     The Invoke-Cmdlet function invokes a cmdlet directly by using its fully qualified name. This allows cmdlets to be
#     invoked even if there are functions or aliases with the same name. It follows Microsoft's house rules whereby the
#     core snapins take precedence over added snapins.
#
# PARAMETERS
#     -cmdletName <string>
#         Specifies the name of a cmdlet. When this parameter is used, this function invokes the cmdlet identified by
#         this parameter.
#
#         Required?                    true
#         Position?                    1
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
#     <CmdletParameters>
#         This cmdlet supports all parameters that are supported by the cmdlet being invoked. Consult the cmdlet help
#         parameter list for more information.
#
# INPUT TYPE
#     String
#
# RETURN TYPE
#     Consult the cmdlet help return type for more information.
#
# NOTES
#     For more information, consult the help documentation for the cmdlet that you are invoking.
#
#     Cmdlets that have a cmdletName parameter are not supported by this cmdlet.
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#     C:\PS>invoke-cmdlet get-help * -parameter codesigningcert
#
#
#     This command gets all commands that have a CodeSigningCert parameter. This will only include those cmdlets whose
#     documentation specifically includes the CodeSigningCert parameter unless the CmdletExtensionLibrary is being used
#     and the Get-Help function has been called at least once.
#
# RELATED LINKS
#     Get-Command
#     Invoke-Expression
#

Function Invoke-Cmdlet {
	param([string]$cmdletName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'CmdletName')))

	BEGIN {
		$matchingCmdlets = Microsoft.PowerShell.Core\Get-Command -CommandType cmdlet -Name $cmdletName
		if (@($matchingCmdlets).Count -eq 0) {
			throw $($(Get-PSResourceString -BaseName 'DiscoveryExceptions' -ResourceId 'CmdletNotFoundException') -f 'CmdletName',$cmdletName)
		}
		$cmdletName = $null
		foreach ($cmdlet in $matchingCmdlets) {
			if ($cmdlet.PSSnapin.IsDefault) {
				$cmdletName = "$($cmdlet.PSSnapin.Name)\$($cmdlet.Name)"
				break
			} elseif (-not $cmdletName) {
				$cmdletName = "$($cmdlet.PSSnapin.Name)\$($cmdlet.Name)"
			}
		}
	}
	PROCESS {
		if ($_) {
			Microsoft.PowerShell.Utility\Invoke-Expression "`$_ | $cmdletName $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		} else {
			Microsoft.PowerShell.Utility\Invoke-Expression "$cmdletName $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		}
	}
	END {
	}
}

if (-not (Get-Alias -Name ic -ErrorAction SilentlyContinue)) {
	New-Alias -Name ic -Value Invoke-Cmdlet -Description 'Invokes a cmdlet by using its fully qualified name.'
}

########################################################################################################################
# NAME
#     Get-DynamicParameterMap
#
# SYNOPSIS
#     Displays a mapping of cmdlet names to dynamic parameters.
#
# SYNTAX
#     Get-DynamicParameterMap [-refresh]
#
# DETAILED DESCRIPTION
#     The Get-DynamicParameterMap function extracts the dynamic parameter help information from the PSProvider help
#     documentation and builds a map of cmdlet names to dynamic parameters. If the map has already been built, it is
#     immediately returned. If the Refresh switch parameter is used, the map is rebuilt.
#
# PARAMETERS
#     -refresh <switch>
#         When this parameter is used, this function reloads the dynamic parameter map before returning it to the
#         caller.
#
#         Required?                    false
#         Position?                    named
#         Default value
#         Accept pipeline input?       false
#         Accept wildcard characters?  false
#
# INPUT TYPE
#     Switch
#
# RETURN TYPE
#     Associative Array
#
# NOTES
#     If multiple providers include a dynamic parameter with the same name but a different type or documentation, both
#     versions will be used.
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#     C:\PS>get-dynamicparametermap
#
#
#     This command retrieves all dynamic parameter help documentation from the PSProviders that are loaded and returns
#     an associative array mapping cmdlet names to dynamic parameters.
#
# RELATED LINKS
#     Invoke-Cmdlet
#     about_associative_array
#

Function Get-DynamicParameterMap {
	param ([Switch]$Refresh)
	
	if (($refresh) -or (-not $global:dynamicParameterMap)) {
		Remove-Variable -Force -Scope Global -Name dynamicParameterMap -ErrorAction SilentlyContinue
		New-Variable -Scope Global -Name dynamicParameterMap -Value @{} -Description 'Maps cmdlet names to dynamic parameter help information. This variable is managed via custom scripts that provide better support for dynamic parameters and should only be used by those custom scripts.'
		Invoke-Cmdlet Get-Help -Category Provider -ErrorAction SilentlyContinue | ForEach-Object {
			$providerHelp = $_
			$_.DynamicParameters.DynamicParameter | Where-Object { $_.CmdletSupported } | ForEach-Object {
				$type = New-Object -TypeName System.Management.Automation.PSObject
				$type.PSObject.TypeNames.Clear()
				$type.PSObject.TypeNames.Add('MamlCommandHelpInfo#type')
				$type | Add-Member -Name name -MemberType NoteProperty -Value $_.Type.Name
				$type | Add-Member -Name uri -MemberType NoteProperty -Value $([String]$null)
				$description = New-Object -TypeName System.Management.Automation.PSObject
				$description.PSObject.TypeNames.Clear()
				$description.PSObject.TypeNames.Add('MamlParaTextItem')
				$description.PSObject.TypeNames.Add('MamlTextItem')
				$description | Add-Member -Name Text -MemberType NoteProperty -Value $_.Description
				$descriptionArray = @($description)
				foreach ($cmdletSupported in $_.CmdletSupported.Split(', ', [StringSplitOptions]::RemoveEmptyEntries)) {
					if (-not $global:dynamicParameterMap.ContainsKey($cmdletSupported)) {
						$global:dynamicParameterMap[$cmdletSupported] = @{$providerHelp.Name=@{$_.Name=@{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues}}}
					} elseif (-not $global:dynamicParameterMap[$cmdletSupported].ContainsKey($providerHelp.Name)) {
						$global:dynamicParameterMap[$cmdletSupported][$providerHelp.Name] = @{$_.Name=@{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues}}
					} else {
						$global:dynamicParameterMap[$cmdletSupported][$providerHelp.Name][$_.Name] = @{'Description'=$descriptionArray;'Type'=$type;'PossibleValues'=$_.PossibleValues}
					}
				}
			}
		}
		foreach ($cmdlet in $global:dynamicParameterMap.Keys) {
			Invoke-Cmdlet Get-Help -Name $cmdlet -Category Cmdlet -ErrorAction SilentlyContinue | ForEach-Object {
				$_.Parameters.parameter = [System.Management.Automation.PSObject[]]($_.Parameters.parameter | Where-Object { $_.isDynamic -ne $true })
				$_.Syntax.syntaxItem = [System.Management.Automation.PSObject[]]($_.Syntax.syntaxItem | Where-Object { $_.name -notmatch "`r`n" })
			}
		}
	}
	$global:dynamicParameterMap
}

########################################################################################################################
# NAME
#     Add-PSSnapin
#
# SYNOPSIS
#     Wraps the Add-PSSnapin cmdlet. Consult the Add-PSSnapin cmdlet help for more information.
#
# SYNTAX
#     Consult the Add-PSSnapin cmdlet help for the syntax.
#
# DETAILED DESCRIPTION
#     Wraps the Add-PSSnapin cmdlet. After the Add-PSSnapin cmdlet returns, it recreates the dynamic parameter map.
#
# NOTES
#     Consult the Add-PSSnapin cmdlet help for more information on syntax, detailed description, input types, return
#     types, notes, examples, and related links.
#
# RELATED LINKS
#     Add-PSSnapin (cmdlet)
#

Function Add-PSSnapin {
	BEGIN {
	}
	PROCESS {
		if ($_) {
			Invoke-Expression "`$_ | Invoke-Cmdlet Add-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		} else {
			Invoke-Expression "Invoke-Cmdlet Add-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		}
	}
	END {
		Get-DynamicParameterMap -Refresh | Out-Null
	}
}

########################################################################################################################
# NAME
#     Remove-PSSnapin
#
# SYNOPSIS
#     Wraps the Remove-PSSnapin cmdlet. Consult the Remove-PSSnapin cmdlet help for more information.
#
# SYNTAX
#     Consult the Remove-PSSnapin cmdlet help for the syntax.
#
# DETAILED DESCRIPTION
#     Wraps the Remove-PSSnapin cmdlet. After the Remove-PSSnapin cmdlet returns, it recreates the dynamic parameter
#     map.
#
# NOTES
#     Consult the Remove-PSSnapin cmdlet help for more information on syntax, detailed description, input types, return
#     types, notes, examples, and related links.
#
# RELATED LINKS
#     Remove-PSSnapin (cmdlet)
#

Function Remove-PSSnapin {
	BEGIN {
	}
	PROCESS {
		if ($_) {
			Invoke-Expression "`$_ | Invoke-Cmdlet Remove-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		} else {
			Invoke-Expression "Invoke-Cmdlet Remove-PSSnapin $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		}
	}
	END {
		Get-DynamicParameterMap -Refresh | Out-Null
	}
}

########################################################################################################################
# NAME
#     Get-Help
#
# SYNOPSIS
#     Wraps the Get-Help cmdlet and integrates dynamic parameter help information. Consult the Get-Help cmdlet help for
#     more information.
#
# SYNTAX
#     Consult the Get-Help cmdlet help for the syntax.
#
# DETAILED DESCRIPTION
#     Wraps the Get-Help cmdlet and integrates dynamic parameter help information. By default, dynamic parameters are
#     not included in the help information for cmdlets. Consult the Get-Help cmdlet help or the provider specific help
#     for more information.
#
# INPUT TYPE
#     Consult the Get-Help cmdlet help for more information.
#
# RETURN TYPE
#     Consult the Get-Help cmdlet help for more information.
#
# NOTES
#     For more information, consult the help information for the Get-Help cmdlet.
#
#     If multiple providers include a dynamic parameter with the same name but a different type or documentation, the
#     documentation for the last one found will be used.
#
#     -------------------------- EXAMPLE 1 --------------------------
#
#     C:\PS>get-help * -parameter -codesigningcert
#
#
#     This command gets all commands that have a CodeSigningCert parameter. This will include all cmdlets that support
#     the dynamic CodeSigningCert parameter, including those cmdlets whose documentation specifically includes the
#     CodeSigningCert parameter as well as those whose documentation does not specifically include the CodeSigningCert
#     parameter but who support the parameter through a dynamic parameter that is included in a specific provider.
#
# RELATED LINKS
#     Get-Help (cmdlet)
#

Function Get-Help {
	BEGIN {
		$resourceManager = New-Object -TypeName System.Resources.ResourceManager('HelpDisplayStrings', [System.Reflection.Assembly]::GetExecutingAssembly())
		$falseString = $resourceManager.GetString('FalseShort')
		$namedString = $resourceManager.GetString('NamedParameter')
	
		$createDynamicParameterHelpInfoScriptBlock = {
			param(
				$dynamicParameterName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterName')),
				$dynamicParameterInfo = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterInfo'))
			)
	
			$shortTypeName = Invoke-Expression "[$($dynamicParameterInfo.Type.Name)].Name"
			$dynamicParameterHelpInfoBase = New-Object -TypeName System.Management.Automation.PSObject
			$dynamicParameterHelpInfoBase | Add-Member -Name type -MemberType NoteProperty -Value $dynamicParameterInfo.Type
			$dynamicParameterHelpInfoBase | Add-Member -Name defaultValue -MemberType NoteProperty -Value $(if ($dynamicParameterInfo.Type.Name -eq 'System.Management.Automation.SwitchParameter') { $falseString } else { $null })
			$dynamicParameterHelpInfoBase | Add-Member -Name possibleValues -MemberType NoteProperty -Value $dynamicParameterInfo.PossibleValues
			$dynamicParameterHelpInfoBase | Add-Member -Name name -MemberType NoteProperty -Value $dynamicParameterName
			$dynamicParameterHelpInfoBase | Add-Member -Name parameterValue -MemberType NoteProperty -Value $shortTypeName
			$dynamicParameterHelpInfoBase | Add-Member -Name description -MemberType NoteProperty -Value $dynamicParameterInfo.Description
			$dynamicParameterHelpInfo = New-Object -TypeName System.Management.Automation.PSObject($dynamicParameterHelpInfoBase)
			$dynamicParameterHelpInfo.PSObject.TypeNames.Clear()
			$dynamicParameterHelpInfo.PSObject.TypeNames.Add('MamlCommandHelpInfo#parameter')
			$dynamicParameterHelpInfo | Add-Member -Name required -MemberType NoteProperty -Value $falseString
			$dynamicParameterHelpInfo | Add-Member -Name variableLength -MemberType NoteProperty -Value ($dynamicParameterInfo.Type.Name -eq 'System.String').ToString().ToLower()
			$dynamicParameterHelpInfo | Add-Member -Name globbing -MemberType NoteProperty -Value $falseString
			$dynamicParameterHelpInfo | Add-Member -Name pipelineInput -MemberType NoteProperty -Value $falseString
			$dynamicParameterHelpInfo | Add-Member -Name position -MemberType NoteProperty -Value $namedString
			$dynamicParameterHelpInfo | Add-Member -Name isDynamic -MemberType NoteProperty -Value $true
			$dynamicParameterHelpInfo
		}
		
		$updateParametersScriptBlock = {
			param(
				[REF]$parameters = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'Parameters')),
				$dynamicParameterName = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterName')),
				$dynamicParameterInfo = $(throw $($(Get-PSResourceString -BaseName 'ParameterBinderStrings' -ResourceId 'ParameterArgumentValidationErrorNullNotAllowed') -f $null,'DynamicParameterInfo'))
			)
			
			if (@($parameters.Value.parameter | Where-Object { $_.Name -eq $dynamicParameterName }).Count -eq 0) {
				$parameters.Value.parameter = [System.Management.Automation.PSObject[]]($parameters.Value.parameter + $(&$createDynamicParameterHelpInfoScriptBlock $dynamicParameterName $dynamicParameterInfo))
			}
		}
	
		$parameterMap = Get-DynamicParameterMap
		
		foreach ($cmdlet in $parameterMap.Keys) {
			Invoke-Cmdlet Get-Help -Name $cmdlet -Category Cmdlet -ErrorAction SilentlyContinue | ForEach-Object {
				foreach ($provider in $parameterMap[$cmdlet].Keys) {
					foreach ($dynamicParameter in $parameterMap[$cmdlet][$provider].Keys) {
						&$updateParametersScriptBlock ([REF]($_.Parameters)) $dynamicParameter $parameterMap[$cmdlet][$provider][$dynamicParameter]
					}
					if (@($_.Syntax.syntaxItem | Where-Object { $_.Name -eq "[$provider]`r`n$cmdlet" }).Count -eq 0) {
						$_.Syntax.syntaxItem += &{
							foreach ($parameterSet in $_.Syntax.syntaxItem | Where-Object { $_.name -notmatch "`r`n" }) {
								$syntaxItem = New-Object -TypeName System.Management.Automation.PSObject
								$syntaxItem.PSObject.TypeNames.Clear()
								$syntaxItem.PSObject.TypeNames.Add('MamlCommandHelpInfo#syntaxItem')
								$syntaxItem | Add-Member -Name name -MemberType NoteProperty -Value "[$provider]`r`n$cmdlet"
								$syntaxItem | Add-Member -Name parameter -MemberType NoteProperty -Value $parameterSet.parameter
								foreach ($dynamicParameter in $parameterMap[$cmdlet][$provider].Keys) {
									&$updateParametersScriptBlock ([REF]($syntaxItem)) $dynamicParameter $parameterMap[$cmdlet][$provider][$dynamicParameter]
								}
								$syntaxItem
							}
						}
					}
				}
			}
		}
	}
	PROCESS {
		if ($_) {
			Invoke-Expression "`$_ | Invoke-Cmdlet Get-Help $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		} else {
			Invoke-Expression "Invoke-Cmdlet Get-Help $($passThruArgs = $args; for ($i = 0; $i -lt $passThruArgs.Count; $i++) { if ($passThruArgs[$i] -match '^-') { $passThruArgs[$i] } else { `"`$passThruArgs[$i]`" } })"
		}
	}
	END {
	}
}

########################################################################################################################

if ($MyInvocation.InvocationName -eq '&') {
	Write-Warning @"
The functions and aliases in '$(Split-Path -Path $MyInvocation.MyCommand.Definition -Leaf)' were not added to your PowerShell session properly. To add these to your PowerShell session or profile, you must dot-source the file. To do this, execute the following command:
. '$($MyInvocation.MyCommand.Definition)'
"@
}

########################################################################################################################





right
footer   footer
footer Sponsored by Quest Software • SAPIEN Technologies • Compellent • Microsoft Windows Server 2008 R2 footer
footer   footer