positional parameters and parameter sets

Hi All,

why does running get-service ‘background intelligent transfer service’ work?

We’re doing a book club in work, based on “powershell in a month of lunches” for powershell v2 (yes, i know it’s old, but none of our customers have gone to windows 2012 yet) and we’ve noticed that something we don’t think should work, works.

if we run “help get-service” we see:

SYNTAX Get-Service [[-Name] <string[]>] [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <s tring[]>] [-RequiredServices] [<CommonParameters>]
Get-Service -DisplayName &lt;string[]&gt; [-ComputerName &lt;string[]&gt;] [-DependentServices] [-Exclude &lt;string[]&gt;] [-Include
 &lt;string[]&gt;] [-RequiredServices] [&lt;CommonParameters&gt;]
so we take that to mean that if we type "get-service <string>" then "<string>" will be taken as input for the -Name parameter, not the display name parameter. the -DisplayName parameter isn't in square brackets, so must be typed if you are going to use a displayname value...

now, consider the BITS service:

Name : BITS DisplayName : Background Intelligent Transfer Service Status : Running DependentServices : {} ServicesDependedOn : {EventSystem, RpcSs} CanPauseAndContinue : False CanShutdown : True CanStop : True ServiceType : Win32ShareProcess
and if i run get-service BITS | gm i see:
Name MemberType Definition ---- ---------- ---------- Name AliasProperty Name = ServiceName <snip> DisplayName Property System.String DisplayName {get;set;} MachineName Property System.String MachineName {get;set;} ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;} ServiceName Property System.String ServiceName {get;set;} ServicesDependedOn Property System.ServiceProcess.ServiceController[] ServicesDependedOn {get;} ServiceType Property System.ServiceProcess.ServiceType ServiceType {get;} Site Property System.ComponentModel.ISite Site {get;set;} Status Property System.ServiceProcess.ServiceControllerStatus Status {get;}
so it looks like "Name" is definitely distinct from "DisplayName"; it's an alias for "ServiceName".

so, my original question… why does this work?

Nice catch, but it’s not quite what you think. When you call get-service ‘background intelligent transfer service’ , it really is binding to the Name parameter, not the DisplayName (which you can verify with Trace-Command, as shown below).

Trace-Command -Name ParameterBinding -PSHost -Expression { Get-Service 'Background Intelligent Transfer Service' }

DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args [Get-Service]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args [Get-Service]
DEBUG: ParameterBinding Information: 0 :     BIND arg [Background Intelligent Transfer Service] to parameter [Name]
DEBUG: ParameterBinding Information: 0 :         Binding collection parameter Name: argument type [String], parameter type [System.String[]], collection type Array, element type [System.String], no coerceElementType
DEBUG: ParameterBinding Information: 0 :         Creating array with element type [System.String] and 1 elements
DEBUG: ParameterBinding Information: 0 :         Argument type String is not IList, treating this as scalar
DEBUG: ParameterBinding Information: 0 :         Adding scalar element of type String to array position 0
DEBUG: ParameterBinding Information: 0 :         BIND arg [System.String[]] to param [Name] SUCCESSFUL
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on cmdlet [Get-Service]
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : CALLING EndProcessing

It’s just that the Get-Service cmdlet is written to work with either value passed to the Name argument, for some reason, but only if the Name argument does not contain any wildcards. You won’t find that in the documentation anywhere, and it may not have even been a conscious design choice by the PowerShell team, as it’s actually behavior from the underlying ServiceController class. See http://msdn.microsoft.com/en-us/library/ssbk2tf3(v=vs.110).aspx; you’ll see that the “name” parameter can be either the name or display name of the service, and this is the constructor that Get-Service calls if you pass it a Name without wildcards.

ah, i see:

name Type: System.String The name that identifies the service to the system. This can also be the display name for the service.

thanks a lot Dave, that’s really helpful!

If you are really curious, download a copy of dotPeek sometime. It’s free, and it’s what I use to investigate how PowerShell’s various bits actually work. You can run this command to identify the DLL and class name of a particular cmdlet:

Get-Command Get-Service | Format-Table DLL, ImplementingType -Wrap

DLL                                                                                                              ImplementingType                                                                                               
---                                                                                                              ----------------                                                                                               
C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.PowerShell.Commands.Management\v4.0_3.0.0.0__31bf3856ad364e Microsoft.PowerShell.Commands.GetServiceCommand                                                                
35\Microsoft.PowerShell.Commands.Management.dll

So I just load up dotPeek, choose the “Open from GAC” option, point it at Microsoft.PowerShell.Commands.Management.dll, and look for the Microsoft.PowerShell.Commands.GetServiceCommand class.

This does require some understanding of C#, and the decompiled code isn’t always the clearest to read, but it’s very handy for this type of investigation.