DCSIMG
January 2008 - Posts - Shay Levy

Shay Levy

If you repeat it, PowerShell it!

News


btn_donate_LG

View Shay Levy's profile on LinkedIn Follow Shay Levy at Twitter Shay Levy's Facebook profile Subscribe to my FriendFeed


site statistics




January 2008 - Posts

Creating and managing processes in PowerShell

 

process

PowerShell's get-process cmdlet gets the processes that are running on the local computer, and is restricted to the local computer only, you can't interact with processes that runs on remote computers nor create ones locally or remotely.
You can use WMI (see below), but I want to show another way of doing it, less documented one - static methods.

Static methods give you the option to break loose of the cmdlet 'wrapper' and work directly on the underlying NET class. To find which static methods you can use on processes we'll use PowerShell Get-Process cmdlet, we'll pipe it to Get-Member to reveal the underlying NET type that get-process is using: 

 

PS >Get-Process | Get-Member

   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName(...)  
(...)
 
 

Managing processes with static methods

As you can see, below the surface, Get-Process is using the [System.Diagnostics.Process] NET type to retrieve a process object for each process. From now on, you can work with the static methods of this type to interact with process objects. To find the static methods (surround the type name with square brackets), type:

 

PS > [System.Diagnostics.Process] | Get-Member -Static -MemberType method

   TypeName: System.Diagnostics.Process

Name               MemberType Definition
----               ---------- ----------
EnterDebugMode     Method     static System.Void EnterDebugMode()...
Equals             Method     static System.Boolean Equals(Object...
GetCurrentProcess  Method     static System.Diagnostics.Process G...
GetProcessById     Method     static System.Diagnostics.Process G...
GetProcesses       Method     static System.Diagnostics.Process[]...
GetProcessesByName Method     static System.Diagnostics.Process[]...
LeaveDebugMode     Method     static System.Void LeaveDebugMode()...
ReferenceEquals    Method     static System.Boolean ReferenceEqua...
Start              Method     static System.Diagnostics.Process S...
(...)

  

The Definition column shows all the available overloads for each method. The notation for calling a static method is:
PS > [System.Diagnostics.Process]::StaticMethodName(<args>)

Lets find the overloads for the GetProcesses method, (each overload is delimited with a comma):

PS > ([System.Diagnostics.Process] | gm -s GetProcesses).Definition 

static System.Diagnostics.Process[] GetProcesses(),
static System.Diagnostics.Process[] GetProcesses(String machineName) 

The output shows two ways to call the method. You can call it with no arguments and get all running processes on the local computer or use the later to get processes from a remote computer. We are interested in the second one since it accept a string argument for a computer name. To save typing space we'll assign the type name to a variable:

PS > $process = [System.Diagnostics.Process] 


# examples
$server="server"
$process = [System.Diagnostics.Process]

# get all running processes on the remote server 
PS > $process::GetProcesses($server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32
    196       6     2764       5132    54            3740 LVComSX
     73       4     1100       3392    30            2452 ctfmon
(...)


As you can see the output is the same output produced by the get-process. To find which methods you can invoke on a process, pipe it to get-member:

 

PS > $process::GetProcessById(2992,$server) | gm -member method   

	TypeName: System.Diagnostics.Process
	
Name                      MemberType Definition
----                      ---------- ----------
(...)
Close                     Method     System.Void Close()
CloseMainWindow           Method     System.Boolean CloseMainWindow()
CreateObjRef              Method     System.Runtime.Remoting.ObjRef Cr
Dispose                   Method     System.Void Dispose()
Kill                      Method     System.Void Kill()
Refresh                   Method     System.Void Refresh()
Start                     Method     System.Boolean Start()
ToString                  Method     System.String ToString()
WaitForExit               Method     System.Boolean 
WaitForExit
(...)


 

This are the same methods you'll get if you run get-process | gm -member method. To call a static method simply append it to the command:

PS > $process::GetProcessById(2992,$server).Kill() 


# In the same manner you can get a list all properties of a process.
PS > $process::GetProcessById(2992,$server) | gm -member property
 

# get remote process by its process id
PS > $process::GetProcessById(2992,$server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32


# get remote process by its name
PS > $process::GetProcessesByName("vmserverdWin32",$server)

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    165       7    15364      21684    56            2992 vmserverdWin32


# get PowerShell's process, locally
PS > [System.Diagnostics.Process]::GetCurrentProcess() 

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    512       9    39340      33148   163     4.06  11952 powershell

 


Creating local processes

To create processes, locally, you need to use the static Start() method. To find what overloads this method takes:

PS > $process = [System.Diagnostics.Process]
PS > (($process | gm -static start).definition).replace("static","`nstatic") static System.Diagnostics.Process Start(String fileName, String userName, SecureString password,
String domain),
static System.Diagnostics.Process Start(String fileName, String arguments, String userName,
SecureString password, String domain),
static System.Diagnostics.Process Start(String fileName) static System.Diagnostics.Process Start(String fileName, String arguments), static System.Diagnostics.Process Start(ProcessStartInfo startInfo)

 

There are five different ways to call it, each overload accepts different set of arguments:

1. Run a file (executable) with alternate credentials and a secure password.
2. Adds an option to add arguments for the file name.
3. Gets only file name.
4. File name and arguments, no alternate credentials.
5. Gets a ProcessStartInfo object.

 

# Use the third overload to run notepad
PS > [System.Diagnostics.Process]::Start("notepad")

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      0       1      136         84     1     0.00  13720 notepad 
      

# Use the fourth to run notepad with arguments, in this case make notepad load the system.ini file.
PS > [System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")


Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
      5       1      340        840     8     0.03  23728 notepad
      
      



notepad

 

BTW, if you want to avoid the default output, assign the command to a variable or cast it to [void], as in:
 
PS > [void][System.Diagnostics.Process]::Start("notepad","$env:windir\system.ini")
PS >       
      

 

The fifth option is the one I want to use since it is using another type to specify what starting info to assign to the new process, including alternate credentials (though in plain text and not secure as the first and second overloads which uses the SecureString parameter). Here's a partial property list, you can find the full list here:

- Arguments
- FileName
- Password
- UserName
- WorkingDirectory


 

Using the New-Object cmdlet

In this example I'll use the New-Object cmdlet to instantiate a new System.Diagnostics.ProcessStartInfo object to specify a set of values to be used when the process start:

 

# Specifies a set of values that are used when you start a process.
$si = new-object System.Diagnostics.ProcessStartInfo
$si.fileName = "notepad.exe"
$si.Arguments= "$env:windir\system.ini"
$si.WorkingDirectory = $pwd
# this will launch the process in the background (hidden)
# $si.windowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
# run process under alternate credentials
# $si.UserName="username"
# $si.Password="Password"

# start the process
$process = New-Object System.Diagnostics.Process
$process.startInfo=$si

# this is also valid
# $process = [System.Diagnostics.Process]::Start($si)

# close notepad and watch the output
if($process.start()) { $process.waitForExit(); "existed"}  
      


Note: The WaitForExit() method is available only for processes that are running on the local computer. If you need to track a process on a remote system construct a loop to check if the remote process exists.
Want to know how to list all name/value pairs of the [System.Diagnostics.ProcessWindowStyle] type or any other [enum] type, see my post on Listing enumeration type values.

 

 Creating remote processes

So far so good. But how about creating new processes on remote computers? Well, that's another story (Class). None of the above, Get-Process nor its underlying type, let you do that. The only way to do it is by using WMI.

You can access WMI with the Get-WmiObject cmdlet or with PowerShell's Type accelerators (shortcuts). Type accelerators were developed by the Windows PowerShell team to make WMI scripts more easy to write for system administrators:

[WMI] - type accelerator for ManagementObject.
[WMICLASS] - type accelerator for ManagementClass.
[WMISEARCHER] - type accelerator for a ManagementObjectSearcher.

See the second resource link below for more information on this. 

 

Here's a one-liner to create new process(es) on local/remote computers:

PS > ([WMICLASS]"\\$computer\ROOT\CIMV2:win32_process").Create("notepad")


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 2
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ProcessId        : 7380
ReturnValue      : 0
      
 

If the ReturnValue property is set to 0 then you've successfully created the process on the remote computer, the ProcessId property shows the process id for the new process on the remote computer.

Note: For security reasons the WMI Win32_Process.Create method cannot be used to start an interactive process remotely (it runs hidden, in the background).

 

 

Summary

The above technique on static methods, opens a whole new world of discovering PowerShell and NET classes. You can apply it to any cmdlet and work your way up or down, depending on your point of view :-). Soon enough you'll find your way to MSDN, where you can get more information and code examples on each class.

This post only scratches the surface and there's a lot more to write on processes... but now comes your part. Check out what Get-Service has to offer and what can you get by applying all of this to it.

Last but not least, I'd like to thank Hal Rottenberg from TechProsaic and the PowerScripting Podcast for his help with editing and prompting me to write this article.

Happy New Year!

 

Additional Resources:

Managing Processes in PowerShell
Improved Support for WMI
Getting Services associated with a Process via PowerShell
Process Class
ProcessStartInfo Class
Create Method of the Win32_Process Class
Casts and Get-WmiObject