Die unglaubliche Macht von WSH (oder Powershell) in Kombination mit WMI ist mir schon länger bekannt. Ich möchte das für mich nutzen, um ein Skript zu schreiben, dass mir auf Klick (oder ereignisgesteuert) alle Prozesse mit einem bestimmten Namen beschleunigt oder abbremst, also die Priorität verändert.
Wie mache ich das? Mit einem Skript namens setprio.vbs, das zwei Parameter entgegennimmt, nämlich ‘Processname’ (in Kleinbuchstaben) und ‘DesiredPriority’
DesiredPriority kann einen dieser Werte haben:
IDLE|BELOW_NORMAL|NORMAL|ABOVE_NORMAL|HIGH_PRIORITY|REALTIME
In meinem Skript führt ein ungültiger Wert automatisch dazu, dass der Prozess auf ‘NORMAL’ zurückgesetzt wird.
Mit diesem Befehl werden also alle Internet Explorer Prozesse abgebremst:
cscript setprio.vbs iexplore.exe BELOW_NORMAL

Wofür kann man nun sowas in der Praxis wirklich brauchen?
Also: Man nehme an, man hat eine sehr große Musik- oder Videobibliothek umzuwandeln (wir sprechen von vielen Tagen, wenn nicht Wochen Rechenarbeit!), möchte aber z.B. unter Tags auf dem Rechner normal arbeiten können, dann muss man z.B. in der Früh nur diesen Befehl ausführen:
cscript setprio.vbs convertvideo.exe BELOW_NORMAL
Vor dem Schlafen gehen dann:
cscript setprio.vbs convertvideo.exe ABOVE_NORMAL
Natürlich kann man auch IDLE oder REALTIME nehmen, bzw. das ganze zeitgesteuert machen oder (unter Vista, Windows Server 2008 oder Windows 7) das ganze ereignisgesteuert machen.
Das Skript ist natürlich auch hervorragend etwa für einen (Home-)server oder ein Media Center geeignet, um die schwächeren Stunden oder die Nacht perfekt für Rechenaufgaben zu nützen. Ich werde mir das mittels Ereignissteuerung so konfigurieren, dass bestimmte Prozesse automatisch eine höhere oder sehr hohe Priority bekommen, wenn niemand vor dem Computer sitzt (=Bildschirm schwarz), und die Prozesse sofort in den Hintergrund fliegen, sobald der PC wieder benutzt wird.
Normalerweise schreibe ich gerne kurze, lesbare Skripts, ich habe hier bewußt etwas ausgefeiltere Rückmeldungen und Fehlerbehandlungen eingebaut. Mit Powershell wäre das natürlich sogar als Einzeiler realisierbar gewesen. Aber Powershell ist halt noch nicht auf jedem Windows PC standardmäßig installiert, daher bevorzuge ich zur Zeit (von Exchange 2007 Servern abgesehen) noch WSH/VBScript:
set args = WScript.Arguments
num = args.Count
'Error handling if no command line parameters given
if num < 2 then
WScript.Echo "No command line parameters!"
WScript.Echo "Syntax: cscript setprio.vbs ProcessName DesiredPriority"
WScript.Echo "DesiredPriority can be: "
WScript.Echo "IDLE|BELOW_NORMAL|NORMAL|ABOVE_NORMAL|HIGH_PRIORITY|REALTIME"
WScript.Quit 1
end if
ProcessName = args.Item(0)
DesiredPriority = args.Item(1)
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
'Here all Processes will be found,
'that contain the ProcessName and the Sub SetPID will be called
For Each objItem in colItems
If InStr(lcase(objItem.Name), ProcessName) Then
' Use objItem.CommandLine instead of objItem.Name
' if you want to match a string as part of the
' full commandline for the process!
Call SetPID (objItem.ProcessID, GetPriorityID (DesiredPriority))
End If
Next
'Here the Processes will be set to the desired priority
Sub SetPID (intPID, intPriority)
set objWMIProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process.Handle='" & intPID & "'")
intErrorCode = objWMIProcess.SetPriority(intPriority)
if intRC = 0 Then
Wscript.Echo "Successfully set priority for process " & objWMIProcess.Name & " (ID: " & intPID & ") to " & GetPriorityName(intPriority)
else
Wscript.Echo "Error: '" & intErrorCode & "' setting priority for process " & objWMIProcess.Name & "(ID: " & intPID & ") to " & GetPriorityName(intPriority)
end if
Wscript.Echo "(Command: '" & objItem.CommandLine & "')"
Wscript.Echo vbcrLF
End Sub
'These two functions aren't really necessary, they just translate
'the more readable priority names into the numbers
'required by objWMIProcess.SetPriority
Function GetPriorityName (intPriority)
Select Case intPriority
Case 32: GetPriorityName = "NORMAL"
Case 64: GetPriorityName = "IDLE"
Case 128: GetPriorityName = "HIGH_PRIORITY"
Case 256: GetPriorityName = "REALTIME"
Case 16384: GetPriorityName = "BELOW_NORMAL"
Case 32768: GetPriorityName = "ABOVE_NORMAL"
End Select
End Function
Function GetPriorityID (strPriorityName)
Select Case strPriorityName
Case "NORMAL": GetPriorityID = 32
Case "IDLE": GetPriorityID = 64
Case "HIGH_PRIORITY": GetPriorityID = 128
Case "REALTIME": GetPriorityID = 256
Case "BELOW_NORMAL": GetPriorityID = 16384
Case "ABOVE_NORMAL": GetPriorityID = 32768
'Invalid Priorityname resets Priority to 'NORMAL'
Case ELSE: GetPriorityID = 32
End Select
End Function
Hier noch eine verkürzte Version davon, nur um zu zeigen, dass es natürlich auch kürzer geht, wenn man etwa die Parameter direkt in’s Skript gibt, und nicht ganz so viele Ausgaben macht:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
If InStr(lcase(objItem.Name), "notepad.exe") Then
Call SetPID (objItem.ProcessID, 16384)
End If
Next
Sub SetPID (intPID, intPriority)
set objWMIProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process.Handle='" & intPID & "'")
intErrorCode = objWMIProcess.SetPriority(intPriority)
if intRC = 0 Then
Wscript.Echo "Successfully set priority for process " & objWMIProcess.Name & " (ID: " & intPID & ")"
else
Wscript.Echo "Error: '" & intErrorCode & "' setting priority for process " & objWMIProcess.Name & "(ID: " & intPID & ")"
end if
End Sub
Weitere Informationen: Das Einrichten von ereignisgesteuerten Aufgaben erkläre ich in einem anderen Zusammenhang in diesem Beitrag.