Summary: Learn how to use Windows PowerShell to work with the .NET Framework service controller classes.
Hey, Scripting Guy! I am curious about the .NET Framework. I tried to use a .NET Framework class in a Windows PowerShell script recently, and the script seems to run erratically. Because I am in the middle of troubleshooting the script this is really frustrating. To be honest, I cannot tell if a change I make to the script is what causes the script to run correctly, or if it is something else.
— TA
Hello TA, Microsoft Scripting Guy Ed Wilson here. It is amazing how much I take computers for granted. Most of the time, the computers just work and I do not have to do much troubleshooting. I have a dozen physical computers on my network at home (10 of them are in my office which keeps me nice and warm in the winter. Of course, they keep me nice and warm in the summer, too). The nice thing is that when one of my computers does cause problems, I have the skills to be able to diagnose the problem quickly.
In one respect Windows PowerShell is like this, in that most of the time, it simply works. When straying from the safety net of Windows PowerShell into the “uncharted waters” of the .NET Framework, problems can arise. Not that the .NET Framework classes are uncharted waters, there are thousands of pages worth of information about the .NET Framework on MSDN. However, it can feel like one is adrift because all the things that Windows PowerShell does automatically now have to be done manually.
An example is the Get-Service Windows PowerShell cmdlet. Typing a single command Get-Service returns a nice display of information about services on the local machine. The command appears here.
Get-Service
The output from that single command appears in the following figure.
By piping the output from the Get-Service Windows PowerShell cmdlet to the Get-Member cmdlet, I can see what kind of object I am working with. The output from this sequence of two commands is here.
PS C:\> Get-Service | Get-Member
TypeName: System.ServiceProcess.ServiceController
Name MemberType Definition
—- ———- ———-
Name AliasProperty Name = ServiceName
RequiredServices AliasProperty RequiredServices = ServicesDependedOn
Disposed Event System.EventHandler Disposed(System.Objec…
Close Method System.Void Close()
Continue Method System.Void Continue()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjR…
Dispose Method System.Void Dispose()
Equals Method bool Equals(System.Object obj)
ExecuteCommand Method System.Void ExecuteCommand(int command)
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Pause Method System.Void Pause()
Refresh Method System.Void Refresh()
Start Method System.Void Start(), System.Void Start(st…
Stop Method System.Void Stop()
ToString Method string ToString()
WaitForStatus Method System.Void WaitForStatus(System.ServiceP…
CanPauseAndContinue Property System.Boolean CanPauseAndContinue {get;}
CanShutdown Property System.Boolean CanShutdown {get;}
CanStop Property System.Boolean CanStop {get;}
Container Property System.ComponentModel.IContainer Containe…
DependentServices Property System.ServiceProcess.ServiceController[]…
DisplayName Property System.String DisplayName {get;set;}
MachineName Property System.String MachineName {get;set;}
ServiceHandle Property System.Runtime.InteropServices.SafeHandle…
ServiceName Property System.String ServiceName {get;set;}
ServicesDependedOn Property System.ServiceProcess.ServiceController[]…
ServiceType Property System.ServiceProcess.ServiceType Service…
Site Property System.ComponentModel.ISite Site {get;set;}
Status Property System.ServiceProcess.ServiceControllerSt…
PS C:\>
The .NET Framework class that is behind the Get-Service Windows PowerShell cmdlet is therefore the System.ServiceProcess.ServiceController class. The class name is ServiceController, and the .NET Framework namespace is System.ServiceProcess. If I put the namespace and class name in square brackets I will retrieve a bit of information that tells me the class is available for use. This is seen here.
PS C:\> [System.ServiceProcess.ServiceController]
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False ServiceController System.ComponentModel….
PS C:\>
As soon as I know a .NET Framework class is available, I can use the Get-Member Windows PowerShell cmdlet to retrieve its static members. To do this, I pipeline the .NET Framework class (together with its namespace) to the Get-Member cmdlet and use the -static switch. This command and its associated output appear here.
PS C:\> [System.ServiceProcess.ServiceController] | Get-Member -Static
TypeName: System.ServiceProcess.ServiceController
Name MemberType Definition
—- ———- ———-
Equals Method static bool Equals(System.Object objA, System.Object o…
GetDevices Method static System.ServiceProcess.ServiceController[] GetDe…
GetServices Method static System.ServiceProcess.ServiceController[] GetSe…
ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System…
PS C:\>
Now there are two static methods that look really interesting, and two static methods that always appear in the output (because they are inherited from the System.Object .NET Framework class). The two static methods that I want to explore are GetDevices and GetServices. I can call the GetDevices method by using double colons as shown here.
PS C:\> [System.ServiceProcess.ServiceController]::GetDevices()
Status Name DisplayName
—— —- ———–
Stopped 1394ohci 1394 OHCI Compliant Host Controller
Running ACPI Microsoft ACPI Driver
Stopped AcpiPmi ACPI Power Meter Driver
Running ADIHdAudAddService ADI UAA Function Driver for High De…
Stopped adp94xx adp94xx
Stopped adpahci adpahci
Stopped adpu320 adpu320
<output truncated>
Similarly, I can call the GetServices static method from [System.ServiceProcess.ServiceController] by using the command shown here.
[System.ServiceProcess.ServiceController]::GetServices()
The output from the GetServices static method is seen in the following figure.
A quick comparison of the output from Get-Service with the output from [System.ServiceProcess.ServiceController]::GetServices() reveals that the two commands are identical. Therefore, there is no reason to mess around with calling the GetServices static method from the ServiceController class. On the other hand, calling the GetDevices static method is useful because it provides a great view of device drivers, and this information is not exposed from the Get-Service Windows PowerShell cmdlet.
Now, close the Windows PowerShell console. Open it back up, and type [System.ServiceProcess.ServiceController] to make sure the .NET Framework ServiceController class is available. The output of this operation causes an error, as seen here.
PS C:\> [System.ServiceProcess.ServiceController]
Unable to find type [System.ServiceProcess.ServiceController]: make sure that the as
sembly containing this type is loaded.
At line:1 char:42
+ [System.ServiceProcess.ServiceController] <<<<
+ CategoryInfo : InvalidOperation: (System.ServiceProcess.ServiceContr
oller:String) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
PS C:\>
The error generated by the previous command tells me that Windows PowerShell cannot find the assembly that contains the [System.ServiceProcess.ServiceController] class. This means that we cannot use the ServiceController class directly.
Now type Get-Service and press Enter to run the Get-Service Windows PowerShell cmdlet. The output that contains information about the installed services on my machine appears. Now press the Up Arrow, to retrieve the [System.ServiceProcess.ServiceController] command. Pressing Enter this time, reveals the output that tells me that the class is available.
PS C:\> [System.ServiceProcess.ServiceController]
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False ServiceController System.ComponentModel….
PS C:\>
Well, what happened? It seems that the Get-Service Windows PowerShell cmdlet automatically loads the assembly that contains the ServiceController .NET Framework class. The easiest way to find information about a .NET Framework class’s assembly is to use MSDN. The MSDN article for the ServiceController class is shown in the following figure.
The assembly information for a .NET Framework class is always found on the main page for the class, in the Inheritance Hierarchy portion if the page. For the ServiceController class, the assembly and the namespace have the same name. Both are called System.ServiceProcess. While this is true of a good many .NET Framework classes, it is not true of all .NET Framework classes. Therefore, it is not safe to assume that the assembly name and the namespace name will always be the same. To load the System.ServiceProcess assembly that contains the ServiceController class, use the Add-Type cmdlet in Windows PowerShell 2.0. If, for some reason, you are still using Windows PowerShell 1.0, and you cannot upgrade to Windows PowerShell 2.0, you can load the assembly by using the assembly class from the System.Reflection .NET Framework namespace. The command uses the LoadWithPartialName static method and it is shown here.
PS C:\> [system.reflection.assembly]::LoadwithPartialName(“System.ServiceProcess”)
GAC Version Location
— ——- ——–
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.ServiceProcess\2.0.0.0_…
PS C:\>
The LoadWithPartialName static method is actually obsolete according to MSDN. To load an assembly by using the Assembly .NET Framework class, you should actually use the load method. Unfortunately, the load method is more difficult to use because you must tell it which exact version of the assembly to use. This is why I like to use the Add-Type Windows PowerShell cmdlet instead. It is much easier to use. To show this: close Windows PowerShell, and open it back up again. Use the -assemblyname parameter to specify the System.ServiceProcess assembly. After this is completed, type the [System.ServiceProcess.ServiceController] class to make sure it is available. This is shown here.
PS C:\> Add-Type -assemblyname System.ServiceProcess
PS C:\> [System.ServiceProcess.ServiceController]
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False ServiceController System.ComponentModel….
No feedback is returned from using the Add-Type Windows PowerShell cmdlet to load the assembly. However, if the assembly name is misspelled, and error will appear as shown here.
PS C:\> Add-Type -assemblyname System.ServiceProc
Add-Type : Cannot add type. The assembly ‘System.ServiceProc’ could not be found.
At line:1 char:9
+ Add-Type <<<< -assemblyname System.ServiceProc
+ CategoryInfo : ObjectNotFound: (System.ServiceProc:String) [Add-Type
], Exception
+ FullyQualifiedErrorId : ASSEMBLY_NOT_FOUND,Microsoft.PowerShell.Commands.AddT
ypeCommand
Add-Type : Cannot add type. One or more required assemblies are missing.
At line:1 char:9
+ Add-Type <<<< -assemblyname System.ServiceProc
+ CategoryInfo : InvalidData: (:) [Add-Type], InvalidOperationExceptio
n
+ FullyQualifiedErrorId : ASSEMBLY_LOAD_ERRORS,Microsoft.PowerShell.Commands.Ad
dTypeCommand
PS C:\>
One other thing to be aware of is that the -assemblyname parameter is required. You cannot use the Add-Type cmdlet to load an assembly and rely on a positional parameter. This is seen here.
PS C:\> Add-Type system.serviceprocess
Add-Type : c:\Users\ed.NWTRADERS\AppData\Local\Temp\vctcnhpk.0.cs(1) : A namespace d
oes not directly contain members such as fields or methods
c:\Users\ed.NWTRADERS\AppData\Local\Temp\vctcnhpk.0.cs(1) : >>> system.serviceproces
s
At line:1 char:9
+ Add-Type <<<< system.serviceprocess
+ CategoryInfo : InvalidData: (c:\Users\ed.NWT…elds or methods:Compi
lerError) [Add-Type], Exception
+ FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTy
peCommand
Add-Type : Cannot add type. There were compilation errors.
At line:1 char:9
+ Add-Type <<<< system.serviceprocess
+ CategoryInfo : InvalidData: (:) [Add-Type], InvalidOperationExceptio
n
+ FullyQualifiedErrorId : COMPILER_ERRORS,Microsoft.PowerShell.Commands.AddType
Command
In addition, while you can leave the Word system off, when calling the class, you cannot leave off the word system when loading the assembly. This is illustrated here, where I generate an error.
PS C:\> Add-Type -assembly serviceprocess
Add-Type : Cannot add type. The assembly ‘serviceprocess’ could not be found.
At line:1 char:9
+ Add-Type <<<< -assembly serviceprocess
+ CategoryInfo : ObjectNotFound: (serviceprocess:String) [Add-Type], E
xception
+ FullyQualifiedErrorId : ASSEMBLY_NOT_FOUND,Microsoft.PowerShell.Commands.AddT
ypeCommand
Add-Type : Cannot add type. One or more required assemblies are missing.
At line:1 char:9
+ Add-Type <<<< -assembly serviceprocess
+ CategoryInfo : InvalidData: (:) [Add-Type], InvalidOperationExceptio
n
+ FullyQualifiedErrorId : ASSEMBLY_LOAD_ERRORS,Microsoft.PowerShell.Commands.Ad
dTypeCommand
The assembly name is not case-sensitive. After the assembly has been loaded, the word system can be left off if you want. This is shown here.
PS C:\> Add-Type -AssemblyName system.serviceprocess
PS C:\> [serviceprocess.servicecontroller]
IsPublic IsSerial Name BaseType
——– ——– —- ——–
True False ServiceController System.ComponentModel….
PS C:\>
TA, that is all there is to working with the ServiceController .NET Framework class. .NET Framework week will continue tomorrow when I continue to work with the .NET Framework classes.
I invite you to follow me on Twitter or Facebook. If you have any questions, send email to me at scripter@microsoft.com or post them on the Official Scripting Guys Forum.. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments