As part of a migration from Windows XP to Windows 7, I was asked to come up with a way to export the network printers installed on the XP machines such that they could be reinstalled on the Windows 7 machines. We did not want to capture local printers (printers installed via TCP/IP or connected via USB) or virtual printers (like the Adobe PDF virtual printer or the Microsoft XPS Document Writer). I thought that migrating the printers was less attractive because the source machines are 32-bit Windows XP but the destination machines are 64-bit Windows 7 and the drivers are therefore different.
There are a number of ways to export print queues, printer settings, and printer ports, but for my purposes, I decided that all I wanted was to determine the name of each printer (eg.: \\SERVER\Printer) on the XP machine, export that to a text file on a network share, and then run PrintUI.exe /ga on the Windows 7 machine, looping through the lines from the text file as input.
(Check out the Printer Migration wizard by launching PrintBrmUI.exe, or the command line version %WINDIR%\System32\Spool\Tools\Printbrm /?, for alternatives to printui.exe.)
As an added benefit, I’m also exporting the name of the default printer so that it can be set programatically in the new environment.
The code is a work in progress, but I hope it helps get your started.
The VBScripts
Here are the scripts I’ve pieced together.
exportPrinters.vbs
The exportPrinters.vbs script creates two text files in H:\PRINTERS, so adjust your path accordingly.
Const ForWriting = 2
Set objNetwork = CreateObject("Wscript.Network")
strName = objNetwork.UserName
strDomain = objNetwork.UserDomain
strUser = strDomain & "\" & strName
'strText = strUser & vbCrLf
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' Export a list of network printers to a text file
Set colPrinters = objWMIService.ExecQuery _
("Select * From Win32_Printer Where Local = FALSE")
For Each objPrinter in colPrinters
strText = strText & objPrinter.Name & vbCrLf
Next
Set objFSO = CreateObject("Scripting.FileSystemObject")
strFolder = "H:\PRINTERS"
If Not objFSO.FolderExists(strFolder) Then
objFSO.CreateFolder(strFolder)
End If
Set objFile = objFSO.CreateTextFile _
("H:\PRINTERS\printers.txt", ForWriting, False)
objFile.Write strText
objFile.Close
' Export the default printer separately
Set colPrinters = objWMIService.ExecQuery _
("Select * From Win32_Printer Where Default = TRUE")
For Each objPrinter in colPrinters
strText = objPrinter.Name
Next
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.CreateTextFile _
("H:\PRINTERS\default.txt", ForWriting, False)
objFile.Write strText
objFile.Close
importPrinters.vbs
Note that, if your users in Windows 7 are not administrators, you will need to run the script as an administrator (there are a few different ways) or else you’ll get a UAC prompt for each printer installation and the restarting of the print spooler.
Again, watch the paths. This is a home-grown script for my specific environment.
Option Explicit
'This script must be run with administor privileges
'If it is not run with administrator privileges, it will launch a UAC prompt for each printer as it loops through the list
'Here's an interesting article about running VBS as a different user:
'http://blogs.technet.com/b/heyscriptingguy/archive/2006/04/28/how-can-i-use-the-runas-command-to-run-a-script-under-alternate-user-credentials.aspx
'For example:
'runas /profile /user:[username]\[password] "cscript.exe \"F:\Printer Driver Research\Automation\printers-import.vbs"\"
Dim objNetwork, strComputer, strName, strFolder, objFSO, strTextFile, strData, strLine, arrLines, strRunCmd, WshShell
CONST ForReading = 1
'Create a Network Object
Set objNetwork = CreateObject("Wscript.Network")
'Get the local machine name from the Network Object
strComputer = objNetwork.ComputerName
'Get the user's username from the Network Object
strName = objNetwork.UserName
'Create a File System Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Save the name of the text file as a variable
'Note that the script must be run as an administrator and that invoking the script with
'Run As causes the script to run as though it's located in the same directory as "%SystemRoot%\System32\WScript.exe"
'Hence the need to pass the full path to the printers.txt file
strTextFile = "H:\PRINTERS\printers.txt"
'Open the text file - strData now contains the whole file
strData = objFSO.OpenTextFile(strTextFile,ForReading).ReadAll
'Split the text file into lines
arrLines = Split(strData,vbCrLf)
'Initialize the wshShell
Set WshShell = WScript.CreateObject("WSCript.shell")
'Step through the lines
For Each strLine in arrLines
If Len(strLine) > 0 Then
'Only run the process on lines that aren't blank
' strRunCmd = "rundll32 printui.dll,PrintUIEntry /ga /c\\" & strComputer & " /n" & strLine & ""
strRunCmd = """printui.exe"" /ga /q /c\\" & strComputer & " /n" & strLine & ""
'Echo back the command to be run
' WScript.Echo strRunCmd
'This launches printui.exe
' strRunCmd = """printui.exe"""
'Run the command, display the window, and wait for the command to complete before continuing
Dim result
result = WshShell.Run(strRunCmd, 1, True)
' WScript.Echo result
'Write to the application log that the printer was installed
If result = 0 Then
WshShell.LogEvent 0, "User: " & strName & " - Event: attempted to install printer " & strLine & " with [" & strRunCmd & "] (success unknown)"
End If
' WScript.Echo "Processed printer: " & strLine
End If
Next
'Cleanup
Set objFSO = Nothing
'Wait 10 seconds
WScript.Sleep 10000
'Restart the Print Spooler
RestartService "Print Spooler", True
Sub RestartService( myService, blnQuiet )
' This subroutine restarts a service
' Arguments:
' myService use the service's DisplayName
' blnQuiet if False, the state of the service is displayed
' every second during the restart procedure
'
' Written by Rob van der Woude
' http://www.robvanderwoude.com
' Standard housekeeping
Dim colServices, colServicesTest, objService
Dim objServiceTest, objWMIService, strQuery, strTest
' Create a WMI object
Set objWMIService = GetObject( "winmgmts:\\.\root\CIMV2" )
' Query the services for "our" service
strQuery = "SELECT * FROM Win32_Service WHERE DisplayName='" & myService & "'"
Set colServices = objWMIService.ExecQuery( strQuery, "WQL", 48 )
' Loop through the "collection" of returned services
For Each objService In colServices
' See if we need to tell the user we're going to stop the service
If Not blnQuiet Then
WScript.Echo "Stopping " & myService
End If
' Stop the service
objService.StopService
' Wait until the service is stopped
Do Until strTest = "Stopped"
' Create a new object for our service; this work-around is required
' since otherwise the service's state information isn't properly updated
Set colServicesTest = objWMIService.ExecQuery( strQuery, "WQL", 48 )
' Loop through the "collection" of returned services
For Each objServiceTest In colServicesTest
' Check the service's state
strTest = objServiceTest.State
' See if we need to show the progress
If Not blnQuiet Then
WScript.Echo "State: " & strTest
End If
' Wait 1 second
WScript.Sleep 1000
Next
' Clear the temporary object
Set colServicesTest = Nothing
Loop
' See if we need to tell the user we're going to (re)start the service
If Not blnQuiet Then
WScript.Echo "Starting " & myService
End If
' Start the service
objService.StartService
' Wait until the service is running again
Do Until strTest = "Running"
' Create a new object for our service; this work-around is required
' since otherwise the service's state information isn't properly updated
Set colServicesTest = objWMIService.ExecQuery( strQuery, "WQL", 48 )
' Loop through the "collection" of returned services
For Each objServiceTest In colServicesTest
' Check the service's state
strTest = objServiceTest.State
' See if we need to show the progress
If Not blnQuiet Then
WScript.Echo "State: " & strTest
End If
' Wait 1 second
WScript.Sleep 1000
Next
' Clear the temporary object
Set colServicesTest = Nothing
Loop
Next
End Sub
setDefaultPrinter.vbs
Finally, we want to set the default printer. The printer will have to already exist (obviously). If you’ve just fired off the printui.exe /ga command to install the printer, it won’t be available until the print spooler is restarted. So wait a minute or two before running the script.
Option Explicit
Dim objNetwork, strComputer, objFSO, strTextFile, strData, strLine, arrLines, strRunCmd, wshShell
CONST ForReading = 1
'Create a Network Object
Set objNetwork = CreateObject("Wscript.Network")
'Get the local machine name from the Network Object
strComputer = objNetwork.ComputerName
'Save the name of the text file as a variable
strTextFile = "H:\PRINTERS\default.txt"
'Create a File System Object
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Open the text file - strData now contains the whole file
strData = objFSO.OpenTextFile(strTextFile,ForReading).ReadAll
'Split the text file into lines
arrLines = Split(strData,vbCrLf)
'Initialize the wshShell
Set wshShell = WScript.CreateObject ("WSCript.shell")
'Step through the lines
For Each strLine in arrLines
If Len(strLine) > 0 Then
'Only run the process on lines that aren't blank
' strRunCmd = "rundll32 printui.dll,PrintUIEntry /y /c\\" & strComputer & " /n" & strLine & ""
strRunCmd = """printui.exe"" /y /c \" & strComputer & " /n """ & strLine & """"
' wscript.echo strRunCmd
wshShell.Run strRunCmd
' wscript.echo "Processed printer: " & strLine
End If
Next
'Cleanup
Set objFSO = Nothing
As the migration proceeds, I’ll come back and tweak the scripts. Please feel free to leave comments and suggestions.
Thanks go to Rob van der Woude for his terrific Command Line Printer Control page, as well as many others.
