Korábban már találkozhattunk a Get-Process és a Get-Service cmdlettel, amelyek segítségével a rendszerfolyamatok és a szolgáltatások listáját kérhetjük le. A folyamatokkal és szolgáltatásokkal kapcsolatban azonban nem csak listázást, hanem bármilyen más feladatot is elvégezhetünk a PowerShell cmdletjeivel, illetve ha minden kötél szakad, közvetlenül a megfelelő .NET komponensek segítségével.
Először is nézzük meg, hogy milyen cmdleteket használhatunk a folyamatok kezelésére:
[75] PS C:\> Get-Command -noun process
CommandType Name Definition
----------- ---- ----------
Cmdlet Debug-Process Debug-Process [-Name] <Stri...
Cmdlet Get-Process Get-Process [[-Name] <Strin...
Cmdlet Start-Process Start-Process [-FilePath] <...
Cmdlet Stop-Process Stop-Process [-Id] <Int32[]...
Cmdlet Wait-Process Wait-Process [-Name] <Strin...
Négy cmdlet került a listába. A Debug-Process a paraméterként megadott processzhez tartozó debuggert nyitja meg, igazából fejlesztőknek szánt cmdlet ez.
A Start-Process segítségével lehet újabb folyamatokat indítani. Például nézzük meg a Notepad.exe indítását:
[1] PS C:\> Start-Process notepad.exe
Ez így nem mutat túl sokat, akár így is elindíthattam volna:
PS C:\> notepad
Ez a megoldás valóban egyszerű, azonban korántsem egyenértékű az előzővel. Merthogy a Start-Process-nek további paramétereket is megadhatunk:
[2] PS C:\> Start-Process notepad.exe -ArgumentList C:\fájl.txt -PassThru
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
57 7 1256 5144 72 0,19 3176 notepad
A -PassThru paraméterrel visszakapjuk az elindított processz adatait. Ez ahhoz kell nekünk, hogy például pontosan ezt a processzt tudjuk a Stop-Process segítségével bezárni, mert név alapján a Stop-Process minden adott nevű folyamatot bezár:
[3] PS C:\> stop-process -name "notepad"
Ha tudjuk a processz azonosítóját, akkor a Wait-Process segítségével várakozhatunk annak befejeződéséig:
[4] PS C:\> Wait-Process -Id 3176
Sajnos a processzek indítása csak helyi gépen történhet. Távoli gépeken próbálkozhatunk az Invoke-Command segítségével:
[5] PS C:\> Invoke-Command -ComputerName member -ScriptBlock {start-process not
epad}
Ezzel az a baj, hogy nem az éppen bejelentkezett felhasználó desktop felületén nyílik meg az alkalmazás, hanem háttérben, így nem sok minden látszik belőle. Konkrétan a Notepad le is áll. Egy másik módszer a távoli indításra a WMI-n keresztül:
[5] PS C:\> Invoke-WmiMethod -Path Win32_Process -Name create -ArgumentList not
epad.exe -ComputerName member
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 2
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ProcessId : 1124
ReturnValue : 0
Ez ugyanúgy háttérben futtatja a processzt, így itt sem érdemes grafikus alkalmazásokkal játszani, csak olyan folyamatokat indítsunk, amelyek nem igényelnek ablakot, például ilyenek általában a parancssori eszközök.
Se a Get-Process, se a WMI nem adja meg, hogy kinek a nevében fut a folyamat. Viszont a WMI segítségével ez lekérdezhető a process GetOwner metódusa segítségével:
PS C:\> (Get-WmiObject -Class Win32_process -Filter "Name = 'notepad.exe'").get
owner().user
soost
PS C:\> $owner = (Get-WmiObject -Class Win32_process -Filter "Name = 'notepad.e
xe'").getowner()
PS C:\> $owner.domain + "\" + $owner.user
IQJB\soost
A folyamatokat, azaz a futó programokat bezárhatjuk „szelíden”, azaz az éppen módosított dokumentum elmentését lehetővé tehetjük a felhasználónak. Ezt a következőképpen tehetjük meg:
PS C:\> (Get-Process notepad).CloseMainWindow()
True
Ez – tapasztalatom szerint –mindenképpen „True” értéket ad vissza, függetlenül attól, hogy sikerült-e bezárni az alkalmazást, vagy pedig a felhasználó beavatkozására vár.
Erőszakos bezárás történhet a korábban már látott Stop-Process cmdlettel, vagy a Process objektum Close vagy Kill metódusával. A Kill-el vigyázzunk, mert ennek hatására az alkalmazás által éppen írt fájlok sérülhetnek.
Windows 7-től kezdődően hiába vagyunk rendszergazdaként, az általunk futtatott folyamatok nem futnak „igazi” rendszergazda jogkörrel, kilistázva a privilégiumokat, a lista elég rövidke:
PS C:\> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeShutdownPrivilege Shut down the system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeUndockPrivilege Remove computer from docking station Disabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
Ha ténylegesen „Run as Administrator” módon futtatjuk a PowerShellt, akkor a lista jóval hosszabb:
PS C:\> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description
State
=============================== ========================================= ========
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeSecurityPrivilege Manage auditing and security log Disabled
SeTakeOwnershipPrivilege Take ownership of files or other objects Disabled
SeLoadDriverPrivilege Load and unload device drivers Disabled
SeSystemProfilePrivilege Profile system performance Disabled
SeSystemtimePrivilege Change the system time Disabled
SeProfileSingleProcessPrivilege Profile single process Disabled
SeIncreaseBasePriorityPrivilege Increase scheduling priority Disabled
SeCreatePagefilePrivilege Create a pagefile Disabled
SeBackupPrivilege Back up files and directories Disabled
SeRestorePrivilege Restore files and directories Disabled
SeShutdownPrivilege Shut down the system Disabled
SeDebugPrivilege Debug programs Enabled
SeSystemEnvironmentPrivilege Modify firmware environment values Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeRemoteShutdownPrivilege Force shutdown from a remote system Disabled
SeUndockPrivilege Remove computer from docking station Disabled
SeManageVolumePrivilege Perform volume maintenance tasks Disabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
SeTimeZonePrivilege Change the time zone Disabled
SeCreateSymbolicLinkPrivilege Create symbolic links Disabled
Ha egy processzt akarunk emelt szintű jogosultsággal futtatni, akkor a Start-Process-nek a -Verb paraméterét használva tudjuk ezt megtenni:
PS C:\> Start-Process -FilePath notepad -Verb runas
Sajnos ez, ha az UAC beállításaink megfelelően szigorúak, megerősítést fog kérni, hogy tényleg akarjuk-e. Azaz ezzel nem feltétlenül tudunk automatikusan futtatni processzeket.
A StackOverflow oldalon találtam a következő szkriptet, amivel nagyon profin tudunk úgy processzeket indítani, hogy a PowerShell megvárja a processz futását és minden lényeges információt visszaad:
function Invoke-Executable {
# Runs the specified executable and captures its exit code, stdout
# and stderr.
# Returns: custom object.
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[String]$sExeFile,
[Parameter(Mandatory=$false)]
[String[]]$cArgs,
[Parameter(Mandatory=$false)]
[String]$sVerb
)
# Setting process invocation parameters.
$oPsi = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$oPsi.CreateNoWindow = $true
$oPsi.UseShellExecute = $false
$oPsi.RedirectStandardOutput = $true
$oPsi.RedirectStandardError = $true
$oPsi.FileName = $sExeFile
if (! [String]::IsNullOrEmpty($cArgs)) {
$oPsi.Arguments = $cArgs
}
if (! [String]::IsNullOrEmpty($sVerb)) {
$oPsi.Verb = $sVerb
}
# Creating process object.
$oProcess = New-Object -TypeName System.Diagnostics.Process
$oProcess.StartInfo = $oPsi
# Creating string builders to store stdout and stderr.
$oStdOutBuilder = New-Object -TypeName System.Text.StringBuilder
$oStdErrBuilder = New-Object -TypeName System.Text.StringBuilder
# Adding event handers for stdout and stderr.
$sScripBlock = {
if (! [String]::IsNullOrEmpty($EventArgs.Data)) {
$Event.MessageData.AppendLine($EventArgs.Data)
}
}
$oStdOutEvent = Register-ObjectEvent -InputObject $oProcess `
-Action $sScripBlock -EventName 'OutputDataReceived' `
-MessageData $oStdOutBuilder
$oStdErrEvent = Register-ObjectEvent -InputObject $oProcess `
-Action $sScripBlock -EventName 'ErrorDataReceived' `
-MessageData $oStdErrBuilder
# Starting process.
[Void]$oProcess.Start()
$oProcess.BeginOutputReadLine()
$oProcess.BeginErrorReadLine()
[Void]$oProcess.WaitForExit()
# Unregistering events to retrieve process output.
Unregister-Event -SourceIdentifier $oStdOutEvent.Name
Unregister-Event -SourceIdentifier $oStdErrEvent.Name
$oResult = New-Object -TypeName PSObject -Property ([Ordered]@{
"ExeFile" = $sExeFile;
"Args" = $cArgs -join " ";
"ExitCode" = $oProcess.ExitCode;
"StdOut" = $oStdOutBuilder.ToString().Trim();
"StdErr" = $oStdErrBuilder.ToString().Trim()
})
return $oResult
}
A kimeneti objektumban láthatjuk a kilépési kódot, a folyamat standard kimenetét, a hibafolyamot is. Amivel még lehetne fejleszteni ezt, az egy timeout paraméter, ami behatárolná, hogy a processz maximum mennyi ideig futhat.