Starting a process in VBS: path not found - vbscript

I need to make a simple vbs script to run some process' automatically. I found the following script on microsoft's website. It works fine to run notepad.exe the way the original example shows, but I'm trying to modify it to run myprog.exe. The full path to this program is: C:\myprogdir\myprog.exe
Const SW_NORMAL = 1
strComputer = "."
strCommand = "myprog.exe"
strPath = "C:\myprogdir\"
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
' Configure the Notepad process to show a window
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = SW_NORMAL
' Create Notepad process
Set objProcess = objWMIService.Get("Win32_Process")
intReturn = objProcess.Create _
(strCommand, strPath, objConfig, intProcessID)
If intReturn <> 0 Then
Wscript.Echo "Process could not be created." & _
vbNewLine & "Command line: " & strCommand & _
vbNewLine & "Return value: " & intReturn
Else
Wscript.Echo "Process created." & _
vbNewLine & "Command line: " & strCommand & _
vbNewLine & "Process ID: " & intProcessID
End If
I keep getting Return value: 9, which indicates "Path Not Found". However the path is correct. Is there something I'm not getting?

You don't need all that to start a process, you just need the Shell object. Also, be sure to wrap the path of your executable in quotes (in case the path has spaces). Like this:
Option Explicit
Dim shl
Set shl = CreateObject("Wscript.Shell")
Call shl.Run("""C:\myprogdir\myprog.exe""")
Set shl = Nothing
WScript.Quit

Unless the path to your program is included in the system's %PATH% environment variable you need to specify the commandline with the full path to the executable. Specifying the path just as the working directory will not work.
strProgram = "myprog.exe"
strPath = "C:\myprogdir"
Set fso = CreateObject("Scripting.FileSystemObject")
strCommand = fso.BuildPath(strPath, strProgram)
...
intReturn = objProcess.Create(strCommand, strPath, objConfig, intProcessID)
Using the BuildPath method will save you the headaches caused by having to keep track of leading/trailing backslashes.
Note that you need to put double quotes around a path that contains spaces, e.g. like this:
strCommand = Chr(34) & fso.BuildPath(strPath, strProgram) & Chr(34)
As others have already pointed out, there are simpler ways to start a process on the local computer, like Run:
Set sh = CreateObject("WScript.Shell")
sh.Run strCommand, 1, True
or ShellExecute:
Set app = CreateObject("Shell.Application")
app.ShellExecute strCommand, , strPath, , 1
There are some notable differences between Run and ShellExecute, though. The former can be run either synchronously or asynchronously (which means the command either does or doesn't wait for the external program to terminate). The latter OTOH always runs asynchronously (i.e. the method returns immediately without waiting for the external program to terminate), but has the advantage that it can be used to launch programs with elevated privileges when UAC is enabled by specifying the verb "runas" as the 4th argument.
However, these methods only allow for launching processes on the local computer. If you want to be able to launch processes on remote computers you will have to use WMI:
strComputer = "otherhost"
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" _
& strComputer & "\root\cimv2")
See here for more information about WMI connections to remote hosts.

Related

Running a VBS script elevated to get remote computer serial number

Ok, I have an error someplace in here, but not sure where. I am NOT a coder by any means, this is something I have put together from a couple of different sources. This code works, however it seems to run once as a normal user and once at elevated permissions... I just need it to run just once at elevated permissions.
Set WshShell = WScript.CreateObject("WScript.Shell")
If WScript.Arguments.length = 0 Then
Set ObjShell = CreateObject("Shell.Application")
ObjShell.ShellExecute "wscript.exe", """" & _
WScript.ScriptFullName & """" &_
" RunAsAdministrator", , "runas", 1
End if
On Error Resume Next
Dim System
if Wscript.Arguments.Count >0 then
sSystem=Wscript.Arguments(0)
end if
ComputerName = InputBox("Enter the name of the computer you wish to query")
winmgmt1 = "winmgmts:{impersonationLevel=impersonate}!//"& ComputerName &""
Set SNSet = GetObject( winmgmt1 ).InstancesOf ("Win32_BIOS")
for each SN in SNSet
MsgBox "The serial number for the specified computer is: " & SN.SerialNumber
next
This is the part that re-runs your script with elevated privileges by using the Shell.ShellExecute method with the "runas" verb:
If WScript.Arguments.length = 0 Then
Set ObjShell = CreateObject("Shell.Application")
ObjShell.ShellExecute "wscript.exe", """" & _
WScript.ScriptFullName & """" &_
" RunAsAdministrator", , "runas", 1
End if
Re-running the script with the additional parameter RunAsAdministrator makes sure that the re-run script skips the above part (since WScript.Arguments.Length is greater than 0 due to that parameter) and goes directly to the "worker" code.
However, the above code snippet doesn't exit after re-running the script, so both the elevated and the original invocation are executing the worker code.
Add a WScript.Quit statement to your code to make the original invocation exit right after re-running itself with elevated permissions and the issue will disappear:
If WScript.Arguments.Length = 0 Then
Set ObjShell = CreateObject("Shell.Application")
ObjShell.ShellExecute "wscript.exe", _
"""" & WScript.ScriptFullName & """ RunAsAdministrator", , "runas", 1
WScript.Quit 0
End If
that's all (for remote computer):
ComputerName = InputBox("Enter the name of the computer you wish to query")
winmgmt1 = "winmgmts:(impersonationLevel=impersonate}!//"& ComputerName &"\root\cimv2")
Set SNSet = winmgmt1.ExecQuery("Select * from Win32_BIOS")
for each SN in SNSet
MsgBox "The serial number for the specified computer is: " & SN.SerialNumber
next

Output Vbscript info to txt file

Newbie question - I have here a VBScript that looks for an Upgrade Code, and based on that finds the Product Codes for the specified Upgrade Code. The Upgrade Code is always the same, but Product Code changes from version to version, and that can make uninstalling software troublesome. And no, I didn't make this script myself.
This script works very well, but I'd like to make it output all the product codes it found to a text file. I've looked on Google for hours, found some clues, but I've not been able to make it work. Always turns up with a blank text file.
Here's the code:
strComputer = "."
Set WshShell = CreateObject("Wscript.Shell")
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
On Error Resume Next
Set colSoftware = objWMIService.ExecQuery _
("Select * from Win32_Property Where Property = 'UpgradeCode'")
For Each objSoftware in colSoftware
If objSoftware.Value = "{BCCCB25E-C6A6-4340-9018-DA0FB34AF226}" Then
strCMD = "MsiExec.exe /x " & objSoftware.ProductCode & " /qn"
objExec = WshShell.Run(strCMD,1,True)
If objExec <> 0 Then
WScript.Quit objExec
End If
End If
Next
WScript.Quit 0
How do I output objSoftware.ProductCode to a text file? Or do I need to output something else to get the Product Codes I'm looking for?
The easy way to write text to a file is to WScript.Echo the desired info and run the script like cscript x.vbs > output.txt.
If that seems to pedestrian, start your research here.
Played around with it, Googled it, and I found a solution that works for me. This prints out all the Product Codes on your computer based on the specificed Upgrade Code. Here's the script:
strComputer = "."
Set WshShell = CreateObject("Wscript.Shell")
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
On Error Resume Next
Set colSoftware = objWMIService.ExecQuery _
("Select * from Win32_Property Where Property = 'UpgradeCode'")
For Each objSoftware in colSoftware
If objSoftware.Value = "{BCCCB25E-C6A6-4340-9018-DA0FB34AF226}" Then
Wscript.Echo objSoftware.ProductCode
strCMD = "MsiExec.exe /x " & objSoftware.ProductCode & " /qn"
objExec = WshShell.Run(strCMD,1,True)
If objExec <> 0 Then
WScript.Quit objExec
End If
End If
Next
WScript.Quit 0
Running //NoLogo scriptname.vbs > log.txt in command prompt, gives me a txt file with all the product codes for the upgrade code specified.
Please note this code also uninstalls the software afterwards.

VBScript errors with GetObject call

The goal is to retrieve the Dell service tags of all systems in a list (pseudo-systems given below in place of real system names)
The following script was originally used, and works just fine.
On Error Resume Next
strComputer=InputBox ("Enter the computer name of the server you'd like to query for Service Tag")
Set objWMIservice = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
WScript.Echo "Error: " & Err.number
Set colitems = objWMIservice.ExecQuery("Select SerialNumber from Win32_BIOS", , 48)
For Each objitem In colitems
WScript.Echo "Dell Service Tag: " & objitem.SerialNumber
Next
It asks for the user to input the system name, and then retrieves the tag.
However, there are 200+ systems to run, and it'd be nice to avoid having to type them all in manually.
My attempt to do just that (below) is close to right, but fails with error 70 codes on systems that the first script finds just fine.
On Error Resume Next
Dim systems, splitSystems, objWMIservice, fso, output, tag, mystr
systems = "sys1,sys2,sys3,sys4"
splitSystems = Split(systems,",")
Set fso = CreateObject("Scripting.FileSystemObject")
Set output = fso.CreateTextFile("system_tags.csv", True)
output.WriteLine """System Name"",""Service Tag"""
For Each sys In splitSystems
If Ping(sys) = True Then
'Doesn't work
mystr = "winmgmts:\\" & sys & "\root\cimv2"
Set objWMIservice = GetObject(mystr)
'Also doesn't work
'Set objWMIservice = GetObject("winmgmts:\\" & sys & "\root\cimv2")
'Works just dandy
'Set objWMIservice = GetObject("winmgmts:\\sys1\root\cimv2")
If Err.number <> 0 Then
'output.WriteLine """" & sys & """,""ERROR """ & Err.number
WScript.Echo "Set objWMIservice = GetObject('" & mystr & "') failed from Err.code:" & Err.description
Else
For Each objitem In objWMIservice.ExecQuery("Select SerialNumber from Win32_BIOS",,48)
tag = objitem.SerialNumber
Next
output.WriteLine """" & sys & """,""" & tag & """"
End If
Else
output.WriteLine """" & sys & """,""OFFLINE"""
End If
Next
MsgBox "All done!"
Function Ping(strComputer)
Dim objShell, boolCode
Set objShell = CreateObject("WScript.Shell")
boolCode = objShell.Run("Ping -n 1 -w 300 " & strComputer, 0, True)
If boolCode = 0 Then
Ping = True
Else
Ping = False
End If
End Function
Can someone explain to me why the first two methods (commented "Doesn't work" and "Also doesn't work") fail, but hardcoding the system name works just fine?
EDIT: The lines in the latter script following the condition If Err.number <> 0 Then were updated to provide the error description. Output is as follows, with system names replaced with pseudonyms:
Set objWMIservice = GetObject('winmgmts:\\sys1\root\cimv2') failed from Err.code:Permission denied
Set objWMIservice = GetObject('winmgmts:\\sys3\root\cimv2') failed from Err.code:Permission denied
EDIT2: Further testing shows that it at least one issue is related to successfully finding the service tag of one system, and failing on the following system, which for some reason results in the previous tag being used

Start Service with VBscript

I am trying to have this script take a text file running and stopped services before a reboot and start any services that did not automatically start after the machine starts back up. The script that gets the list of service names, state and startmode and creates a comma separated text file line by line works fine. Here it is for reference (taken from the interwebs, lost the link in my travels. Modified slightly.):
Const ForAppending = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objLogFile = objFSO.CreateTextFile("service_list.txt", _
ForWriting, True)
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colListOfServices = objWMIService.ExecQuery("Select * from Win32_Service")
For Each objService in colListOfServices
objLogFile.Write objService.Name & ","
objLogFile.Write objService.StartMode & ","
objLogFile.Write objService.State
objLogFile.Writeline
Next
objLogFile.Close
This next bit reads the file line by line, compares the state of all of the services with the state of the services that were recorded before the machine was shut down. If they match, do nothing, if they are different, start the service:
Const ForReading = 1
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objServiceName = objWMIService.get("Win32_Service.Name='" & ServiceName & "'")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("\\some path\service_list.txt",ForReading,True,-2)
Do Until objFile.AtEndOfStream
fLine = Split(objFile.ReadLine,",")
'wscript.echo fLine(2)
if InStr(fLine(2),"Running") then
'wscript.echo "it was running!"
if objServiceName.Started then
'do nothing
else
'Set servicetostart = objWMIService.ExecQuery ("Select " & ServiceName & " from Win32_Service Where Name ='Alerter'")
'servicetostart.StartService()
'Result = objServiceName.StartService
'If 0 <> Result Then
' wscript.echo "Start " & ServiceName & " error:" & Result
'End If
objServiceName.StartService
'wscript.echo Servicename & "could not start with error: " & Result
end if
end if
'wscript.echo objServiceName
Loop
As of right now I am recieving an error whenever it actually tries to start the service. I receive a "Provider Failure code:80041004 Source:SWbemObjectEX". I have been looking through the posts about this error and attempting the fixes suggested. Also, as you can see, I have been trying variations, but I am afraid I am merely guessing.
So to my question, what is causing the "Provider Failure"? I have looked up these information for the Win32_Service Class here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394418%28v=vs.85%29.aspx#methods
and looked up the method here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393660%28v=vs.85%29.aspx
But have been unable to work out where the I am going wrong.
Thanks,
Joe
on a side note, the service I am testing, ie. making sure the service is starting, creating the text file, then stopping the service and running the "start service" code is Windows Defender. The service name is "WinDefend".
FINAL WORKING CODE:
Const ForReading = 1
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("\\vmware-host\Shared Folders\Documents\Biffduncan\Monthly Server Maintanence\service_list.txt",ForReading,True,-2)
Do Until objFile.AtEndOfStream
fLine = Split(objFile.ReadLine,",")
Set objService = objWMIService.get("Win32_Service.Name='" & fLine(0) & "'")
if InStr(fLine(2),"Running") then
'wscript.echo "it was running!"
if objService.Started then
'do nothing
else
Result = objService.StartService()
if Result <> 0 then
wscript.echo "The service: " & objService.Name & " did not start with error: " & Result
else
wscript.echo "Service " & objService.Name & " started"
end if
end if
end if
Loop
Error code 0x80041004 means that the WMI provider encountered an error after it was already initialized. The error code doesn't say anything about the cause of the error, though, nor does it provide any details. Try running WBEMTest or WMIDiag to track down the error. Also check the eventlog for related errors/warnings. If everything else fails, try rebuilding the WMI repository.
As for your code, the first thing I'd do is strip it down to the bare minimum, to avoid potential error sources:
Set wmi = GetObject("winmgmts://./root/cimv2")
Set svc = wmi.Get("Win32_Service.Name='WinDefend'")
rc = svc.StartService
WScript.Echo rc
Also, I wouldn't recommend writing the service status to a file at some random point in time, and then try starting services according to the contents of that file. There is no guarantee that the start mode hasn't been changed since the file was created, or that the service is even installed anymore.
Whether or not a service should be started is indicated by its StartMode property, so just check those services that are set to Auto. Services set to Manual will be started by the system on demand, so there's no need to launch them just because they were running when you took the snapshot.
qry = "SELECT * FROM Win32_Service WHERE StartMode='Auto'"
For Each svc In wmi.ExecQuery(qry)
If Not svc.Started Then svc.StartService
Next

Can I pick up environment variables in vbscript WSH script?

Is is possible to read system environment variables in a Windows Scripting Host (WSH) VBS script?
(I am writing a VBScript using Windows Scripting Host for task for a Cruise Control and want to pick up the project build URL.)
Here's an example (taken from here):
Set oShell = CreateObject( "WScript.Shell" )
user=oShell.ExpandEnvironmentStrings("%UserName%")
comp=oShell.ExpandEnvironmentStrings("%ComputerName%")
WScript.Echo user & " " & comp
The existing answers are all helpful, but let me attempt a pragmatic summary:
Typically, you want the current process's definition of an environment variable:
CreateObject("WScript.Shell").ExpandEnvironmentStrings("%TEMP%")
This is the equivalent of (note the absence of % around the variable name):
CreateObject("WScript.Shell").Environment("Process").Item("TEMP")
Caveat: Do not omit the ("Process) part: if you do, you'll get the system scope's definition of the variable; see below.
.ExpandEnvironmentStrings is conceptually simpler and more flexible: It can expand arbitrary strings with embedded (%-enclosed) environment-variable references; e.g.:
CreateObject("WScript.Shell").ExpandEnvironmentStrings("My name is %USERNAME%")
On rare occasions you may have to access environment-variable definitions from a specific scope (other than the current process's).
sScope = "System" ' May be: "Process", "User", "Volatile", "System"
CreateObject("WScript.Shell").Environment(sScope).Item("TEMP")
Note: As stated above, omitting the scope argument defaults to the System scope.
Caveat: Accessing a value this way does not expand it: Environment-variable values can be nested: they can refer to other environment variables.
In the example above, the return value is %SystemRoot%\TEMP, which contains the unexpanded reference to %SystemRoot%.
To expand the result, pass it to .ExpandEnvironmentStrings(), as demonstrated above.
From here ...
Set WshShell = WScript.CreateObject("WScript.Shell")
Set WshProccessEnv = WshShell.Environment("Process")
Set WshSysEnv = WshShell.Environment("System")
Wscript.Echo WshSysEnv("NUMBER_OF_PROCESSORS")
Wscript.Echo WshProccessEnv("Path")
Also, much more detail on TechNet.
Set WshShell = CreateObject("WScript.Shell")
Set WshEnv = WshShell.Environment
WScript.Echo "WINDIR=" & WshEnv.Item("WINDIR") & vbCrLf & vbCrLf
Set WshShell = CreateObject("WScript.Shell")
WScript.Echo "Environment System:" & vbCrLf & _
"..............................................."
For Each IEnv In WshShell.Environment("System")
WScript.Echo IEnv
Next
WScript.Echo vbCrLf & "Environment User:" & vbCrLf & _
"..............................................."
For Each IEnv In WshShell.Environment("User")
WScript.Echo IEnv
Next
WScript.Echo vbCrLf & "Environment Volatile:" & vbCrLf & _
"..............................................."
For Each IEnv In WshShell.Environment("Volatile")
WScript.Echo IEnv
Next
WScript.Echo vbCrLf & "Environment Process:" & vbCrLf & _
"..............................................."
For Each IEnv In WshShell.Environment("Process")
WScript.Echo IEnv
Next
This works for me:
Dim objNetwork
Set objNetwork = CreateObject("WScript.Network")
MsgBox objNetwork.UserName
or from the shell:
Set wshShell = WScript.CreateObject( "WScript.Shell" )
strUserName = wshShell.ExpandEnvironmentStrings( "%USERNAME%" )
or from environment variable (it should work, but when i tested it was wrong!):
Set WshShell = CreateObject("WScript.Shell")
Set WshEnv = WshShell.Environment
MsgBox "USERNAME=" & WshEnv.Item("USERNAME")

Resources