Use PowerShell to Work with the .NET Framework Service Classes

Use PowerShell to Work with the .NET Framework Service Classes

  • Comments 3
  • Likes

 

Summary: Learn how to use Windows PowerShell to work with the .NET Framework service controller classes.

 

Hey, Scripting Guy! QuestionHey, 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

 

Hey, Scripting Guy! AnswerHello 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

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Thanks for the tip about using Add-Type as the preferred V2 method of loading assemblies.  I wasn't aware LoadWithPartialName was obsolete as I have been using it since V1 and into V2 now.  Looks like I have some refactoring to do on my current scripts.

  • Loadwithpartialname has been deprecated for several years and therefore is now listed as obsolete. The problem in Windows PowerShell V1 (when loadwithpartial name was deprecated) is that there was not an easy alternative. The nice thing is in Windows PowerShell V2 is that add-type is even easier to use than loadwithpartialname.

  • Ditto to Brian's comment:  Many thanks for the explanation behind the Add-Type prerequisite for assemblies, helped me with much head scratching.