Ziel ist es, mit einem unbeaufsichtigten Script (also ohne Benutzereingaben etc) alle installierten Netzwerkdrucker (lokal installiert, nicht vom Druckerserver), von denen die IPs bekannt sind, zu löschen. Dieser Vorgang soll unabhängig vom vergebenen Druckernamen auf jedem beliebigen PC funktionieren.
Szenario
Wir haben zum Beispiel 14 verschiedenen Drucker installiert. Nun wollen wir 8 von denen deinstallieren und danach neu installieren. Von den 8 Druckern ist der Name nicht bekannt, da diese bei der Installation beliebig benannt wurden. Da dieser Vorgang auf dutzenden oder hunderten PCs funktionieren soll, ist der Name ein schlechter Anlaufpunkt. Stattdessen wissen wir aber die IP jedes Druckers, der deinstalliert werden soll.
Lokale Drucker wie PDF Drucker oder Microsoft-interne Drucker sollen unberührt bleiben.
Wie löschen wir am besten Drucker scriptbasiert? Mit printui.dll via CMD.
Zum Löschen nutzen wir den Befehl:
rundll32 printui.dll,PrintUIEntry /dl /n "Name des Druckers"
Wie gesagt kennen wir die Druckernamen nicht. Aufgabe ist also, den Namen der gewünschten Drucker zu bekommen und mit diesem dann den Löschbefehl zu starten.
Den Namen bekommen wir (indirekt) von der WSH Funktion EnumPrinterConnections.
Also Schritt 1, Druckernamen herausfinden:
show-printers.vbs
Set WshShell = CreateObject("Wscript.Shell") Set WshNetwork = CreateObject("WScript.Network") Set objPrinters = WshNetwork.EnumPrinterConnections For i = 0 to objPrinters.Count - 1 Step 2 WScript.Echo "Port: " & objPrinters.Item(i) & " | Name: " & objPrinters.Item(i+1) Next
.vbs Download (Rechtsklick -> Ziel speichern) | Codegrundlage
Probiert es mal aus, beim Ausführen der .vbs werden alle installierten Drucker nacheinander mit Port und Name angezeigt. objPrinters.Item ist also unser Freund. Die einzelnen Codezeilen erkläre ich nachher im Gesamtbild.
Wir können also die Druckernamen herausfinden und für den printui.dll Löschbefehl weiterverwenden.
Nun wollen wir aber nicht alle installierten Drucker löschen sondern nur die gewünschten Drucker, dessen IPs wir nennen. Zudem sollen Systemdrucker (Microsoft-interne, PDF Drucker etc) erhalten bleiben, genauso wie durch Druckserver hinzugefügte Drucker.
Also schauen wir uns das Konstrukt mal an:
delete-printers.vbs:
Set WshShell = CreateObject("Wscript.Shell") printer = Array("111.40.162.60", "111.40.162.90", "111.40.162.100", "111.40.162.105", "111.40.162.120", "111.40.162.129", "111.40.162.140") Set WshNetwork = CreateObject("WScript.Network") Set objPrinters = WshNetwork.EnumPrinterConnections For i = 0 to objPrinters.Count - 1 Step 2 If Left(objPrinters.Item(i+1),2) <> "\\" Then For Each item In printer If Len(item) >= 2 Then If InStr(objPrinters.Item(i), item) > 0 Then WshShell.Run "cmd /c rundll32 printui.dll,PrintUIEntry /dl /n " & Chr(34) & objPrinters.Item(i+1) & Chr(34) End If End If Next End If Next
.vbs Download (Rechtsklick -> Ziel speichern)
Nun im Detail:
Zeile 2 enthält, mit Kommata getrennt, die IPs aller Drucker, die gelöscht werden sollen.
Zeile 4 speichert in der Variable objPrinters alle Druckerobjekte.
Zeile 5 startet die Schleife, die so oft durchläuft, wie es Druckerobjekte (objPrinters.Count – 1) gibt. Die Schleife in 2er Schritten (Step 2) durchzugehen muss sein. Denn:
Nachdem EnumPrinterConnections in objPrinters geschrieben hat, sieht objPrinters etwa so aus, um es mal bildlich zu haben und die folgenden Codezeilen einfacher zu verstehen:
Step 2 | i= | Abfrage | Typ | Wert |
---|---|---|---|---|
0x | 0 | objPrinters.Item(i) | Portname | [IP_111.40.162.60] |
0x | 1 | objPrinters.Item(i+1) | Druckername .60 | [Drucker-1] |
1x | 2 | objPrinters.Item(i) | Portname | [IP_111.40.162.90_2] |
1x | 3 | objPrinters.Item(i+1) | Druckername .90 | [Kopierer_EG] |
2x | 4 | objPrinters.Item(i) | Portname | [111.40.162.105_1] |
2x | 5 | objPrinters.Item(i+1) | Druckername .105 | [EG – Netzdrucker] |
… usw.
Wir überprüfen den Portnamen, der die IP enthält, aber durch „IP_“ am Anfang oder „_X“ am Ende doch stark variieren kann. Dazu später mehr.
Ab Zeile 6 Nun wird für jeden Port durchgegangen (da jeder 2. Step den Druckernamen überspringt und zum nächsten Port springt):
Zeile 6 prüft mit Left(XXXXXX,2) die ersten 2 Stellen des Druckernamens. Sind diese nicht „\\“ so handelt es sich nicht um einen Druckerserver-Drucker und die Schleife wird fortgeführt.
Zeile 7 startet eine Schleife, die jeden Eintrag des Arrays printer durchgeht. In diesem Array stehen alle Drucker-IPs, die wir löschen wollen.
Zeile 8 prüft, ob der Eintrag des Arrays, der überprüft wird, 2 oder mehr Zeichen lang ist. Ehrlich gesagt, keine Ahnung warum ich damals diese Abfrage eingebaut habe…. 😀 wenn ich drauf kommt packe ich es als edit dazu. Mit Kommentaren wäre das nicht passiert, ich weiß.
Zeile 9 vergleicht den Druckerport (objPrinters.Item(i)) und die eingelesene IP, also: „IP_111.40.162.90_2 <> 111.40.162.90″ und prüft, ob der eingelesene Arraywert im Portnamen vorkommt. Ist der Rückgabewert > 0, so wurde der String 2 (IP aus dem Array) in String 1 (Portname) gefunden. Ist wie Regular Expressions, nur simpler. Wer gut regexen kann, hier könnte es Sinn machen.
Zeile 10 ist nun der Löschbefehl. objPrinters.Item(i+1) steht hier also für einen Druckernamen, der weder Druckserverdrucker noch lokaler Drucker noch ungewollter Drucker ist.
Da wir den Löschbefehl
printui.dll,PrintUIEntry /dl /n "Name"
über
WshShell.Run "Befehl"
laufen lassen müssen, dessen Befehl in Anführungszeichen stehen muss, kriegen wir ein kleines Problem. So würde es nämlich aussehen:
WshShell.Run "cmd /c rundll32 printui.dll,PrintUIEntry /dl /n "Druckername"
Der schlaue Leser weiß längst, dass hier der vbs Befehl nach dem /n als beendet angesehen wird und das Script damit fehlschlägt. Um den Namen des Druckers für printui.dll in Anführungszeichen zu kriegen, nutzen wir das Chr(34), das in ein “ umgewandelt wird. Der vbs Befehl läuft damit weiter und dem CMD Befehl wird ein “ vorgegeben.
Fertsch, Drucker wird gelöscht.
Voraussetzung für das Löschen ist natürlich das Recht dazu, Adminrechte also. CPAU kann helfen, doch wie immer Vorsicht, CPAU ist nicht sicher. Dank Kenny… 😛
Das Programm ist funktionsfähig, getestet und im Produktiveinsatz. Bei Fragen, Wünschen, Verbesserungen oder Kritik etc, Kommentarfeld ist unten oder per Mail/Kontakt.
delete-printer.vbs mit Parametern
Danke an Kenny für die Parameter Idee, macht das Script viel flexibler als zuvor!
Set WshShell = CreateObject("Wscript.Shell") Set WshNetwork = CreateObject("WScript.Network") Set objPrinters = WshNetwork.EnumPrinterConnections If WScript.Arguments.Count = 0 Then WScript.Echo "Usage: delete-printer.vbs "&Chr(34)&"Portname1"&Chr(34)&" "&Chr(34)&"Portname2"&Chr(34)&" ..." Else Dim first first = WScript.Arguments(0) Select Case first Case "help" WScript.Echo "Usage: delete-printer.vbs "&Chr(34)&"Portname1"&Chr(34)&" "&Chr(34)&"Portname2"&Chr(34)&" ..." Case "/?" WScript.Echo "Usage: delete-printer.vbs "&Chr(34)&"Portname1"&Chr(34)&" "&Chr(34)&"Portname2"&Chr(34)&" ..." Case "-?" WScript.Echo "Usage: delete-printer.vbs "&Chr(34)&"Portname1"&Chr(34)&" "&Chr(34)&"Portname2"&Chr(34)&" ..." Case "?" WScript.Echo "Usage: delete-printer.vbs "&Chr(34)&"Portname1"&Chr(34)&" "&Chr(34)&"Portname2"&Chr(34)&" ..." Case Else End Select End If For i = 0 to objPrinters.Count - 1 Step 2 If Left(objPrinters.Item(i+1),2) <> "\\" Then For Each item In WScript.Arguments If Len(item) >= 2 Then If InStr(objPrinters.Item(i), item) > 0 Then WshShell.Run "cmd /c rundll32 printui.dll,PrintUIEntry /dl /n " & Chr(34) & objPrinters.Item(i+1) & Chr(34) End If End If Next End If Next
.vbs Download (Rechtsklick -> Ziel speichern)
Okay, was hat sich geändert? Das Array ist weg und wird durch Parameter ersetzt. Diese werden beim Aufruf einfach hinten angehängt. Mehrere Parameter werden mit Leerzeichen getrennt angegeben. Ein Parameter mit Leerzeichen drin wird einfach in Anführungzeichen angegeben. Das lässt sich also beliebig kombinieren.
Wird kein Parameter angegeben (Doppelklick z.B.) bzw. ist der erste Parameter „help“ o.Ä. so wird eine Usage Hilfe angezeigt.
Beispiel:
delete-printer.vbs 111.40.162.90 111.40.162.105 "Farbe EG" "Farbe 1.OG"
Dieser Aufruf hätte 4 Parameter und würde alle Drucker löschen, die diese 2 Zeichenketten im Portnamen enthalten.
BTW: für VBS Neulinge, hier hab ich etwas Codehilfe her.
Weitergabe oder Verwendung der Anleitung nur mit voller Quellen- und Autorangabe! Ich bitte euch, seid fair.
Wenn du dir die Arbeit ein wenig erleichtern willst: Du könntest anstatt des Arrays direkt im Script einfach einen Parameter nutzen. Dadurch hättest du den Vorteil, dass du per Batch-Script dein VB-Script aufrufen kannst und auch später noch Automatisierungen damit betreiben kannst. 🙂
Ist das Korrekt das in der Zeile:
WshShell.Run „cmd /c rundll32 printui.dll,PrintUIEntry /dl /n “ & Chr(34) & oPrinters.Item(i+1) & Chr(34)
es wirklich oPrinters heißt? Da kam bei mir ein Error beim Ausführen, hab dann einfach ein objPrinters draus gemacht und nu gehts =)
Ist zwar schon betagt – aber immernoch sehr nützlich – 1000 Dank!
Der Fehler in der Download-Datei ist noch immer drin, aber wie der Vorposter schon schrieb „oPrinters“ in „objPrinters“ ändern!
Nochmals – Danke!
Hallo Thomas,
danke für dein Feedback!
Zur Feier des Tages, weil der Beitrag auch nach 5 Jahren noch hilft, habe ich den Beitrag überarbeitet, sodass die Codepassagen richtig dargestellt werden, und den Fehler in der Download-Datei behoben.
Cheers!