VBScript - Rundll32 to Install Printer on Remote Machines

Hello,

I am trying to use rundll32.exe to install printers with local printer ports on remote Windows 7 workstations.  This script is currently in phase 1.  Eventually I will need to rework it so that I can interchange variable data.  Before I can get to that point I need to resolve the syntax issue for oshell.run.  I'm currently getting a Printer pop-up box "Operation could not be completed (error 0x00000003)" due to oshell.run syntax.

Any help would be greatfully appreciated.

Thank you
Diammond




Const HKLM = &h80000002
strComputer = "computer1"
strInfFile = "C$\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf"
strPrinterName= "printer1"
strDriverName = "HP LaserJet 4250 PCL6"
strServer = "server1"


Set objWMIService = GetObject("winmgmts:" _
            & "{impersonationLevel=impersonate}!\\" _
            & strComputer & "\root\cimv2")
Set wmiSvc = GetObject("winmgmts:\\.\root\cimv2")
Set spooler = wmiSvc.Get("Win32_Service.Name='spooler'")
Set Reg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")
 
 
'----------------------------------------------------------------
ADD PORT TO REGISTRY OF REMOTE MACHINE
'----------------------------------------------------------------

If WScript.Arguments.Named.Exists("elevated") = False Then
  CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
  WScript.Quit
End If
reg.SetStringValue HKLM, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports", "\\server1\printer1"

'-----------------------------------------------------------------------------------------
Set objWMIService = GetObject("winmgmts:" _
            & "{impersonationLevel=impersonate}!\\" _
            & strComputer & "\root\cimv2")
Set WSHNetwork = WScript.CreateObject("WScript.Network")


Set oShell = WScript.CreateObject("WScript.shell")

'------------------------------------------------------------------------------------------
"Install Printer on Remote Machine"
'-------------------------------------------------------------------------------------------

oshell.run "rundll32 printui.dll,PrintUIEntry /if /c ""\\" & strComputer & """ /b """ & strPrinterName & """ /f """ & strInfFile & """ /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True


MsgBox ("Printer installation complete")
DiammondAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

RobSampsonCommented:
Hi, try this.  The rundll32 line needs to be run from a command prompt, so I've put cmd /c in front of it.

Const HKLM = &h80000002 
strComputer = "computer1"
strInfFile = "C$\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf"
strPrinterName= "printer1"
strDriverName = "HP LaserJet 4250 PCL6"
strServer = "server1"


Set objWMIService = GetObject("winmgmts:" _
            & "{impersonationLevel=impersonate}!\\" _
            & strComputer & "\root\cimv2")
Set wmiSvc = GetObject("winmgmts:\\.\root\cimv2") 
Set spooler = wmiSvc.Get("Win32_Service.Name='spooler'") 
Set Reg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")
 
 
'----------------------------------------------------------------
'ADD PORT TO REGISTRY OF REMOTE MACHINE 
'----------------------------------------------------------------

If WScript.Arguments.Named.Exists("elevated") = False Then
  CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
  WScript.Quit
End If 
reg.SetStringValue HKLM, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports", "\\server1\printer1" 

'-----------------------------------------------------------------------------------------
Set objWMIService = GetObject("winmgmts:" _
            & "{impersonationLevel=impersonate}!\\" _
            & strComputer & "\root\cimv2")
Set WSHNetwork = CreateObject("WScript.Network")


Set oShell = CreateObject("WScript.shell")

'------------------------------------------------------------------------------------------
'Install Printer on Remote Machine
'-------------------------------------------------------------------------------------------

strCommand = "cmd /c rundll32 printui.dll,PrintUIEntry /if /c ""\\" & strComputer & """ /b """ & strPrinterName & """ /f """ & strInfFile & """ /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """"
oShell.run strCommand, 1, True


MsgBox "Printer installation complete"

Open in new window

0
DiammondAuthor Commented:
Rob,
Thanks for responding.  I can see the command prompt open up but I am still getting the same error.  I believe the rundll32 syntax is incorrect.

Diammond
0
RobSampsonCommented:
Can you copy that command you see in the input box to a command prompt (without the cmd /c) and see if it has errors? I can't test it right now.

Rob.
0
Newly released Acronis True Image 2019

In announcing the release of the 15th Anniversary Edition of Acronis True Image 2019, the company revealed that its artificial intelligence-based anti-ransomware technology – stopped more than 200,000 ransomware attacks on 150,000 customers last year.

DiammondAuthor Commented:
If I copy the rundll32 syntax (without cmd /c and first and last quotes) to a command prompt, I get the error, "the arguments are invalid".
0
RobSampsonCommented:
Ok, I'll test it soon...
0
DiammondAuthor Commented:
Ok thanks.

Actually, I forgot to use names instead of variables when I tested at the command prompt.  Here is the syntax I used at the command prompt...

rundll32 printui.dll,PrintUIEntry /if /c "\\computer1" /b "printer1" /f C:\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf /r "\\server1\printer1" /m "HP LaserJet 4250 PCL6"

I get error, "Operation could not be completed (error 0x00000709).  Double check printer name and make sure that the printer is connected to the network."
0
RobSampsonCommented:
Try setting the port (/r switch) to an IP address:
rundll32 printui.dll,PrintUIEntry /if /c "\\computer1" /b "printer1" /f C:\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf /r "192.168.0.20" /m "HP LaserJet 4250 PCL6"

or
rundll32 printui.dll,PrintUIEntry /if /c "\\computer1" /b "printer1" /f C:\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf /r "IP_192.168.0.20" /m "HP LaserJet 4250 PCL6"

Once it works at a command line, we can plug that into the script.

Rob.
0
RobSampsonCommented:
You may need the TCIP/IP Port created on the target machine first....prnport.vbs can be used for this:
cscript prnport.vbs -a -r IP_192.168.0.20 -h 192.168.0.20 -o raw -n 515 -2e
0
DiammondAuthor Commented:
Hi Rob,

Your suggestions produced the same error.  For my circumstances, I need to have a local port that references a print server queue (\\server1\printer1).  The section in the code labeled 'ADD PORT TO REGISTRY OF REMOTE MACHINE', adds this local port to the registry.


The following syntax works for installing the printer on a local machine:

oshell.run "RUNDLL32 PRINTUI.DLL,PrintUIEntry /if /b ""printer1""    /f C:\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf   /r ""\\server1\printer1"" /m ""HP LaserJet 4250 PCL6"""  ,,true

It just needs to be modified to install on a remote machine.

Thanks
Diammond
0
DiammondAuthor Commented:
Rob,

I found that psexec will work from the command prompt to install the printer on the remote workstation.  Do you know how to incorporate this statement into my script?

'PSEXEC \\computer1 C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b "printer1" /f C:\windows\System32\DriverStore\FileRepository\prnhp002.inf_x86_neutral_e6daa9c39ac001a3\prnhp002.inf /r "\\server1\printer1" /m "HP LaserJet 4250 PCL6"

Thanks
Diammond
0
RobSampsonCommented:
Sure, change this:
strCommand = "cmd /c rundll32 printui.dll,PrintUIEntry /if /c ""\\" & strComputer & """ /b """ & strPrinterName & """ /f """ & strInfFile & """ /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """"

Open in new window


to this
strCommand = "C:\Tools\PSEXEC -accepteula \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """"
strCommand = InputBox("Prompt", "Title", strCommand)

Open in new window


I have added an InputBox so you can see the command as it would be executed.

Regards,

Rob.
0
DiammondAuthor Commented:
Hi Rob,

The strCommands did not work.  However, I added everything in the first strCommand (except -accepteula)  to oshell.run statement and the printer was successfully installed on the remote machine!

Here is the statement:

oshell.Run "cmd /K c:\temp\PSEXEC.exe \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """"

I think I may need to add admin credentials to this statement since other people who don't have admin rights to the workstations will eventually run this script.  I will try that out.

I'm making good progress.  Now I need to advance to next phase of the script which is to
figure out how to have the variables apply to different workstations, printer names, inf files, servers and driver names.  The idea is to be able to populate a text file with this info and have the script reference the text file for the variable info.  Please let me know if you have any ideas for this next phase.

Thanks for your help
Diammond
0
DiammondAuthor Commented:
Hi Rob,
Here is my first attempt at phase 2.  Can you look it over and let me know if it's feasible?
Please note where I am asking 'SET STATEMENT HERE?  'FOR EACH STATEMENT HERE?

Attached is an example of the workstations.txt file

Thanks
Diammond

Function CSVParse(ByVal strLine)
    ' Function to parse comma delimited line and return array
    ' of field values.

    Dim arrFields
    Dim blnIgnore
    Dim intFieldCount
    Dim intCursor
    Dim intStart
    Dim strChar
    Dim strValue

    Const QUOTE = """"
    Const QUOTE2 = """"""

    ' Check for empty string and return empty array.
    If (Len(Trim(strLine)) = 0) then
        CSVParse = Array()
        Exit Function
    End If

    ' Initialize.
    blnIgnore = False
    intFieldCount = 0
    intStart = 1
    arrFields = Array()

    ' Add ";" to delimit the last field.
    strLine = strLine & ";"

    ' Walk the string.
    For intCursor = 1 To Len(strLine)
        ' Get a character.
        strChar = Mid(strLine, intCursor, 1)
        Select Case strChar
            Case QUOTE
                ' Toggle the ignore flag.
                blnIgnore = Not blnIgnore
            Case ";"
                If Not blnIgnore Then
                    ' Add element to the array.
                    ReDim Preserve arrFields(intFieldCount)
                    ' Makes sure the "field" has a non-zero length.
                    If (intCursor - intStart > 0) Then
                        ' Extract the field value.
                        strValue = Mid(strLine, intStart, _
                            intCursor - intStart)
                        ' If it's a quoted string, use Mid to
                        ' remove outer quotes and replace inner
                        ' doubled quotes with single.
                        If (Left(strValue, 1) = QUOTE) Then
                            arrFields(intFieldCount) = _
                                Replace(Mid(strValue, 2, _
                                Len(strValue) - 2), QUOTE2, QUOTE)
                        Else
                            arrFields(intFieldCount) = strValue
                        End If
                    Else
                        ' An empty field is an empty array element.
                        arrFields(intFieldCount) = Empty
                    End If
                    ' increment for next field.
                    intFieldCount = intFieldCount + 1
                    intStart = intCursor + 1
                End If
        End Select
    Next
    ' Return the array.
    CSVParse = arrFields
End Function

'Procedure
Dim objFSO, strInput, objInput
Dim arrValues, strItem, strLine
Dim strOutput, strOutput1, strOutput2, strOutput3, strOutput4, strOutput5, objOutput, objOutput1, objOutput2, objOutput3, objOutput4, objOutput5

Const ForReading = 1
Const ForWriting = 2

' Specify the input file.
strInput = "c:\TEMP\VAR\workstations.TXT"

' Specify the output file that will be used to create the array.
strOutput = "c:\TEMP\VAR\array.txt"
strOutput1 = "c:\TEMP\VAR\array1.txt"
strOutput2 = "c:\TEMP\VAR\array2.txt"
strOutput3 = "c:\TEMP\VAR\array3.txt"
strOutput4 = "c:\TEMP\VAR\array4.txt"

' Specify the output file that will list result info.
strOutput5 = "c:\TEMP\VAR\results.txt"



' Open the input file for read access.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInput = objFSO.OpenTextFile(strInput, ForReading)

' Open the output files for writing.
Set objOutput = objFSO.CreateTextFile(strOutput, ForWriting)
Set objOutput1 = objFSO.CreateTextFile(strOutput1, ForWriting)
Set objOutput2 = objFSO.CreateTextFile(strOutput2, ForWriting)
Set objOutput3 = objFSO.CreateTextFile(strOutput3, ForWriting)
Set objOutput4 = objFSO.CreateTextFile(strOutput4, ForWriting)
Set objOutput5 = objFSO.CreateTextFile(strOutput5, ForWriting)

' Add header to the result file.
hdr = "Device;" & _
	"Results(0=Printer Successfully Installed)" 
	
	objOutput5.WriteLine(hdr)
	

	
' Read the input file.
Do Until objInput.AtEndOfStream
strLine = objInput.ReadLine

' Skip blank lines.
If (Trim(strLine) <> "") Then
' Parse the fields in the file.
arrValues = CSVParse(strLine)
arrValues1 = CVSParse(strLine)
arrValues2 = CVSParse(strLine)
arrValues3 = CVSParse(strLine)
arrValues4 = CVSParse(strLine)

' Write the value in the first,second, third, fourth, fifth fields to the array.txt output file.
objOutput.WriteLine arrValues(0)
objOutput1.WriteLine arrValues1(1)
objOutput2.WriteLine arrValues2(2)
objOutput3.WriteLine arrValues3(3)
objOutput4.WriteLine arrValues4(4)
		  
End If
	  
Loop
	 	  
'Create array from array.txt file.

Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim arrLines
arrLines = split(objFSO.OpenTextFile(strOutput).ReadAll(), VbCrLf)
	  
For counter = 0 to UBound(arrLines)-1 
strComputer = arrLines(counter)
wscript.echo strComputer
'--------------

Dim arrLines1
arrLines1 = split(objFSO.OpenTextFile(strOutput1).ReadAll(), VbCrLf) 

For counter1 = 0 to UBound(arrLines1)-1 
strServerName = arrLines1(counter)
wscript.echo strServerName

'--------------
Dim arrLines2
arrLines2 = split(objFSO.OpenTextFile(strOutput2).ReadAll(), VbCrLf) 

For counter2 = 0 to UBound(arrLines2)-1 
strPrinterName = arrLines2(counter)
wscript.echo strPrinterName

'------------------

Dim arrLines3
arrLines3 = split(objFSO.OpenTextFile(strOutput3).ReadAll(), VbCrLf) 

For counter3 = 0 to UBound(arrLines3)-1 
strDriverName = arrLines3(counter)
wscript.echo strDriverName

'------------------

Dim arrLines4
arrLines4 = split(objFSO.OpenTextFile(strOutput4).ReadAll(), VbCrLf) 

For counter4 = 0 to UBound(arrLines4)-1 
strInfFile = arrLines4(counter)
wscript.echo strInfFile


'Install printers and write to results.txt file.

	On Error Resume Next
	Const HKLM = &h80000002 
	Dim strComputer	
	Dim strServerName
	Dim StrPrinterName
	Dim StrDriverName
	Dim StrInfFile
	strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports"
	strValue = "\\" & strServer & "\" & strPrinterName

	Set objWMIService = GetObject("winmgmts:" _
		& "{impersonationLevel=impersonate}!\\" _
		& strComputer & "\root\cimv2")
	Set Reg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
	strComputer & "\root\default:StdRegProv")
	Set WSHNetwork = WScript.CreateObject("WScript.Network")
	Set oShell = WScript.CreateObject("WScript.shell")

		If Err <> 0 Then
		txt = strComputer & " " & "-" & " " & "DEVICE NOT FOUND"
		objOutput5.WriteLine(txt)
		
		Else
		
		'SET STATEMENT HERE?
		'FOR EACH STATEMENT HERE?
		
    		
		'Create Port
			If WScript.Arguments.Named.Exists("elevated") = False Then
  				CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
  			WScript.Quit
			End If 
			reg.SetStringValue HKLM, strKeyPath, strValue
		
						
		'Install Printer on Remote Workstation
			'SET STATEMENT HERE?
			'FOR EACH STATEMENT HERE?
			oshell.Run "cmd /K c:\temp\PSEXEC.exe \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """"

			RetVal = oshell.Run("cmd /K c:\temp\PSEXEC.exe \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """")
	
			If RetVal = 0 then
			txt = strComputer & ";" & _
			"Printer Installed Successfully"
			objOutput5.WriteLine(txt)
			
			Else 

			txt = strComputer & ";" & _
			"Error Installing Printer. Please Install Manually"
			objOutput5.WriteLine(txt)


			End If
			Next
		
	
		
		End If
		
	
Next

Open in new window


EDIT: Code placed in code tags -- RobSampson
workstations.txt
0
RobSampsonCommented:
Hi, firstly, I notice you have two semi-colons after the computer name in the file.  This should probably be only one.

Secondly, here's my take on the script you might need.  I have used quite different code to loop through workstations.txt and split the fields into variables, then run the PSExec command.

Note that I have also added a commented out line to run PSExec with a username and password, but you wouldn't need that if you run the script from your computer as an admin.

Just thinking as well, you may need to add the /ga switch to the PrintUIEntry command, to make it a per machine connection.  The reason for that is, when you are running the command via PSExec, it is executing under the user context of the person running the remote script, and not the person currently logged on to the remote machine.

Regards,

Rob.

Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const HKLM = &h80000002 

If WScript.Arguments.Named.Exists("elevated") = False Then
	CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
	WScript.Quit
End If 

' Specify the input file that will contain workstation and printer info
strInput = "c:\TEMP\VAR\workstations.TXT"
' Specify the output file that will list result info.
strOutput = "c:\TEMP\VAR\results.csv"
Set objInput = objFSO.OpenTextFile(strInput, 1, False)
Set objOutput = objFSO.CreateTextFile(strOutput, True)
objOutput.WriteLine """Computer"",""Server"",""PrinterName"",""DriverName"",""InfFile"",""Result"""
While Not objInput.AtEndOfStream
	strLine = objInput.ReadLine
	'Make sure the line contains the delimiter, and 5 fields
	If InStr(strLine, ";") > 0 Then
		arrFields = Split(strLine, ";")
		If UBound(arrFields) = 5 Then
			strComputer = arrFields(0)
			strServer = arrFields(1)
			strPrinterName = arrFields(2)
			strDriverName = arrFields(3)
			strInfFile = arrFields(4)
			strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports"
			strValue = "\\" & strServer & "\" & strPrinterName
			On Error Resume Next
			Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
			If Err <> 0 Then
				objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""<CONNECTION ERROR>"""
				Err.Clear
				On Error GoTo 0
			Else
				Err.Clear
				On Error GoTo 0
				'Create Port
				objReg.SetStringValue HKLM, strKeyPath, strValue
		
				'Install Printer on Remote Workstation
				'RetVal = objShell.Run("cmd /K c:\temp\PSEXEC.exe -u domain\user -p password \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True)
				RetVal = objShell.Run("cmd /K c:\temp\PSEXEC.exe \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True)

				If RetVal = 0 Then
					strResult = "Printer Installed Successfully"
				Else 
					strResult = "Error Installing Printer. Please Install Manually"
				End If
				objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""" & strResult & """"
			End If
		Else
			WScript.Echo "Invalid amount of fields on line: " & strLine
		End If
	End If
Wend
objInput.Close
objOutput.Close
WScript.Echo "Done"

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
DiammondAuthor Commented:
Thanks Rob.

I'm getting two Windows Script host errors:
1) Invalid amount of fields on line: Computer, ServerName, PrinterName, PrinterDriver, InfFile
(these are the headers in the input doc)

2) Invalid amount of fields on line: and then it lists the 5 data values stored in the variables
0
RobSampsonCommented:
Are the fields separated by commas, or semi-colons?

Line 22 should be
		If UBound(arrFields) = 4 Then

Open in new window


and not 5.  Sorry....the lower bound starts at zero.

If the file is separated by commas, change these lines
	If InStr(strLine, ";") > 0 Then
		arrFields = Split(strLine, ";")

Open in new window


to this
	If InStr(strLine, ",") > 0 Then
		arrFields = Split(strLine, ",")

Open in new window


Also, don't forget to remove the double semi-colon from your workstations.txt file after the computer name.

Rob.
0
DiammondAuthor Commented:
I tested the script on one workstation and the printer was installed on that workstation!  There appears to be something wrong with the results.cvs file .  It is consistenly in use .  I'm having to open it as read-only and this is the output:

Computer      Server      PrinterName      DriverName      InfFile      Result
Computer      ServerName      PrinterName      PrinterDriver      InfFile      <CONNECTION ERROR>

Tomorrow I will test the script in depth on multiple workstations.

Thanks again!

Diammond
0
RobSampsonCommented:
The script needs to complete before the CSV can be opened with write access.  To give you some visual progress, I've added a couple of extra outputs, so if you run the script from a command prompt with
cscript C:\Temp\InstallPrinters.vbs

you will see the status updates.

Regards,

Rob,

Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const HKLM = &h80000002 

If WScript.Arguments.Named.Exists("elevated") = False Then
	CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
	WScript.Quit
End If 

' Specify the input file that will contain workstation and printer info
strInput = "c:\TEMP\VAR\workstations.TXT"
' Specify the output file that will list result info.
strOutput = "c:\TEMP\VAR\results.csv"
Set objInput = objFSO.OpenTextFile(strInput, 1, False)
Set objOutput = objFSO.CreateTextFile(strOutput, True)
objOutput.WriteLine """Computer"",""Server"",""PrinterName"",""DriverName"",""InfFile"",""Result"""
While Not objInput.AtEndOfStream
	strLine = objInput.ReadLine
	'Make sure the line contains the delimiter, and 5 fields
	If InStr(strLine, ";") > 0 Then
		arrFields = Split(strLine, ";")
		If UBound(arrFields) = 4 Then
			strComputer = arrFields(0)
			strServer = arrFields(1)
			strPrinterName = arrFields(2)
			strDriverName = arrFields(3)
			strInfFile = arrFields(4)
			strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports"
			strValue = "\\" & strServer & "\" & strPrinterName
			On Error Resume Next
			WScript.Echo "Connecting to " & strComputer & "..."
			Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
			If Err <> 0 Then
				WScript.Echo "Connection to " & strComputer & " failed."
				objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""<CONNECTION ERROR>"""
				Err.Clear
				On Error GoTo 0
			Else
				WScript.Echo "Installing printer on " & strComputer & "."
				Err.Clear
				On Error GoTo 0
				'Create Port
				objReg.SetStringValue HKLM, strKeyPath, strValue
		
				'Install Printer on Remote Workstation
				'RetVal = objShell.Run("cmd /K c:\temp\PSEXEC.exe -u domain\user -p password \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True)
				RetVal = objShell.Run("cmd /K c:\temp\PSEXEC.exe \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True)

				If RetVal = 0 Then
					strResult = "Printer Installed Successfully"
				Else 
					strResult = "Error Installing Printer. Please Install Manually"
				End If
				objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""" & strResult & """"
			End If
		Else
			WScript.Echo "Invalid amount of fields on line: " & strLine
		End If
	End If
Wend
objInput.Close
objOutput.Close
WScript.Echo "Done"

Open in new window

0
DiammondAuthor Commented:
Hi Rob,
Just wanted to give you an update.  Still having the same issue with the results.csv file.  However, I ran into a bigger issue.  The rundll32.exe statement works fine when running on windows 7, 32-bit workstations but gets hung on windows 7, 64-bit workstations.  I've executed the rundll32.exe statement via the command prompt on the 64 bit workstations to rule out the script as being the issue (ensuring the inf file is inf file from 64 bit device).  It hung at the prompt as well. Today, I will try to work through this issue.

Thanks
Diammond
0
DiammondAuthor Commented:
Hi Rob,
Please disregard my last update.  In my attempt to troubleshoot why the script was not working when the input file contained multiple workstations, I executed rundll32 statement at the command prompt of the 64-bit workstations and it hung because I didn't have the correct syntax.  It is working for both 32-bit and 64-bit devices with some manual intervention....

The command prompt opens up as the script installs the printer on the first workstation.  Once the printer is installed successully and returns code 0, the command prompt stays open and the script does not progress to the next workstation in the input file.  If I manually close the command prompt, the script starts the install on the next workstation.  I have to keep doing the manual intervention until the script traverses through all the workstations in input file.  Once the printer is installed on the last workstation, the script echos "done".  The results.csv file is no longer in use and can be opened.  The results.csv file is not updating properly.  I think this is because of the manual intervention process.  The result column for all workstations is "Error Installing Printer. Please Install Manually" even though the printers installed successfully.

Any thoughts?

Thanks
Diammond
0
DiammondAuthor Commented:
Ok.  I changed "cmd /K" in the psexec statement to "cmd /c" and now the command prompt terminates after every printer install and the results.csv appears to be reporting correctly.  Still testing......

Thanks
Diammond
0
RobSampsonCommented:
Sorry for my late response.  That all makes sense.  The cmd /k makes the DOS window stay open after the command executes, and when you close the command prompt (using the X), the return code is non zero from the command prompt.  However, if you had have typed "exit" at the prompt to close it, you would have had a return code of zero, so it should have been OK that way too, although still requiring you to manually close it.  I use cmd /k when testing commands so that you can see the output that PSExec produced.  You are right to make that cmd /c in cases when you know the command works.

Just another thought, I wonder if for 64 bit machines, you need to execute
C:\Windows\System32\rundll32.exe
or
C:\Windows\SysWOW64\rundll32.exe

I guess it would have to be System32, since that would be the 64 bit version of rundll32.exe, and printers need to be installed in the 64 bit architecture.

Anyway, hopefully the testing continues well.

Regards,

Rob.
0
DiammondAuthor Commented:
Hi Rob,
I'm just about finished testing.  I had to add a couple of things to the script: 1) code to skip the first line of the input file so that the header is not read as an invalid device, 2) code
to restart the print spooler.  I found that the printers were installing fine but not being associated with the local printer port until I restarted the print spooler.



Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const HKLM = &h80000002


If WScript.Arguments.Named.Exists("elevated") = False Then
      CreateObject("Shell.Application").ShellExecute "wscript.exe", """" & WScript.ScriptFullName & """ /elevated", "", "runas", 1
      WScript.Quit
End If


' Specify the input file that will contain workstation and printer info
strInput = "c:\TEMP\workstations.TXT"
' Specify the output file that will list result info.
strOutput = "c:\TEMP\results.csv"
Set objInput = objFSO.OpenTextFile(strInput, 1, False)
Set objOutput = objFSO.CreateTextFile(strOutput, True)
objOutput.WriteLine """Computer"",""Server"",""Printer"",""Driver"",""InfFile"",""Result"",""PrintSpoolerReset"""
'skip the header in the input file
objInput.SkipLine
While Not objInput.AtEndOfStream
      strLine = objInput.ReadLine
      
      'Make sure the line contains the delimiter, and 5 fields
      If InStr(strLine, ";") > 0 Then
            arrFields = Split(strLine, ";")
            If UBound(arrFields) = 4 Then
                  strComputer = arrFields(0)
                  strServer = arrFields(1)
                  strPrinterName = arrFields(2)
                  strDriverName = arrFields(3)
                  strInfFile = arrFields(4)
                  strKeyPath = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports"
                  strValue = "\\" & strServer & "\" & strPrinterName
                  On Error Resume Next
                  Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
                  
                  If Err <> 0 Then
                  
                        objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""<DEVICE NOT FOUND>"""
                        Err.Clear
                        On Error GoTo 0
                        
                  Else
                        Err.Clear
                        On Error GoTo 0
                        'Create Port
                        objReg.SetStringValue HKLM, strKeyPath, strValue
            
                        'Install Printer on Remote Workstation
                        
                        RetVal = objShell.Run("cmd /c c:\temp\PSEXEC.exe -u domain\user -p password \\" & strComputer & " C:\Windows\System32\Cmd.exe /c RUNDLL32.exe PRINTUI.DLL,PrintUIEntry /if /b """ & strPrinterName & """ /f " & strInfFile & " /r ""\\" & strServer & "\" & strPrinterName & """ /m """ & strDriverName & """", 1, True)                        
                                                
                        If RetVal = 0 Then
                              strResult = "Printer Installed Successfully"
                              
                        Else
                              strResult = "Error Installing Printer. Please Install Printer Manually."
                              
                        End If
                  
                  Set objWMIService = GetObject("winmgmts:" _
                      & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
                        Set colServiceList = objWMIService.ExecQuery _
                    ("Select * from Win32_Service where Name='Spooler'")
                        For each objService in colServiceList
                       errReturn = objService.StopService()
                       Wscript.Sleep 20000
                       If errReturn = 0 Then
                       errSPResult = "Print Spooler Service Stopped Successfully"
                       Else
                       errSPResult = "Unable to Stop Print Spooler Service"
                       End If
                        Next
                                    
                        For each objService in colServiceList
                       errReturn = objService.StartService()
                       Wscript.Sleep 20000
                       If errReturn = 0 Then
                       errSTResult = "Print Spooler Service Started Successfully"
                       Else
                       errSTResult = "Unable to Start Print Spooler Service"
                       End If
                        Next
                  
                                    
                        objOutput.WriteLine """" & strComputer & """,""" & strServer & """,""" & strPrinterName & """,""" & strDriverName & """,""" & strInfFile & """,""" & strResult & """,""" & errSPResult & """,""" & errSTResult & """"
                  
                  
                  
                  End If
            Else
                  WScript.Echo "Invalid amount of fields on line: " & strLine
            End If
      End If
Wend
objInput.Close
objOutput.Close
WScript.Echo "Done"
0
DiammondAuthor Commented:
Rob,
I have finished testing the script.  It is working great!

Thanks so much for your help!

Diammond
0
RobSampsonCommented:
Hi.  Great to hear!  I'm not sure the 20 second wait between stopping and starting the service is required, you might get away with 5 or so, but otherwise, great job!  Glad it's working for you.

Rob.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
VB Script

From novice to tech pro — start learning today.