VBScript not working properly when scheduled - vbscript

I have a VBScript to back up several registry keys into a single file:
Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
arrRegPaths = Array( _
"HKEY_CURRENT_USER\RegKey1\", _
"HKEY_CURRENT_USER\RegKey2\", _
"HKEY_CURRENT_USER\RegKey3\" _
)
Const intForReading = 1
Const intUnicode = -1
strFileName = objShell.ExpandEnvironmentStrings("%UserProfile%") & "\FolderName\FileName.reg"
Set objRegFile = objFSO.CreateTextFile(strFileName, True, True)
objRegFile.WriteLine "Windows Registry Editor Version 5.00"
For Each strRegPath In arrRegPaths
strCommand = "cmd /c REG EXPORT " & strRegPath & " " & Replace(strRegPath, "\", "_") & ".reg"
objShell.Run strCommand, 0, True
If objFSO.FileExists(Replace(strRegPath, "\", "_") & ".reg") = True Then
Set objInputFile = objFSO.OpenTextFile(Replace(strRegPath, "\", "_") & ".reg", intForReading, False, intUnicode)
If Not objInputFile.AtEndOfStream Then
objInputFile.SkipLine
objRegFile.Write objInputFile.ReadAll
End If
objInputFile.Close
Set objInputFile = Nothing
objFSO.DeleteFile Replace(strRegPath, "\", "_") & ".reg", True
End If
Next
objRegFile.Close
Set objRegFile = Nothing
I can run it directly from Explorer (Win10), directly from CMD, or with WScript/CScript, and it works perfectly. Takes about 2 seconds to complete, and the output file has everything it's supposed to have.
But when I try to run it as a scheduled task, it never finishes running. I've let it sit for several minutes, before manually killing it. When I look at the output file after this, all it has is the first line, Windows Registry Editor Version 5.00. So I know the script is finding the target file OK, and it's running the first WriteLine. But after that, it hangs or something.
How can I make it work as a scheduled task?

Haven't tried your code, but you send a commandline command from within your script. That seems counterintuitive to me. You are probably better off by tapping into the registry directly from your script. You can find examples of how to do that here: https://learn.microsoft.com/en-us/windows/desktop/WmiSdk/wmi-tasks--registry

Got it figured out, and it actually had little to do with the code. I'll post the answer in case anyone else runs across something similar. It turns out that the problem was in the scheduled task itself—I needed to put a folder in the "Start in" box (when specifying the action to be scheduled). I assume this is because the script outputs to temp files along the way, so it needed a place to put those. Anyway, once I filled in that blank, it began running successfully.

Related

How to redirect output from EXE to TXT file using VBScript?

The requirement is to execute a certain script on multiple workstations using a tool such as Microsoft SCCM.
This script is required to execute the EXE 'C:\ugs\nx5\UGII\env_print.exe' on every workstation. This is to be done twice using the following parameters :
C:\ugs\nx5\UGII\env_print.exe -m
C:\ugs\nx5\UGII\env_print.exe -n
The script must be designed such that the output from the above mentioned should be stored at someplace on the workstation, from where SCCM could read the values.
To achieve this requirement, I wrote the following VBscript :
--------------------------------------------------------------------------------------------
On Error Resume Next
Const HKEY_LOCAL_MACHINE = &H80000002
Dim WshShell, fso, file, objRegistry, strKeyPath, strSysDrive, outputFile, strTEMP, file2, oTxtFile, oTxtFile2
Dim ComExec, strSysRoot, strComputer, outputFile2, EXEpath, ComExec2, return, return2, text, text2, CMDPath
strComputer = "."
Set WshShell = Wscript.CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
strSysDrive = WshShell.ExpandEnvironmentStrings("%SystemDrive%")
strSysRoot = WshShell.ExpandEnvironmentStrings("%SystemRoot%")
EXEpath = strSysDrive & "\ugs\nx5\UGII\env_print.exe"
CMDPath = strSysRoot & "\system32\cmd.exe"
'-----------------------SET TXT FILE LOCATION-----------------------
outputFile = strSysDrive & "\env_print_m.txt"
outputFile2 = strSysDrive & "\env_print_n.txt"
'-----------------------CREATE TEXT FILES-----------------------
Set oTxtFile = fso.CreateTextFile(outputFile)
Set oTxtFile2 = fso.CreateTextFile(outputFile2)
'-------COMMAND TO EXECUTE AND REDIRECT OUTPUT TO TXT FILE-------
ComExec = CMDPath & " /c " & EXEpath & " -m >> " & outputFile
ComExec2 = CMDPath & " /c " & EXEpath & " -n >> " & outputFile2
'-----------------------EXEUTE COMMANDS-----------------------
return = WshShell.Run(ComExec, 0, true)
return2 = WshShell.Run(ComExec2, 0, true)
'-----------------------READ OUTPUT FROM TXT FILES-----------------------
Set file = fso.OpenTextFile(outputFile, 1)
text = file.ReadAll
file.Close
Set file2 = fso.OpenTextFile(outputFile2, 1)
text2 = file2.ReadAll
file.Close
'-----------------------WRITE OUTPUT VALUES TO REGISTRY STRING VALUES-----------------------
strKeyPath = "SOFTWARE\env_print_Ver"
objRegistry.CreateKey HKEY_LOCAL_MACHINE, strKeyPath
WshShell.RegWrite "HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_m", text, "REG_SZ"
WshShell.RegWrite "HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_n", text2, "REG_SZ"
'-----------------------DELETE TXT FILES-----------------------
fso.DeleteFile outputFile
fso.DeleteFile outputFile2
--------------------------------------------------------------------------------------------
This script executes the EXE with the required parameters and stores the output to 2 different TXT files(env_print_m.txt and env_print_n.txt).
Then reads these string values from the text files and stores them as registry string values at the following locations, so that it could be read by SCCM.
HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_m
HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_n
However, when this script is executed on workstations running Windows XP, the outputs aren't redirected to the TXT files. No errors are displayed either.
I am at my wits end. Please help.
As your first output file is not named/specified outputFile, change
Set file = fso.OpenTextFile("outputFile", 1)
to
Set file = fso.OpenTextFile(outputFile, 1)
(Same for the second one)
Trying to access a file wrongly named should abort your script with an error message. You switched of this feature by an EVIL global "On Error Resume Next". Get rid of it and see if "outputs aren't redirected' is explained by an error message.
Added wrt comment:
If something like
WshShell.Run ComExec, 0, true
'does not work' you should:
call .Run as a function and check the return value
echo the command (ComExec) and try to execute exactly this command from a console
switch /c to /k and look
sacrify a goat and think about permissions
Oh, I forgot:
manually delete the output files and check if they are created but get no content - then reconder the .exe

Run script in background

I want to run following script as scheduled task on Windows 7 in background. Now, script displays cmd window and, can I run script without visible cmd window?
Option Explicit
Dim WshShell, oExec
Dim RegexParse
Dim hasError : hasError = 0
Set WshShell = WScript.CreateObject("WScript.Shell")
Set RegexParse = New RegExp
Set oExec = WshShell.Exec("%comspec% /c echo list volume | diskpart.exe")
RegexParse.Pattern = "\s\s(Volume\s\d)\s+([A-Z])\s+(.*)\s\s(NTFS|FAT)\s+(Mirror|RAID-5)\s+(\d+)\s+(..)\s\s([A-Za-z]*\s?[A-Za-z]*)(\s\s)*.*"
While Not oExec.StdOut.AtEndOfStream
Dim regexMatches
Dim Volume, Drive, Description, Redundancy, RaidStatus
Dim CurrentLine : CurrentLine = oExec.StdOut.ReadLine
Set regexMatches = RegexParse.Execute(CurrentLine)
If (regexMatches.Count > 0) Then
Dim match
Set match = regexMatches(0)
If match.SubMatches.Count >= 8 Then
Volume = match.SubMatches(0)
Drive = match.SubMatches(1)
Description = Trim(match.SubMatches(2))
Redundancy = match.SubMatches(4)
RaidStatus = Trim(match.SubMatches(7))
End If
If RaidStatus <> "Healthy" Then
hasError = 1
'WScript.StdOut.Write "WARNING "
MsgBox "Status of " & Redundancy & " " & Drive & ": (" & Description & ") is """ & RaidStatus & """", 16, "RAID error"
End If
End If
Wend
WScript.Quit(hasError)
Thanks a lot
Option 1 - If the task is running under your user credentials (if not, msgbox will not be visible)
There are two possible sources for the cmd window.
a) The script itself. If the task is executing cscript, the console window will be visible, avoid it calling wscript instead
b) The Shell.exec call. The only way to hide this window is to start the calling script hidden. On start of your script test for the presence of certain argument. If not present, make the script call itself with the argument, using Run method of the WshShell object, and indicating to run the script with hidden window. Second instance of the script will start with the special parameter, so it will run, but this time windows will be hidden.
Option 2 - Running the task under system credentials.
In this case, no window will be visible. All will be running in a separate session. BUT msgbox will not be seen. Change MsgBox call with a call to msg.exe and send a message to current console user.

For each loop syntax error

There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor
Hey I wrote this script delete shares of a computer but when I run my script it repeats the same wscript.echo statating the share being deleted. Why does my code never end when run How do I fix that.
My fumction:
'The function that is called to run the command Line that deletes a specific share from a pc
Function DeleteThisShare(Share)
Dim objShell
'Logging The deleted Approved Shares
objDeletedFile.WriteLine (Now & " - Removed share " & Trim(Share))
DeleteThisShare = "net share " & chr(34) & Share & chr(34) &" /DELETE"
Wscript.echo DeleteThisShare
Set objShell = CreateObject("Wscript.Shell")
objShell.Run DeleteThisShare
End Function
My loops:
'Compares The UnApproved Shares to the Current Shares
For Each objItem In colItems
Dim StrNonUnapprovedShares, Item
StrCurrentShares = objItem.name
if instr(AdminShares,lcase(objitem.name)) > 0 or mid(objitem.name,2,1) = "$" or left(lcase(objitem.name),10) = "pkgsvrhost" then
'Skipping known admin share
Else
For each Item in arrUnApprovedLines
If Lcase(Item) = Lcase(strCurrentShares) Then
StrNonUnapprovedShares = (StrNonUnapprovedShares & strCurrentShares & vbCrLf)
End If
Next
End If
Next
Dim notUnapprovedShares, notUnapprovedLines
notUnapprovedLines = StrNonUnapprovedShares
notUnapprovedLines = Split(notUnapprovedLines, vbCrLf)
Dim y, Line2
For y = 0 to uBound(notUnapprovedLines)
Line2 = Trim(notUnapprovedLines(y))
If len(Line2) > 0 Then
DeleteThisShare(Line2)
End If
Next
I think the problem is caused by using the function name as a variable. That's okay with VB that you're compiling, but I don't think VBScript recognizes it in the same way. Use a separate variable name in place of DeleteThisShare, e.g. strDeleteThisShare.
If I had to guess it's because you're creating a recursive loop by having your script echo the DeleteThisShare function. The function gets to that line and is called again before it's able to carry on.
Try to only assign values to the result of the function and use local variables to store any other debugging / temporary values.

vbScript to execute all files in a dir

I'm trying to write a vbScript that will execute all files in a given directory (will be mostly batch files).
I've tried to modify a script that deletes all files but I'm not able to get it to work.
Here is what I have:
Option Explicit
'===========================================================================
' Scheduled Task - Visual Basic ActiveX Script
'===========================================================================
Call ExecuteDirectory("c:\users\public\documents\schedule\daily")
Function ExecuteDirectory(strPath2Folder)
Dim fso, f, fc, f1, strFiles, intFiles
Dim WshShell
Set WshShell = CreateObject("WScript.Shell")
strFiles = ""
Set fso = CreateObject("Scripting.FileSystemObject")
If (fso.FolderExists(strPath2Folder)) Then
Set f = fso.GetFolder(strPath2Folder)
Set fc = f.Files
'-- Execute each file in Folder
For Each f1 in fc
strFiles = strFiles & f1.Name & vbCrLf
msgbox strPath2Folder & "\" & strFiles
WshShell.Run Chr(34) & strFiles & Chr(34), 1, true
Next
Set f1 = Nothing
Set fc = Nothing
Set f = Nothing
End If
Set fso = Nothing
End Function
The msgbox line displays the full path and file name that I want to execute, but the run line generates file not found error.
The variable strFiles continually builds up a list of files with line breaks in between. For example, if your folder contains the files "test1.bat" and "test2.bat", you will end up with this:
Iteration 1:
strFiles =
test1.bat
Iteration 1:
strFiles =
test1.bat
test2.bat
I don't think this is what you want to do. If you want to just run each script in order, you should just pass the single script name.
Try changing the inner loop to this:
For Each f1 in fc
Dim fileToRun
fileToRun = strPath2Folder & "\" & f1.Name
WshShell.Run Chr(34) & fileToRun & Chr(34), 1, true
Next
This is a very sloppy approach. If you are needing to execute an entire directory of batch files at one time, then you are not using them correctly. You should only need one batch file or one script an any time. I would begin looking at your whole system for a better approach to whatever it is that you are trying to accomplish.

Use clipboard from VBScript

I am looking for a method to place some text onto the clipboard with VBScript. The VBScript in question will be deployed as part of our login script. I would like to avoid using anything that isn't available on a clean Windows XP system.
Edit:
In answer to the questions about what this is for.
We wanted to encourage users inside our organization to use the file server to transfer documents instead of constantly sending attachments by email. One of the biggest barriers to this is that it isn't always obvious to people what the correct network path is to a file/folder. We developed a quick script, and attached it to the Windows context menu so that a user can right click on any file/folder, and get a URL that they can email to someone within our organization.
I want the URL displayed in the dialog box to also be placed onto the clipboard.
GetNetworkPath
Another solution I have found that isn't perfect in my opinion, but doesn't have the annoying security warnings is to use clip.exe from a w2k3 server.
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "cmd.exe /c echo hello world | clip", 0, TRUE
Example with a multiline string as per question below :
Link1
Dim string
String = "text here" &chr(13)& "more text here"
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "cmd.exe /c echo " & String & " | clip", 0, TRUE
Using Microsoft's clip.exe is the closest to having a clean Windows XP system solution. However you don't have to call CMD.EXE to host it in order to use it. You can call it directly and write to its input stream in your script code. Once you close the input stream clip.exe will write the contents straight to the clipboard.
Set WshShell = CreateObject("WScript.Shell")
Set oExec = WshShell.Exec("clip")
Set oIn = oExec.stdIn
oIn.WriteLine "Something One"
oIn.WriteLine "Something Two"
oIn.WriteLine "Something Three"
oIn.Close
If you need to wait for clip to be finished before your script can continue processing then add
' loop until we're finished working.
Do While oExec.Status = 0
WScript.Sleep 100
Loop
And don't forget to release your objects
Set oIn = Nothing
Set oExec = Nothing
The closest solution I have found so far is a method to use IE to get and set stuff on the clipboard. The problem with this solution is the user gets security warnings. I am tempted to move 'about:blank' to the local computer security zone so I don't get the warnings, but I am not sure what the security implications of that would be.
Set objIE = CreateObject("InternetExplorer.Application")
objIE.Navigate("about:blank")
objIE.document.parentwindow.clipboardData.SetData "text", "Hello This Is A Test"
objIE.Quit
http://www.microsoft.com/technet/scriptcenter/resources/qanda/dec04/hey1215.mspx
No security warnings, full let and get access:
'create a clipboard thing
Dim ClipBoard
Set Clipboard = New cClipBoard
ClipBoard.Clear
ClipBoard.Data = "Test"
Class cClipBoard
Private objHTML
Private Sub Class_Initialize
Set objHTML = CreateObject("htmlfile")
End Sub
Public Sub Clear()
objHTML.ParentWindow.ClipboardData.ClearData()
End Sub
Public Property Let Data(Value)
objHTML.ParentWindow.ClipboardData.SetData "Text" , Value
End Property
Public Property Get Data()
Data = objHTML.ParentWindow.ClipboardData.GetData("Text")
End Property
Private Sub Class_Terminate
Set objHTML = Nothing
End Sub
End Class
Example Usage.
' Create scripting object
Dim WShell, lRunUninstall
Set WShell = CreateObject("WScript.Shell")
WShell.sendkeys "^c"
WScript.Sleep 250
bWindowFound = WShell.AppActivate("Microsoft Excel")
WShell.sendkeys ClipBoard.Data
To avoid the security warnings associated with Internet Explorer and clipboard access, I would recommend you use the Word application object and its methods to put your data onto the clipboard. Of course you can only use this on a machine that has MS Word installed, but these days that's most of them. (*In spite of the fact that you asked for stuff on a 'clean' system :) *)
' Set what you want to put in the clipboard '
strMessage = "Imagine that, it works!"
' Declare an object for the word application '
Set objWord = CreateObject("Word.Application")
' Using the object '
With objWord
.Visible = False ' Don't show word '
.Documents.Add ' Create a document '
.Selection.TypeText strMessage ' Put text into it '
.Selection.WholeStory ' Select everything in the doc '
.Selection.Copy ' Copy contents to clipboard '
.Quit False ' Close Word, don't save '
End With
You can find detail on the MS Word application object and its methods here: http://msdn.microsoft.com/en-us/library/aa221371(office.11).aspx
Microsoft doesn't give a way for VBScript to directly access the clipboard. If you do a search for 'clipboard'on this site you'll see:
Although Visual Basic for Applications supports the Screen, Printer, App, Debug, Err, and Clipboard objects, VBScript supports only the Err object. Therefore, VBScript does not allow you to access such useful objects as the mouse pointer or the clipboard. You can, however, use the Err object to provide runtime error handling for your applications.
So using notepad indirectly is probably about the best you'll be able to do with just VBScript.
Here's another version of using the "clip" command, which avoids adding a carriage return, line feed to the end of the string:
strA= "some character string"
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "cmd /C echo . | set /p x=" & strA & "| c:\clip.exe", 2
s = "String: """ & strA & """ is on the clipboard."
Wscript.Echo s
I've only tested this in XP. clip.exe was downloaded from Link and placed in C:\.
I've found a way to copy multi line information to clipboard by vbscript/cmd.
Sequence:
with VBS generate the final "formatted string" that you need copy to clipboard
generate a (txt) file with the "formatted string"
use type command from cmd to paste information to clip by pipe
Example script:
Function CopyToClipboard( sInputString )
Dim oShell: Set oShell = CreateObject("WScript.Shell")
Dim sTempFolder: sTempFolder = oShell.ExpandEnvironmentStrings("%TEMP%")
Dim sFullFilePath: sFullFilePath = sTempFolder & "\" & "temp_file.txt"
Const iForWriting = 2, bCreateFile = True
Dim oFSO: Set oFSO = CreateObject("Scripting.FileSystemObject")
With oFSO.OpenTextFile(sFullFilePath, iForWriting, bCreateFile)
.Write sInputString
.Close
End With
Const iHideWindow = 0, bWaitOnReturnTrue = True
Dim sCommand: sCommand = "CMD /C TYPE " & sFullFilePath & "|CLIP"
oShell.Run sCommand, iHideWindow, bWaitOnReturnTrue
Set oShell = Nothing
Set oFSO = Nothing
End Function
Sub Main
Call CopyToClipboard( "Text1" & vbNewLine & "Text2" )
End Sub
Call Main
The easiest way is to use built-in mshta.exe functionality:
sText = "Text Content"
CreateObject("WScript.Shell").Run "mshta.exe ""javascript:clipboardData.setData('text','" & Replace(Replace(sText, "\", "\\"), "'", "\'") & "');close();""", 0, True
To put to clipboard a string containing double quote char ", use the below code:
sText = "Text Content and double quote "" char"
CreateObject("WScript.Shell").Run "mshta.exe ""javascript:clipboardData.setData('text','" & Replace(Replace(Replace(sText, "\", "\\"), """", """"""), "'", "\'") & "'.replace('""""',String.fromCharCode(34)));close();""", 0, True
Take a look at this post. It describes a hacky approach to read from the clipboard, but I imagine it could be adapted to also write to the clipboard as well, such as changing the Ctrl+V to Ctrl+A then Ctrl+C.
I devised another way to use IE and yet avoid security warnings...
By the way.. this function is in JavaScript.. but u can easily convert it to VBScript..
function CopyText(sTxt) {
var oIe = WScript.CreateObject('InternetExplorer.Application');
oIe.silent = true;
oIe.Navigate('about:blank');
while(oIe.ReadyState!=4) WScript.Sleep(20);
while(oIe.document.readyState!='complete') WSript.Sleep(20);
oIe.document.body.innerHTML = "<textarea id=txtArea wrap=off></textarea>";
var oTb = oIe.document.getElementById('txtArea');
oTb.value = sTxt;
oTb.select();
oTb = null;
oIe.ExecWB(12,0);
oIe.Quit();
oIe = null;
}
Here is Srikanth's method translated into vbs
function SetClipBoard(sTxt)
Set oIe = WScript.CreateObject("InternetExplorer.Application")
oIe.silent = true
oIe.Navigate("about:blank")
do while oIe.ReadyState <> 4
WScript.Sleep 20
loop
do while oIe.document.readyState <> "complete"
WScript.Sleep 20
loop
oIe.document.body.innerHTML = "<textarea id=txtArea wrap=off></textarea>"
set oTb = oIe.document.getElementById("txtArea")
oTb.value = sTxt
oTb.select
set oTb = nothing
oIe.ExecWB 12,0
oIe.Quit
Set oIe = nothing
End function
function GetClipBoard()
set oIe = WScript.CreateObject("InternetExplorer.Application")
oIe.silent = true
oIe.Navigate("about:blank")
do while oIe.ReadyState <> 4
WScript.Sleep 20
loop
do while oIe.document.readyState <> "complete"
WScript.Sleep 20
loop
oIe.document.body.innerHTML = "<textarea id=txtArea wrap=off></textarea>"
set oTb = oIe.document.getElementById("txtArea")
oTb.focus
oIe.ExecWB 13,0
GetClipBoard = oTb.value
oTb.select
set oTb = nothing
oIe.Quit
Set oIe = nothing
End function
In your Class ClipBoard, neither the Clear sub nor the Let Data sub work. I mean they have no effect on Windows Clipboard. Actually, and ironically so, the only sub that works is the one you have not included in your example, that is Get Data! (I have tested this code quite a few times.)
However, it's not your fault. I have tried to copy data to clipboard with ClipboardData.SetData and it's impossible. At least not by creating an "htmlfile" object. Maybe it works by creating an instance of "InternetExplorer.Application" as I have seen in a few cases, but I have not tried it. I hate creating application instances for such simple tasks!
Alkis
If it's just text can't you simply create a text file and read in the contents when you need it?
Another alternative and clearly a kludge, would be to use the SendKeys() method.
No security warnings and no carriage return at the end of line
' value to put in Clipboard
mavaleur = "YEAH"
' current Dir
path = WScript.ScriptFullName
GetPath = Left(path, InStrRev(path, "\"))
' Put the value in a file
Set objFSO=CreateObject("Scripting.FileSystemObject")
outFile=GetPath & "fichier.valeur"
Set objFile = objFSO.CreateTextFile(outFile,True)
objFile.Write mavaleur
objFile.Close
' Put the file in the Clipboard
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "cmd.exe /c clip < " & outFile, 0, TRUE
' Erase the file
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.DeleteFile outFile

Resources