VBA code slow to run the first time each day - performance

I have a issue, which i thought was to do with network drives but now i have tested and its not the issue.
There is 2000 files(totaling 328MB) that match, used in the test and it takes about 1.4seconds to complete this anytime i run it, EXCEPT the first time each day, when it takes anywhere from 30secs to 60 secs.
I thought Dir was causing the issue but its definitively inside the loop that is slow.
Would File caching be causing this issue?
Is there a better way to load in the first line of a large quantity of files quickly
'Get All Filenames
sAllFiles = Dir("C:\Folder\" & sFile & "??.???")
'Loop through each File
Do While Len(sAllFiles) > 0
sCurrentFileName = sAllFiles
sCurrentFilePath = "C:\Folder\" & sCurrentFileName
'Read 1st line from each file
Open sCurrentFilePath For Input As #1
Line Input #1, sFirstLine
Close #1
vRowData = Split(sFirstLine, "~")
'(Write data to array code here)
sAllFiles = Dir
Loop

Related

Exporting the SAS Project Log file

I used to run 3 SAS EG Projects on a daily basis. Since a couple of days, we have a "SAS Scheduler" that is basically running those latter during the night (the first one at 00:00 AM, second one at 01:00 AM, third one at 03:00 AM). Each SAS Project has multiple SAS Programs.
All in all, that is great news, but this also mean I can't check the logs directly anymore.
To keep track of the night jobs, I am trying to find what could be the best way to export the log files for each project. I found out about the SAS Project Log recently, which basically summarize the logs from all the programs within a SAS Project.
I discovered CaseySmith's answer on the SAS Community forum, basically tweaking the .vbs script to save the SAS Project log file to a .txt using the following code:
Set objProjectLog = objProject.ProjectLog
objProjectLog.Clear()
objProjectLog.Enabled = True
'strProjectLog = objProjectLog.Text
objProjectLog.SaveAs "c:\temp\projectLog.txt"
But, 1) It is a .txt file not a log file and 2) I don't know where to add it in my current .vbs script:
Option Explicit
Dim app
Call dowork
'shut down the app
If not (app Is Nothing) Then
app.Quit
Set app = Nothing
End If
Sub dowork()
On Error Resume Next
'----
' Start up Enterprise Guide using the project name
'----
Dim prjName
Dim prjObject
prjName = "C:\Users\kermit\Desktop\Project.egp" 'Project Name
Set app = CreateObject("SASEGObjectModel.Application.8.1")
If Checkerror("CreateObject") = True Then
Exit Sub
End If
'-----
' open the project
'-----
Set prjObject = app.Open(prjName,"")
If Checkerror("app.Open") = True Then
Exit Sub
End If
'-----
' run the project
'-----
prjObject.run
If Checkerror("Project.run") = True Then
Exit Sub
End If
'-----
' Save the new project
'-----
prjObject.Save
If Checkerror("Project.Save") = True Then
Exit Sub
End If
'-----
' Close the project
'-----
prjObject.Close
If Checkerror("Project.Close") = True Then
Exit Sub
End If
End Sub
Function Checkerror(fnName)
Checkerror = False
Dim strmsg
Dim errNum
If Err.Number <> 0 Then
strmsg = "Error #" & Hex(Err.Number) & vbCrLf & "In Function " & fnName & vbCrLf & Err.Description
'MsgBox strmsg 'Uncomment this line if you want to be notified via MessageBox of Errors in the script.
Checkerror = True
End If
End Function
In the end, what I would like is that on the morning, I run a program that scan the 3 project log files for Notes, Warning and Errors and send to myself an email with the results. Hence, is there a way to export the SAS Project Log (not manually) in a folder?
So, first, what is this code doing?
Set objProjectLog = objProject.ProjectLog
objProjectLog.Clear()
This clears the project log. This needs to be done before your project is run - otherwise the log contains data from past runs. So put this before the prjOBject.Run().
objProjectLog.Enabled = True
'strProjectLog = objProjectLog.Text
objProjectLog.SaveAs "c:\temp\projectLog.txt"
This then exports the project log to a text file. You of course can call that text file whatever you want. You need this code to appear after your program runs, and somewhere before it closes. Right after PrjObject.Run() is probably fine.
You will need to update the names to match your vbs file's names - they use objproject and your vbs uses prjObject, but those are the same thing, just match the names.
Second - what else could you do? If VBS isn't your thing, you have a lot of other ways you could do this.
Export your EG project to a .sas file, then schedule this in base SAS with the normal output options. This may also be possible via the scheduling interface.
Use PROC PRINTTO to redirect your log inside your SAS code.
Copy your EG project to a location you can see. The EG project does contain the log of everything that was run - so there's no reason you couldn't just open the .egp and look at it, just make sure you're not doing that with the production file since you might forget to close out.
My preference is not to schedule EG projects, but to schedule .sas programs; use EG as the development environment and then export to .sas. This gives you more flexibility. But there are a lot of different ways to skin this cat.

VBS: For-each loop (sometimes) iterates through file collection indefinitely

The goal of the following VBscript is to prepend a user-defined string to all files with a particular extension within a specified directory:
directory = "C:\Users\xxxxxxxx\Desktop\Test\" 'include final backslash
extension = ".doc" 'include period, ex: ".tab"
''''''''''''''''''''''''''''''''''''''''''
addStr = InputBox("Enter the text you would like to prepend:", , "xxxxxxxx_xxxxxxxxxx_x_xx_xxx_")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(directory)
Set colFiles = objFolder.Files
For Each file In colFiles
absPath = objFSO.GetAbsolutePathName(file)
currentExtension = objFSO.GetExtensionName(absPath)
If StrComp(currentExtension, Mid(extension, 2)) = 0 Then
file.Name = addStr & objFSO.GetFileName(file)
End If
Next
The script generally works well, but occasionally demonstrates this problematic behavior:
When running the script on a directory with lots of files and/or with files with long names, the script appears to iterate back over the collection of files (i.e. prepends to files that have already been prepended) and does so until the filenames become too long to be recognized by the FSO, crashing the script.
The threshold of the number of files/length of filenames at which this occurs appears to be very distinct and reproducible. For example, if I create a target directory (e.g. "...\Desktop\Test") with a file named '1.doc' that is copied/pasted several times, the script will properly rename up to 31 files, but it demonstrates the problematic behavior with 32+ files. Similarly, if I run the script twice over 31 files (generated in the same manner), the script demonstrates the problematic behavior on the second run.
Any thoughts as to the underlying issue are very much appreciated--thanks in advance!
You may have issues here because you're modifying files while iterating them. Try creating an array of file names first and then iterate over the array, changing the names.
ReDim a(colFiles.Count - 1)
i = 0
For Each File In colFiles
a(i) = File.Path
i = i + 1
Next
For i = 0 To UBound(a)
If StrComp(objFSO.GetExtensionName(a(i)), Mid(extension, 2)) = 0 Then
With objFSO.GetFile(a(i))
.Name = addStr & .Name
End With
End If
Next
The reason the above behaviour occurs is, because when you initially call Set colFiles = objFolder.Files, the first 32 files are retrieved, and placed into a cache. Once those 32 files are processed, then the system retrieves the first 32 filenames which have not been processed yet.
Since you have renamed the files after the initial call, the system sees those as new filenames that have not been processed yet. Since their names are still first alphabetically, they are placed into the 32-file cache, and processed again.
The solution by #Bond is the standard workaround for this issue. Due to limitations of vbs, this is the only practical resolution of this issue.

Vbscript seek to end of file to make a tail command script

I've writen a simple tail command using vbscript. It works fine except for very large files where it has to read through the entire file to get the last 10 lines. Is there a way to seek to the end of the file and then read backwards for ten lines?
I am afraid that seeking backwards is impossible in VBS TextStream, but instead of reading through the entire file you can seek to a position eg. 1K before EOF and then read the rest of the file, displaying only the last 10 lines.
EDIT: I'm adding some sample code to illustrate the idea:
set fso = CreateObject("Scripting.FileSystemObject")
set file = fso.GetFile(filePath)
set stream = file.OpenAsTextStream(1, -2)
pos1KBeforeEnd = file.Size-1024
if pos1KBeforeEnd<0 then pos1KBeforeEnd=0
stream.Skip pos1KBeforeEnd

Getting modification date-time of a file with seconds in cmd.exe

I've created a batch which automatically uploads some files to FTP server, if they're modified. And modification is detected by changed file's modification time and size.
But if the modification is made within the same minute, and file size did not change, modification stays undetected, and file is not uploaded. Is there a way to get exact modification time (including seconds) of a file in a windows batch?
This is a bit of VBScript that might do it for you:
set FSO=CreateObject("Scripting.FileSystemObject")
if WScript.Arguments.Count = 0 then
Wscript.Echo "No files specified"
Wscript.Quit 1
end if
Set File=FSO.GetFile( WScript.Arguments.Item(0))
Date2=File.DateLastModified
Wscript.Echo date2

How to play audio file on windows from command line?

In Windows, is there a simple way (i.e. something you could type on a single command line) to just play a couple of .mp3 files and then exit on its own?
wmplayer, for example, does not seem to exit when finished, forcing the harried user to hunt it down and click it closed every time. (wmplayer 11 also seems to strangely repeat some files if you pass it a list.)
(The older (1990's) versions of 'mplayer' used to support a '/close' command line option, but it doesn't seem to work in wmplayer 11.)
I'd prefer to use something that everyone will have on their machine (like wmplayer, quicktime...)
Old question, new answer - you could use PowerShell:
powershell -c (New-Object Media.SoundPlayer 'c:\PathTo\YourSound.wav').PlaySync();
I have used cmdmp3. Very lightweight at 28Kb.
Use VBScript:
Set objArgs = Wscript.Arguments
if (objArgs.Count = 0) then
Wscript.echo "I need a sound file as argument!"
WScript.Quit 123
end if
Wscript.echo "Playing: " & objArgs(0) & "..."
Set objPlayer = createobject("Wmplayer.OCX.7")
With objPlayer ' saves typing
.settings.autoStart = True
.settings.volume = 50 ' 0 - 100
.settings.balance = 0 ' -100 to 100
.settings.enableErrorDialogs = False
.enableContextMenu = False
.URL = objArgs(0)
WScript.Sleep(10000) ' time to load and start playing
'.Controls.Pause() ' stop
End With
MsgBox "if WMP is still playing, clicking OK will end it", _
vbInformation, "WMP Demo finished"
If the VBScript process ends, the Media Player ends too, you have to wait for it (I don't need it, my sounds are only some seconds long).
I used this for my special case today:
https://groups.google.com/forum/#!topic/microsoft.public.scripting.vbscript/gfOOvnN8t-U
There are pure command line players. Some are listed here.
Also, WinAmp used to have command line controller called CLAMP. I am not sure if it's still alive or available but Google for it - it was pretty good.
Also, VLC has some command line capabilities though I never checked them out.
This is what I did to do it:
rem Replace the following path with your music file
start C:\Users\Username\Desktop\your-music.mp3
rem Replace 10 with how many seconds you want the player to run
ping localhost -n 10 >nul
taskkill /im wmplayer.exe
Note this requires .mp3 to be associated with wmplayer.exe (Windows Media Player).
You can use fmedia to play audio files from Windows terminal:
fmedia file1.mp3 file2.mp3
This command will start the playback of file.mp3, then file2.mp3, and then quit after the files have finished playing.
If you wish to do it in background, add --background switch to your command:
fmedia file.ogg --background
This command will start a new process in background and detach from your console immediately.
fmedia is a portable application (works without installation) and consumes very small amount of system resources. Also, its startup time is instantaneous.
P.S. Use command fmedia.exe --install to add it to your %PATH% environment variable, otherwise you need to execute it with full path, e.g. D:\fmedia\fmedia.exe
You can probably write a small VBScript which will use the Windows Media Player ActiveX control to play an audio file. You should be able to terminate the process from that too, once playing finished.
I'm looking around a bit and maybe can come up with a working solution. Might take a while, though.
I am using this improve version of Mayra Delgado's answer:
Set objArgs = Wscript.Arguments
if (objArgs.Count = 0) then
Wscript.echo "I need a sound file as argument!"
WScript.Quit 123
end if
Wscript.echo "Playing: " & objArgs(0) & "..."
Set objPlayer = createobject("Wmplayer.OCX.7")
With objPlayer ' saves typing
.settings.autoStart = True
.settings.volume = 50 ' 0 - 100
.settings.balance = 0 ' -100 to 100
.settings.enableErrorDialogs = False
.enableContextMenu = False
.URL = objArgs(0)
' Wait until play finish
While .Playstate <> 1
Wscript.Sleep 100
Wend
End With
MsgBox "if WMP is still playing, clicking OK will end it", _
vbInformation, "WMP Demo finished"
I've found that the fastest way to play .mp3 files in Windows commandline is mpeg123
I know it's not available on people's machine per default, but from my point of view, Microsoft's own players are not consistently available over different versions either.
I'm using it on a project where the execution time is essential, and features like only playing certain frames makes is very useful. I find this (in my configuration) faster than the cmdmp3 and vbscript examples mentioned in this thread.
My syntax to only play certain frames of an .mp3 file :
mpg123.exe -k 2 -n 3 -q -1 -4 beep.mp3
Here are few scripts.
mediarunner.bat - it uses windows media player active x objects so you cannot used if there's no installed Windows Media Player (though it usually comes packed with the Windows).Accepts only one argument - the path to the file you want to play.
spplayer.bat - uses SP Voice objects but can play only .wav files.Again it accepts as only argument the path to the file you want to play.
soundplayer.bat - uses Internet Explorer objects and specific bgsound tag that can be used only in internet explorer.Can play .mp3,wav,.. files. It can accepts two arguments. The file you want to play and the sound volume (a number between -10000 to 0) :
call soundplayer.bat "C:\Windows\Media\Windows Navigation Start.wav" 0
If you need something cross-platform, mplayer works well on linux and windows and is compatible with npm's play-sound.

Resources