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.