VBS - Get Default Printer - vbscript

Using the Wscript.Network object shown below, is there an easy way to retrieve the default printer on a machine? I know how to set the default printer, but I'm looking to get the current default printer name. I have a mixture of Windows 2000, XP, and 7 clients and don't want to use WMI for that reason.
Set objNetwork = CreateObject("WScript.Network")
Set objLocalPrinters = objNetwork.EnumPrinterConnections

The WshNetwork.EnumPrinterConnections collection doesn't provide any information about the default printer. You can try retrieving the default printer name from the registry instead, though I'm not sure if it's reliable:
Set oShell = CreateObject("WScript.Shell")
strValue = "HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Device"
strPrinter = oShell.RegRead(strValue)
strPrinter = Split(strPrinter, ",")(0)
WScript.Echo strPrinter
As for WMI, it's true that some WMI classes and class members aren't available on older Windows versions. For example, the Win32_Printer.Default property that indicates whether the printer is the default one, doesn't exist on Windows 2000/NT. Nevertheless, there's a simple workaround for finding the default printer on those Windows versions, which consists in checking for the PRINTER_ATTRIBUTE_DEFAULT attribute in each printer's Attribute bitmask:
Const ATTR_DEFAULT = 4
strComputer = "."
Set oWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colPrinters = oWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each oPrinter in colPrinters
If oPrinter.Attributes And ATTR_DEFAULT Then
Wscript.Echo oPrinter.ShareName
End If
Next
This code works on later Windows versions as well.
For details, check out this Hey, Scripting Guy! article: Is There Any Way to Determine the Default Printer On a Computer?

From (MSDN):
The EnumPrinterConnections method returns a collection. This collection is an array that associates pairs of items — network printer local names and their associated UNC names. Even-numbered items in the collection represent printer ports. Odd-numbered items represent the networked printer UNC names. The first item in the collection is at index zero (0).
So there is little chance to get the default printer from this collection. Sorry
Greetz,
GHad

for MS ACCESS oPrinter.ShareName is null but oPrinter.Caption works well!
Dim strComputer As String
Dim oWMI As Object
Dim colPrinters
Dim oPrinter
Const ATTR_DEFAULT = 4
strComputer = "."
Set oWMI = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colPrinters = oWMI.ExecQuery("SELECT * FROM Win32_Printer")
For Each oPrinter In colPrinters
If oPrinter.Attributes And ATTR_DEFAULT Then
Debug.Print oPrinter.Caption
End If
Next

Related

VBScript Strange Issue with HTA and Type mismatch error

When I run the following script on it's own by double clicking, it works just fine. It returns the last logged on user as expected. But when I run it from the HTA I have been developing as a front end to all of my scripts, I get a type mismatch error on the "wscript.echo strvalue" line. I have tried everything to get it to work, like changing permissions on mshta.exe to full control for myself. I simply can't get it to run from the HTA without getting an error, but it works 100% as expected on its own. I am completely stumped.
strinput = "myserver"
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strinput & "\root\default:StdRegProv")
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
strValueName = "LastLoggedOnUser"
objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
Wscript.Echo strValue
By default, Windows 64-bit uses MSHTA.EXE 32-bit. The registry has a separate branches for 64-bit and 32-bit apps, thus WMI can't find the registry value you are looking for.
Save the code below to e. g. C:\test\tmp.hta, try to launch it from explorer by double-click (32-bit by default) - you will get null, and then launch via Run dialog (Win+R) with path: %windir%\system32\mshta.exe "C:\test\tmp.hta" (64-bit), the result will be your username.
<html>
<head>
<script language="vbscript">
Sub window_onload()
Const HKEY_LOCAL_MACHINE = &H80000002
Set objRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
strKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"
strValueName = "LastLoggedOnUser"
objRegistry.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
document.body.innerText = strValue
End Sub
</script>
</head>
<body>
</body>
</html>
Note that many other stuff within scripts depends on application architecture, e. g. number of ActiveX are available only in 32-bit version, so they should be launched via %windir%\SysWOW64\ (Windows 32-bit on Windows 64-bit subsystem).
Use Msgbox function instead of Wscript.Echo method. HTAs use the Internet Explorer Scripting Object Model which does not contain Wscript object (this belongs to Windows Script Host Object Model).
Read HTA: Why Can’t I Use Wscript.Echo?:
You might have noticed that when it came time to report back the
operating system version we used the VBScript Msgbox function rather
than the more common Wscript.Echo. Why didn’t we use Wscript.Echo?
Here’s why:
As it turns out the various Wscript methods - Wscript.Echo,
Wscript.Sleep, Wscript.Quit, etc. - are designed solely to run under
the Windows Script Host environment. When we’re working in an HTA
we’re not running under WSH; instead we’re running under the MSHTA
process. Because of that the Wscript methods are not available to us
(nor can we create them). Consequently we need to find workarounds for
each method, and Msgbox is a perfectly adequate replacement for
Wscript.Echo. (We’ll talk about workarounds for other methods - such
as Wscript.Sleep - when we get to them.)
The moral of the story: Don’t bother with Wscript.Echo; it won’t work.
Edit: with Wscript.Echo TypeName(strValue) & vbNewLine & VarType(strValue):
==> C:\Windows\System32\cscript.exe D:\VB_scripts\SO\33505295.vbs
String
8
==> C:\Windows\SysWOW64\cscript.exe D:\VB_scripts\SO\33505295.vbs
Null
1
Tried in a simple HTA which gives the same (different) result
==> C:\Windows\System32\mshta.exe 33505295.hta
versus
==> C:\Windows\SysWOW64\mshta.exe 33505295.hta
Conclusion. Check HTA file type association. For instance, ftype htafile in my Windows 8 (64bit) returns (surprisingly?) the same value which causes wrong behaviour on double click:
==> assoc .hta
.hta=htafile
==> ftype htafile
htafile=C:\Windows\SysWOW64\mshta.exe "%1" {1E460BD7-F1C3-4B2E-88BF-4E770A288AF5}%U{1E460BD7-F1C3-4B2E-88BF-4E770A288AF5} %*
I have had the same challenge a few weeks ago.
The following code provided me the possibility to see who is currently logged onto a remote computer.
I hope this can help you.
Sub ActionGetCurrentUser(strCPU) 'strCPU is the computername
set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strCPU & "\root\cimv2")
set Items = objWMI.ExecQuery("Select * From Win32_ComputerSystem")
For Each obj in Items
OutStr = right(obj.username,9)
Next
Resultstring = "Logged in User is: " & OutStr
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("defaultNamingContext")
strTarget = "LDAP://" & strDNSDomain
' ---------------- Write the User's account & password to a variable -------------------
strCurrentuser = Currentuser.value
strPassword = PasswordArea.value
' ---------------- Connect to Ad Provider ----------------
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Properties("User ID") = strCurrentUser ' pass credentials - if you omit this, the search is performed....
objConnection.Properties("Password") = strPassword ' ... with the current credentials
objConnection.Properties("Encrypt Password") = True ' only needed if you set "User ID" and "Password"
objConnection.Open "Active Directory Provider"
Set objCmd = CreateObject("ADODB.Command")
Set objCmd.ActiveConnection = objConnection
objCmd.CommandText = "SELECT DisplayName FROM '" & strTarget & "' WHERE extensionAttribute11 = '" & OutStr & "'"
Const ADS_SCOPE_SUBTREE = 2
objCmd.Properties("Page Size") = 100
objCmd.Properties("Timeout") = 30
objCmd.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCmd.Properties("Cache Results") = False
Set objRecordSet = objCmd.Execute
If objRecordset.Recordcount = 0 then ' If no user is found then the recordcount will be 0
msgbox "No user is logged on"
Resultstring = ""
Set objCmd = Nothing
Set objRootDSE = Nothing
Set objRecordSet = Nothing
Set objWMI = Nothing
Set Items = Nothing
exit sub
End if
Set objRecordSet = objCmd.Execute
objRecordSet.MoveFirst
Resultstring = Resultstring & vbcrlf & "Name: " & objRecordset.fields("DisplayName")
Msgbox Resultstring
Resultstring = ""
Set objCmd = Nothing
Set objRootDSE = Nothing
Set objRecordSet = Nothing
Set objWMI = Nothing
Set Items = Nothing
End Sub

Return volume name in Do loop

I'm trying to create a script for HP UFT to automatically create/mount/unmount VHDs as needed. The problem is that diskpart takes quite a while to create, mount, format and label the VHD. I need a method to pause the script while the VHD is being created/mounted, after which it will continue on with the script.
The variable uftDrive is currently returning a drive letter, not a volume name, so the loop just runs indefinitely. Any thoughts as to how to pass the volume names as a variable? The VHD script is automatically assigning the first-available drive letter to the drive, as we have multiple machines that UFT will be run on, and they don't have identical network drive mappings, forcing us to dynamically detect the drive by volume name.
'===========================================================================
'This will check to see if the Virtual Hard Disk (VHD) exists, and if not,
'create it
'===========================================================================
Dim makevhdExists, vhdExists
Set makevhdExists = CreateObject("Scripting.FileSystemObject")
Set vhdExists = CreateObject("Scripting.FileSystemObject")
If Not makevhdExists.FileExists("c:\UFT\mountVHD.bat") Then
makevhdExists.CopyFile "\\companyADfolders\Users\UFT\VHD\*.*", "c:\UFT\"
End If
If Not makevhdExists.FileExists("c:\UFT\unmountVHD.bat") Then
wait 2
ElseIf Not vhdExists.FileExists("C:\UFT\UFT.vhd") Then
SystemUtil.Run "cmd","/c""C:\UFT\createVHD.bat"""
End If
Dim uftExists, uftDrive
uftExists = "False"
Do
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_LogicalDisk Where VolumeName = 'UFT'")
For Each objItem in colItems
uftDrive = objItem.Name 'This is currently returning a drive letter,
'not a volume name
If uftDrive.Name = "UFT" Then
uftExists = "True"**
End If
Next
Loop Until uftExists = "True"
For the wait part
WScript.Sleep 5000
where the value indicated is milliseconds
For the second part, maybe i'm missing something, so, instead of an answer I have one silly question:
If you query wmi for win32_logicaldisk instances where their VolumeName is UFT, WHY your if command checks the Name property instead of the VolumeName property?
In any case, you don't need that if. If colItems.Count is greater than 0, there is at least one instance that matches the indicated condition
Something like this should work
Dim uftExists
uftExists = False
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Do
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_LogicalDisk Where VolumeName = 'UFT'")
If colItems.Count > 0 Then
utfExists = True
Else
Sleep 500
End If
Loop Until uftExists
Changing ObjItem.Name to ObjItem.VolumeName worked!
For Each objItem in colItems
uftDrive = objItem.VolumeName 'This is currently returning a drive letter, not a volume name
If uftDrive = "UFT" Then
uftExists = "True"**
End If
Next
For the first question: To delay the script, you can use this function:
WScript.Sleep 1000
The number is in Milliseconds, so 1000 is 1 second.

Query to Select All Non-Essential Temporary Files

I am trying to write a script to delete unneeded temporary files. I am wanting to specifically target .tmp's, though. At least for now. So I am trying to write a WQL query to return a collection with which I can use a FOR EACH statement to delete all of the .tmp's in C:\Users\\AppData\Local\Temp. I've only recently started learning VBScript. But I have experience writing programs in C/C++ (mainly "math-y" programs).
Cscript seems to have no problem with the query itself. But when I try to use the Count method on the resulting collection, cscript returns an error: (17,1) Microsoft VBVScript runtime error: Object doesn't support this property or method: 'colTempFiles.Count'.
I've read up on WQL a little bit, thinking that maybe I'm not getting a collection returned for some reason. But I can't seem to find anything wrong with the query. I'm thinking that maybe I shouldn't be selecting from FileSystemObject. But I've read what I can find about it, and it seems to be the right thing to do (although there really isn't a lot of helpful info on MSDN).
Anyway, here's the script I currently have, without comments. The second line is something I am not currently using, but am going to try to use later, so that I can define a variable as the local computer's username and not have to point to the local Temp folder's path specifically. Any help would be greatly appreciated:
strComputer = "."
strUser="adam"
Set objFSO=CreateObject("Scripting.FileSystemObject")
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colTempFiles = objWMIService.ExecQuery _
("SELECT * FROM FileSystemObject WHERE Name = '*.tmp' AND "_
& "NOT Name LIKE 'Prf%' AND Path LIKE 'C:\Users\adam\AppData\Local\Temp\%'")
colTempFiles.Count
For Each objFile in colTempFiles
Wscript.Echo objFile.Name
'Set objF=objFSO.GetFile("objFile.Path")
'objF.Delete(True)
Next
I think you're confusing two different technologies. A FileSystemObject is a COM class that needs to be instantiated using CreateObject() in VBScript. For WQL, you need to use a WMI class in your query. Here is a core list of WMI classes. For your purposes, you'll want to use the CIM_DataFile class to work with files.
You can use either technology. The FileSystemObject is the preferred method if you're working with the local file system. If you need to work with files on a remote machine, use WMI and WQL.
Here's an example using a FileSystemObject:
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder("C:\Users\adam\AppData\Local\Temp")
For Each objFile In objFolder.Files
If StrComp(objFSO.GetExtensionName(objFile.Path), "tmp", vbTextCompare) = 0 Then
objFile.Delete ' This is the Delete() method of the FSO's "File" class
End If
Next
And here's an example using WQL:
strComputer = "."
' Connect to the WMI service on the specified computer...
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
' Build our WQL query...
strQuery = "select * from CIM_DataFile "
strQuery = strQuery & "where Drive='C:' "
strQuery = strQuery & "and Path='\\Users\\adam\\AppData\\Local\\Temp\\' "
strQuery = strQuery & "and Name like '%.tmp'"
' Run the query...
Set colTempFiles = objWMIService.ExecQuery(strQuery)
' Delete each file...
For Each objFile In colTempFiles
objFile.Delete ' This is the Delete() method of the WMI "CIM_DataFile" class
Next

Does VBScript Support Introspection for Objects?

I am pulling results back from WMI using WQL via VBScript.
In examples, a For Each loop is used to iterate over the results, but in each example, it is assumed that the property names are known. Case in point:
Set colInstalledPrinters = objWMIService.ExecQuery ("Select * from Win32_Printer Where Default = True")
For Each objPrinter in colInstalledPrinters
Wscript.Echo objPrinter.Name
Next
Some of the WMI classes have a very long list of properties associated with them. As an additional complication, some properties cannot be expected to be present (according to various webpages I have read about WMI). Rather than researching each WMI class and hoping that the properties listed are present, I would like to obtain a list of the properties (or columns, if I am thinking in SQL/WQL) present for, say, an objPrinter or any other returned item.
Python is my usual language but I cannot install it on the target machines in this instance; I can perform remote querying of WMI via Python but I am trying to trigger on an local event, hence falling back to VBScript. Although I gather Powershell might be able to do this, I would rather not learn it just this instant.
So, does VBScript support that level of introspection which would allow me to enumerate a list of properties? Or is there something I can do involving a schema I can reference and examine in-script?
Use the .Properties_ collection of the item:
Option Explicit
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20
Dim objWMIService
Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2")
Dim colItems
Set colItems = objWMIService.ExecQuery( _
"SELECT * FROM Win32_Printer" _
, "WQL" _
, wbemFlagReturnImmediately + wbemFlagForwardOnly _
)
Dim objItem
For Each objItem In colItems
Dim oProp
For Each oProp In objItem.Properties_
WScript.Echo oProp.Name, TypeName( oProp.Value ), ToString( oProp.Value )
Next
WScript.Echo
Next
Function ToString( vX )
ToString = "!! work to do !!"
On Error Resume Next
ToString = CStr( vX )
On Error GoTo 0
End Function
Output:
...
MimeTypesSupported Null !! work to do !!
Name String Auto HP LaserJet 5 on WINXP2
NaturalLanguagesSupported Null !! work to do !!
Network Boolean False
PaperSizesSupported Variant() !! work to do !!
...
Obviously, the ToString() function needs further work.

Mount/Unmount USB drives with VBSCript

I am looking for a way to mount\unmount a USB flash drive using VBScript. This is the closest I was able to get.
Sub EjectDrive(strDrive)
On Error Resume Next
CONST SSF_DRIVES = 17
Set objShell = CreateObject("Shell.Application")
Set objDrive = objShell.Namespace(SSF_DRIVES).ParseName(strDrive)
objDrive.InvokeVerb "E&ject"
End Sub
This will work on Windows Server 2003, but not NT/2000/XP/Vista unfortunately.
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_Volume Where Name = 'E:\\'")
For Each objItem in colItems
objItem.Dismount(True, True)
Next
From Dismount a Volume.
Take a look at this thread, its talks about using the mountvol.exe command line tool to mount/unmount a drive, and it should work for USB flash drives, or there is also a program called deveject. Se this thread for more info: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d2e5d16e-e7c9-48ef-88b8-3abf6e638384
You can popup the eject dialog by using something like this. I am not sure if it possible to unmount a specific device.
Set WshShell = WScript.CreateObject("WScript.Shell")
intReturn = WshShell.Run("RunDll32.exe shell32.dll,Control_RunDLL hotplug.dll", 1, TRUE)

Resources