MediaInfo CLI vbs multiple Audio and Text streams issue - arrays

I want to output selected parameters from movie files.
I'm doing this in that way:
arrGeneral = Array("General;Video Format List: %Video_Format_List%","General;File name: %FileNameExtension%","General;Commercial name: %Format_Commercial%")
arrVideo = Array("Video;ID: %ID%","Video;Format: %Format/String%","Video;Format info: %Format/Info%","Video;Format profile: %Format_Profile%","Video;Bit rate: %BitRate/String%")
arrAudio = Array("Audio;ID: %ID%\n","Audio;Format: %Format%\n","Audio;Format info: %Format/Info%\n","Audio;Commercial name: %Format_Commercial%\n","Audio;Codec ID: %CodecID%\n")
arrText = Array("Text;ID: %ID%\n","Text;Format: %Format/String%\n","Text;Codec ID: %CodecID/Info%\n","Text;Title: %Title%\n","Text;Language: %Language/String%\n")
' == General section ==
objFileToWrite.Write "General" & vbCrLf
for i = 0 to UBound(arrGeneral)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrGeneral(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
objFileToWrite.Write result
next
objFileToWrite.Write "----------" & vbCrLf
' == Video section ==
objFileToWrite.Write "Video" & vbCrLf
for i = 0 to UBound(arrVideo)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrVideo(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
objFileToWrite.Write result
next
objFileToWrite.Write "----------" & vbCrLf
' == Audio section ==
' Here is a problem beacuse it might be more than 1 Audio stream
objFileToWrite.Write "Audio" & vbCrLf
for i = 0 to UBound(arrAudio)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrAudio(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
objFileToWrite.Write result
next
objFileToWrite.Write "----------" & vbCrLf
For General and Video streams these commands (examples):
MediaInfo.exe --Output="General;Video Format List: %Video_Format_List%" "Sample.mkv"
MediaInfo.exe --Output="Video;Format info: %Format/Info%" "Sample.mkv"
are generating single line results like for instance:
Video Format List: HEVC
Format info: High Efficiency Video Coding
so my script is properly creating output file that contain:
General
Video Format List: HEVC
File name: Sample.mkv
Commercial name: Matroska
Format version: Version 4
File size: 10.03 GiB
Duration: 1 h 52 min 15 s 744 ms
Overall bit rate: 12.8 Mb/s
----------
Video
ID: 1
Format: HEVC
Format info: High Efficiency Video Coding
Format profile: Main 10#L4#Main
Bit rate: 8 525 kb/s
Width: 1 920 pixels
Height: 1 080 pixels
Display aspect ratio: 1.778
Display aspect ratio: 16:9
Frame rate mode: Constant
Frame rate: 23.976 (24000/1001) FPS
Color space: YUV
Chroma subsampling: 4:2:0
Bit depth: 10 bits
Video stream size: 6.68 GiB (67%)
----------
But for the Audio and Text (subtitle) there might be more than one stream and the single command:
MediaInfo.exe --Output="Audio;Format info: %Format/Info%\n" "Sample.mkv"
is generating two or more lines (depends on how many streams movie file contain) like this:
Format info: Digital Theater Systems
Format info: Audio Coding 3
As the result in output file I'm getting something like this:
Audio
ID: 2
ID: 3
Format: DTS
Format: AC-3
Format info: Digital Theater Systems
Format info: Audio Coding 3
Commercial name: DTS-HD Master Audio
Commercial name: Dolby Digital
Codec ID: A_DTS
Codec ID: A_AC3
Bit rate: 3 958 kb/s
Bit rate: 192 kb/s
but expected result is:
Audio #2
ID: 2
Format: DTS
Format info: Digital Theater Systems
Commercial name: DTS-HD Master Audio
Codec ID: A_DTS
Bit rate: 3 958 kb/s
Audio #3
ID: 3
Format: AC-3
Format info: Audio Coding 3
Commercial name: Dolby Digital
Codec ID: A_AC3
Bit rate: 192 kb/s
I think it would be necessary to insert "multiline" results of Audio and Text streams into some dynamic arrays and in the next step to go through them and insert into output file "sorted" values or the second option is to sort final output file but in both cases but I have no clue how to do that.

Here is the vbs script that can be used to collect selected attributes:
InVideoFile = "C:\Temp\Sample.mkv"
InVideoFile = replace(InVideoFile,chr(34),"")
inFile = Left(InVideoFile, Len(InVideoFile) -4) & "-All.txt"
outFile = Left(inFile, Len(inFile) -4) & "-MediaInfo.txt"
Set objFSO = CreateObject("Scripting.FileSystemObject")
if objFSO.FileExists(inFile) then
objFSO.DeleteFile inFile
end if
' MediaInfo.exe location below in ProgramToExecute variable
' MediaInfo.exe (Interface CLI) download location: https://mediaarea.net/en/MediaInfo/Download/Windows
ProgramToExecute = "C:\Program Files\MediaInfo\MediaInfo_CLI\MediaInfo.exe"
commandToRun = "cmd /C " & chr(34) & chr(34) & ProgramToExecute & chr(34) & " -f " & chr(34) & InVideoFile &chr(34) & " > " &chr(34) & inFile &chr(34) & chr(34)
Dim objShell
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run commandToRun,1,true
Set objShell = Nothing
Set objFileToRead = objFSO.OpenTextFile(inFile, 1)
Set objFileToWrite = objFSO.CreateTextFile(outFile,True)
Dim arrGeneral
Dim arrVideo
Dim arrAudio
Dim arrText
' Attributes that are need to be collected are taken from the result of command MediaInfo.exe --Info-Parameters
' Arrays elements are build in the following way:
' section;Text %Attribute%Delimiter
' examples:
' "General;Video Format List: %Video_Format_List%"
' "Video;Format profile: %Format_Profile%"
' "Audio;Channel(s): %Channel(s)/String%|"
' "Text;Format: %Format/String%|"
' Audio and Text sections might contain more than one stream. Result is delimited by "|" character (can be used another) and later on is split
arrGeneral = Array("General;Video Format List: %Video_Format_List%","General;File name: %FileNameExtension%","General;Commercial name: %Format_Commercial%","General;Format version: %Format_Version%","General;File size: %FileSize/String4%","General;Duration: %Duration/String1%","General;Overall bit rate: %OverallBitRate/String%")
arrVideo = Array("Video;ID: %ID%","Video;Format: %Format/String%","Video;Format info: %Format/Info%","Video;Format profile: %Format_Profile%","Video;Bit rate: %BitRate/String%","Video;Width: %Width/String%","Video;Height: %Height/String%","Video;Display aspect ratio: %DisplayAspectRatio/String%","Video;Frame rate mode: %FrameRate_Mode/String%","Video;Frame rate: %FrameRate/String%","Video;Color space: %ColorSpace%","Video;Chroma subsampling: %ChromaSubsampling/String%","Video;Bit depth: %BitDepth/String%","Video;Video stream size: %StreamSize/String5%","Video;Colour primaries: %colour_primaries%","Video;Transfer characteristices: %transfer_characteristics%","Video;Matrix coefficients: %matrix_coefficients%")
arrAudio = Array("Audio;ID: %ID%|","Audio;Format: %Format%|","Audio;Format info: %Format/Info%|","Audio;Commercial name: %Format_Commercial%|","Audio;Codec ID: %CodecID%|","Audio;Bit rate: %BitRate/String%|","Audio;Channel(s): %Channel(s)/String%|","Audio;Channel positions: %ChannelPositions%|","Audio;Channel layout: %ChannelLayout%|","Audio;Sampling rate: %SamplingRate/String%|","Audio;Bit depth: %BitDepth/String%|","Audio;Compression mode: %Compression_Mode/String%|","Audio;Audio stream size: %StreamSize/String5%|","Audio;Title: %Title%|","Audio;Language: %Language/String%|","Audio;Default: %Default/String%|","Audio;Forced: %Forced/String%|")
arrText = Array("Text;ID: %ID%|","Text;Format: %Format/String%|","Text;Codec ID: %CodecID/Info%|","Text;Title: %Title%|","Text;Language: %Language/String%|","Text;Default: %Default/String%|","Text;Forced: %Forced/String%|")
' == General section ==
objFileToWrite.Write "General" & vbCrLf
for i = 0 to UBound(arrGeneral)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrGeneral(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
resultNoCRLF = Replace(result, vbCRLF, "")
if right(resultNoCRLF,2) <> ": " then
objFileToWrite.Write result
end if
next
objFileToWrite.Write "----------" & vbCrLf
' == Video section ==
objFileToWrite.Write "Video" & vbCrLf
for i = 0 to UBound(arrVideo)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrVideo(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
resultNoCRLF = Replace(result, vbCRLF, "")
if right(resultNoCRLF,2) <> ": " then
objFileToWrite.Write result
end if
next
objFileToWrite.Write "----------" & vbCrLf
' == Audio section ==
' It might be more than 1 Audio stream
objFileToWrite.Write "Audio" & vbCrLf
for i = 0 to UBound(arrAudio)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrAudio(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
Dim arrAudioSplit
arrAudioSplit = Split(result,"|")
ReDim Preserve arrA(UBound(arrAudio),UBound(arrAudioSplit)-1)
for z = 0 to UBound(arrAudioSplit)-1
arrA(i,z) = arrAudioSplit(z)
next
next
for j = 0 to Ubound(arrAudioSplit) - 1
for k = 0 to Ubound(arrAudio)
if right(arrA(k,j),2) <> ": " then
objFileToWrite.Write arrA(k,j) & vbCrLf
end if
next
objFileToWrite.Write "----------" & vbCrLf
next
' == Text (subtitles) section ==
' It might be more than 1 Text stream
objFileToWrite.Write "Text" & vbCrLf
for i = 0 to UBound(arrText)
commandToRun = chr(34) & ProgramToExecute & chr(34) & " --Output=" & chr(34) & arrText(i) & chr(34) & " " & chr(34) & InVideoFile & chr(34)
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
Dim arrTextSplit
arrTextSplit = Split(result,"|")
ReDim Preserve arrT(UBound(arrText),UBound(arrTextSplit)-1)
for z = 0 to UBound(arrTextSplit)-1
arrT(i,z) = arrTextSplit(z)
next
next
for j = 0 to Ubound(arrTextSplit) - 1
for k = 0 to Ubound(arrText)
if right(arrT(k,j),2) <> ": " then
objFileToWrite.Write arrT(k,j) & vbCrLf
end if
next
objFileToWrite.Write "----------" & vbCrLf
next
' == Menu (Chapters) section ==
' Have to be handled in different way by checking result of the command MediaInfo.exe -f inputfile
Set objFileToRead = objFSO.OpenTextFile(inFile, 1)
startReadingMenu = false
menuNextLine = 1
do while not objFileToRead.AtEndOfStream
strLineOriginal = Trim(objFileToRead.ReadLine())
MenuNextLine = MenuNextLine + 1
if Left(strLineOriginal,16) = "Chapters_Pos_End" then
startReadingMenu = true
MenuNextLine = 0
end if
if startReadingMenu then
objFileToWrite.Write "Chapters" & vbCrLf
startReadingMenu = false
end if
if MenuNextLine = 1 then
objFileToWrite.Write strLineOriginal & vbCrLf
MenuNextLine = 0
end if
loop
' Close files
objFileToRead.Close
Set objFileToRead = Nothing
objFileToWrite.Close
Set objFileToWrite = Nothing
' Dislay result file in notepad
DisplayInNotepad = "notepad.exe " & chr(34) & outFile &chr(34)
commandToRun = "cmd /C " & DisplayInNotepad
Dim objSh
Set objSh = WScript.CreateObject("WScript.Shell")
objSh.Run commandToRun,0,true
Set objSh = Nothing
' Cleanup
if objFSO.FileExists(inFile) then
objFSO.DeleteFile inFile
end if
if objFSO.FileExists(outFile) then
objFSO.DeleteFile outFile
end if
The only remaining issue is how to hide cmd window that is blinking when the line is invoked:
result = CreateObject("WScript.Shell").Exec(commandToRun).StdOut.ReadAll
but this is cosmetic issue.

Related

Add two values to a VBScript array and compare to see if they equal each other

What I'm trying to achieve here is getting the two folder size values of objLibrarySource and objLibraryDest and using an if statement to compare the two filesize's and if they equal each other pipe this to the textfile ->
objFile.WriteLine "Match". But my issue is im only getting the value for arrayList(0) and not arrayList(1)
Code:
myArray = Array(objLibrarySource, objLibraryDest)
For Each item In myArray
Set objFolder = objFSO.GetFolder(item)
totalSize = objFolder.Size / 1073741824
arrayList = Array()
arrayList = AddItem(arrayList, totalSize)
arrayList = Split(Join(arrayList) & " ")
objFile.WriteLine objFolder & " File Size: " & Round(totalSize, 2) & " GB"
If (arrayList(0) = arrayList(1)) Then
objFile.WriteLine "Match"
End If
Next
objFile.WriteLine arrayList(0) & vbCrLf
objFile.WriteLine arrayList(1)
Function AddItem(arr, val)
ReDim Preserve arr(UBound(arr) + 1)
arr(UBound(arr)) = val
AddItem = arr
End Function
Output:
D:\LIBRARY_TEST File Size: 7.01 GB
D:\seed_backup\LIBRARY File Size: 7.01 GB
7.01436613406986
I had to refactor my code to
Dim libsizesrc, libsizedst, libsizesrcSize, libsizedstSize
Set libsizesrc = objFSO.GetFolder(objLibrarySource)
Set libsizedst = objFSO.GetFolder(objLibraryDest)
libsizesrcSize = libsizesrc.Size / 1073741824
libsizedstSize = libsizedst.Size / 1073741824
objFile.WriteLine libsizesrc & " File Size: " & Round(libsizesrcSize, 2) & " GB"
objFile.WriteLine libsizedst & " File Size: " & Round(libsizedstSize, 2) & " GB"
'Source & Dest SMPDevices Sizes
Dim SMPsizesrc, SMPsizedst, SMPsizesrcSize, SMPsizedstSize
Set SMPsizesrc = objFSO.GetFolder(objSMPDriversSource)
Set SMPsizedst = objFSO.GetFolder(objSMPDriversDest)
SMPsizesrcSize = SMPsizesrc.Size / 1073741824
SMPsizedstSize = SMPsizedst.Size / 1073741824
objFile.WriteLine SMPsizesrc & " File Size: " & Round(SMPsizesrcSize, 2) & " GB"
objFile.WriteLine SMPsizedst & " File Size: " & Round(SMPsizedstSize, 2) & " GB"
'Library & Source Folder Size Verification
If (libsizesrcSize = libsizedstSize) Then
objFile.Write "Library Folder FileSize Match!" & vbCrLf
End If
If (SMPsizesrcSize = SMPsizedstSize) Then
objFile.Write "SMPDrivers Folders FileSize Match!"
End If
it work as needed!

VBScript binary array help wanted (Windows 10 1607 bug?)

I have a working .vbs file which reads a binary file, changes one byte and the saves the file. Up until Windows 1607 this worked fine on many different Windows systems.
However, now with 1607 and later versions of Windows 10 it no longer works!
I have changed the code because the read file code I had no longer works correctly in 1607, but I am still having problems with the
data = Mid(data, 1, 21) & Chr(b21) & Mid(data, 23) line which worked perfectly before Windows 10 1607!
I get
(60, 3) ADODB.Stream: Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
This code creates a shortcut on the Desktop and then changes one bit of one byte so that the shortcut will be run as admin. If I comment out the offending line then it seems to work.
Is this a bug in Windows 10 1607 VBScript?
' Make shortcut on Desktop and Set as Run As Admin
Q = Chr(34)
Dim fso
Dim curDir
Dim WinScriptHost
If WScript.Arguments.Count < 2 Then
WScript.Echo "Please run CreateShortcuts.cmd"
WScript.Quit
End If
' --- SET Target and Desktop Link Name from command line ---
strTargetName = WScript.Arguments.Item(0)
strLinkName = WScript.Arguments.Item(1)
'Target - e.g. %windir%\system32\cmd.exe /c C:\"temp\MakePartImage_AutoRun_FAT32.cmd"
Set WshShell = CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
strWinDir =WshShell.ExpandEnvironmentStrings("%windir%")
strSysDir = strWinDir & "\System32"
strMyDir = fso.GetParentFolderName(wscript.ScriptFullName)
strDesktop = WshShell.SpecialFolders("Desktop")
strCurDir = WshShell.CurrentDirectory ' e.g. C:\temp
strMyDirSpecial = Mid(strMyDir, 1, 3) & Q & Mid(strMyDir, 4) & "\" & strTargetName & Q
Set oMyShortCut= WshShell.CreateShortcut(strDesktop + "\" & strLinkName)
oMyShortCut.WindowStyle = 1 '1=default 3=max 7=Min
oMyShortCut.TargetPath = Q & strSysDir & "\cmd.exe" & Q
oMyShortCut.Arguments= " /c " & strMyDirSpecial
oMyShortcut.IconLocation = "%windir%\system32\cmd.exe"
oMyShortCut.WorkingDirectory = Q & strMyDir & Q
oMyShortCut.Save
Set fso = Nothing
'read binary geometry into byte array
Dim stream, data
Set stream = CreateObject("ADODB.Stream")
stream.Open
stream.Type = 1
stream.LoadFromFile(strDesktop + "\" & strLinkName)
data = stream.Read
stream.Close
WScript.Echo "BYTES 16-23 " & Hex(Asc(Mid(data, 16, 1))) & " " & Hex(Asc(Mid(data, 17, 1))) & " " & Hex(Asc(Mid(data, 18, 1))) & " " & Hex(Asc(Mid(data, 19, 1))) & " " & Hex(Asc(Mid(data, 20, 1))) & " " & Hex(Asc(Mid(data, 21, 1))) & " " & Hex(Asc(Mid(data, 22, 1))) & " " & Hex(Asc(Mid(data, 23, 1)))
' --- PATCH .LNK FILE to set byte 21 bit 5 for Admin rights
Dim b21
b21 = Asc(Nid(data, 22, 1)) Or 32 'set bit 6 0x20
' THIS NEXT LINE CAUSES PROBLEMS!
data = Mid(data, 1, 21) & Chr(b21) & Mid(data, 23)
WScript.Echo "BYTES 16-23 " & Hex(Asc(Mid(data, 16, 1))) & " " & Hex(Asc(Mid(data, 17, 1))) & " " & Hex(Asc(Mid(data, 18, 1))) & " " & Hex(Asc(Mid(data, 19, 1))) & " " & Hex(Asc(Mid(data, 20, 1))) & " " & Hex(Asc(Mid(data, 21, 1))) & " " & Hex(Asc(Mid(data, 22, 1))) & " " & Hex(Asc(Mid(data, 23, 1)))
Const adTypeBinary = 1
Const adTypeText = 2
Const adSaveCreateOverWrite = 2
Dim BinaryStream
Set BinaryStream = CreateObject("ADODB.Stream")
BinaryStream.Type = adTypeBinary
BinaryStream.Open
BinaryStream.Write data
BinaryStream.SaveToFile strDesktop+"\" & strLinkName, adSaveCreateOverWrite
WScript.Echo "Shortcut " & strLinkName & " created on Desktop."
' THIS NEXT LINE CAUSES PROBLEMS!
data = Mid(data, 1, 21) & Chr(b21) & Mid(data, 23)
This line causes problems because it changes the type of data from Byte() to String. This would illustrate it:
WScript.Echo TypeName(data)
' THIS NEXT LINE CAUSES PROBLEMS!
data = Mid(data, 1, 21) & Chr(b21) & Mid(data, 23)
WScript.Echo TypeName(data)
ADODB Stream.Write function only accepts Byte() arrays.
The solution is to use this function from motobit website:
' http://www.motobit.com/tips/detpg_binarytostring/
Function MultiByteToBinary(MultiByte)
'� 2000 Antonin Foller, http://www.motobit.com
' MultiByteToBinary converts multibyte string To real binary data (VT_UI1 | VT_ARRAY)
' Using recordset
Dim RS, LMultiByte, Binary
Const adLongVarBinary = 205
Set RS = CreateObject("ADODB.Recordset")
LMultiByte = LenB(MultiByte)
If LMultiByte>0 Then
RS.Fields.Append "mBinary", adLongVarBinary, LMultiByte
RS.Open
RS.AddNew
RS("mBinary").AppendChunk MultiByte & ChrB(0)
RS.Update
Binary = RS("mBinary").GetChunk(LMultiByte)
End If
MultiByteToBinary = Binary
End Function
But the string needs to be converted to multi-byte first. For this purpose there is another function:
' http://www.motobit.com/help/regedit/pa26.htm
'Converts unicode string to a multibyte string
Function StringToMB(S)
Dim I, B
For I = 1 To Len(S)
B = B & ChrB(Asc(Mid(S, I, 1)))
Next
StringToMB = B
End Function
So, this is how to make it work:
data = Mid(data, 1, 21) & Chr(b21) & Mid(data, 23)
data = MultiByteToBinary(StringToMB(data))

recursively move files from subfolders to another folder with vbscript

I need to recurse a folder and all subfolders and move the pdf files to another folder. Looking here I tried the below but it seems to continuously run (the script never ends) but it does move the files.
I would also like to count the source files, recurse folder and move pdf (ignore case) files, count destinations files. If source and destination are equal then run dir *.pdf > trust.csv and send a success email
Set fso = CreateObject("Scripting.FileSystemObject")
Set Message = CreateObject("CDO.Message")
Set Shell = WScript.CreateObject("WScript.Shell")
Set shell = CreateObject("Wscript.shell")
'Specify variables for Emails
strScriptServer = "ASFOXTECHOPS01"
strScriptPath = "\\techopspc01\c$\scripts\WM..."
strScriptName = "[WM-01]-WMScansToCenterDoc.vbs"
'strToEmail = ""
strCCEmail = ""
strCCEmailFail = ""
strProcessID = "[WM-01]"
strCustomerImpact = "LOW"
strCorporateImpact = "LOW"
strDocumentation = "\\FSCHAFOX01\GROUP_SHARE\Tech Group\Documentation\Automation\"
blnEmailNotification = false
'Specify variables for File Paths
'strFromPath1 = "\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky\"
strToPath1 = "\\fschauni01\GROUP_SHARE\Wealth Management\Tech\"
strToArchive1 = "\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\" & StrYear & "_" & strMonth
'BROKE************CREATE ARCHIVE FOLDER IF IT DOES NOT EXIST
'If FSO.FolderExists(strToArchive1) Then
'Proceed
'Else
' FSO.CreateFolder("\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\" & StrYear & "_" & strMonth)
'End If
'DELETE FILES FROM THE strToPath(s) TO AVOID OVERWRITE ERRORS
FSO.DeleteFile (strToPath1 & "*.*")
testfolder = "\\fschauni01\GROUP_SHARE\Wealth Management\Tech\"
MoveFiles fso.GetFolder("\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky")
blnEmailNotification = True
'Email
'If Err <> 0 Then
' blnEmailNotification = false
' HandleError
'End If
If blnEmailNotification = True Then
'Send Results email
objMessage.Subject = "SUCCESS - " & strProcessID & " - WM Scanned Reports Imported into CenterDoc"
objMessage.From = "IT Automation"
objMessage.Sender = ""
'objMessage.To = strToEmail
objMessage.Cc = strCCEmail
objMessage.TextBody = "---------------SCRIPT SUCCESSFUL---------------" & vbnewline & VbCrLf & "Script successfully moved the files with no errors." & vbnewline & vbcrlf & "- Script Name:" & VbTab & VbTab & VbTab & strScriptName & VbNewLine & VbCrLf & "- Script Origination:" & VbTab & VbTab & strScriptServer & VbNewline & VbCrLf & "- Script Path:" & VbTab & VbTab & VbTab & strScriptPath & VbNewLine & VbCrLf & "- Documentation:" & VbTab & VbTab & strDocumentation & VbNewLine & VbCrLf & "- Set1:" &VbTab & VbTab & "Souce Files = " & sourcecount1 & VbTab & VbTab & "Destination Files = " & destcount1 & VbTab & VbTab & "Archive Files = " & archivecount1 & VbTab & VbTab & "------------------------------------------------------------"
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = ""
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send
Else
'Do Nothing
End If
Sub HandleError
strErrorMessage = "Error Number " & Err.Number & ":" & Err.Description
objMessage.Subject = "SCRIPT ERROR - " & strProcessID & " - IMMEDIATE ACTION REQUIRED"
objMessage.From = "IT Automation"
objMessage.Sender = ""
objMessage.To = strToEmail
'objMessage.Cc = strCCEmail
objMessage.TextBody = "---------------SCRIPT ERROR---------------" & vbnewline & VbCrLf & "Script Name:" & VbTab & VbTab & VbTab & strScriptName & VbNewLine & VbCrLf & "Customer Impact:" & VbTab & VbTab & VbTab & strCustomerImpact & VbNewLine & VbCrLf & "Corporate Impact:" & VbTab & VbTab & VbTab & strCorporateImpact & VbNewLine & VbCrLf & "Error Description:" & VbTab & VbTab & VbTab & Err.Description & vbnewline & VbCrLf & "Error Number:" & VbTab & VbTab & VbTab & Err.Number & VbCrLf & VbNewLine & "Script Location:" & VbTab & VbTab & VbTab & strScriptServer & VbCrLf & VbNewLine & "Script Path:" & VbTab & VbTab & VbTab & strScriptPath & VbNewline & VbCrLf & "Documentation:" & VbTab & VbTab & strDocumentation & VbNewLine & VbCrLf & "-------------------------------------------------"
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = ""
objMessage.Configuration.Fields.Item ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
objMessage.Configuration.Fields.Update
objMessage.Send
Err.Clear
End Sub
Sub MoveFiles(fldr)
For Each f In fldr.Files
basename = fso.GetBaseName(f)
extension = fso.GetExtensionName(f)
If LCase(extension) = "pdf" Then
dest = fso.BuildPath(testfolder, f.Name)
Do While fso.FileExists(dest)
dest = fso.BuildPath(testfolder, basename & "." & extension)
Loop
f.Move dest
End If
Next
For Each sf In fldr.SubFolders
MoveFiles sf
Next
End Sub
Edited original code for more help.
thanks Zoyd
astro
You move all the pdf files from \\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky and its subfolders to \\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky\Tech. Your recursion logic is sound, but there is another problem: your destination folder is a subfolder of your source folder. Thus, your script keeps copying files from \\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky\Tech to itself. Whether this works or not, it is a bad idea to do a foreach loop on a collection and modify its content.
The main problem is that this while loop will never end if it is taken at all, and in this case, it is taken. When your recursion reaches the destination folder, all the files already exist in the destination folder (obviously), the loop is taken and there is no way out of it.
If LCase(extension) = "pdf" Then
dest = fso.BuildPath(testfolder, f.Name)
Do While fso.FileExists(dest)
dest = fso.BuildPath(testfolder, basename & "." & extension)
Loop
f.Move dest
End If
As for the matter of counting files, does folder.Files.Count not work ? (if you need a recursive count, I'am afraid you'll have to do it yourself, but you know how).
After your edit, the code still contains this:
Do While fso.FileExists(dest)
dest = fso.BuildPath(testfolder, basename & "." & extension) ' ☠
Loop
So, for example, if a file a.pdf exists in "\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky\docs1" and in "\\fschauni01\GROUP_SHARE\Wealth Management\Scanning\ZAstrosky\docs2", your script will find that a.pdf already exists in the archive directory and enters the infinite loop. A way around this is to recreate the directory structure in the archive directory (each time you call MoveFiles on directoryA, create directoryA in the archive directory and move files there).

Simultaneous writes to text file from multiple computers

Using VBScript, is there any way to allow for multiple simultaneous writes to a text file from multiple computers? I need to run a script from multiple computers at the same time. The script will perform a DEFRAG and save the results to a text file. After that, the script will read from the DEFRAG log file, pull the fragmentation percentage, and write it to another MASTER log file that is meant to contain these results from every computer across the enterprise. If I always only run the script from one computer at a time, then everything works find and dandy. However, once I use a distribution point to throw the script out to the enterprise, the script works perfectly up until the point where more than one computer attempts to access the MASTER log file at the same time. That's when I'm running into access denied errors and the like. Here's what I've got so far...
strDrivePreCheckStarted = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
a=("===================================================================================")
b=(" Script started on " & strDrivePreCheckStarted)
Set objWMIService = GetObject( "winmgmts:\\.\root\cimv2" )
Set colSettings = objWMIService.ExecQuery ( "Select * from Win32_ComputerSystem", , 48 )
For Each objComputer in colSettings
CompNam = objComputer.Name
If CompNam = "" Then
CompNam = "ComputerNameNotFound"
End If
CompMfr = replace(objComputer.Manufacturer,",","")
If CompMfr = "" Then
CompMfr = "ComputerMfrNotFound"
End If
CompMdl = objComputer.Model
If CompNam = "" Then
CompNam = "ComputerModelNotFound"
End If
Next
Set dClient = GetObject( "winmgmts://" & CompNam & "/root/ccm:SMS_Client" )
Set result = dClient.ExecMethod_("GetAssignedSite")
mClient = result.sSiteCode
If mClient = "OLD" Then
mClient = "SMS"
End If
If mClient = "NEW" Then
mClient = "SCCM"
End If
If mClient = "" Then
mClient = "UNKNOWN"
End If
c=(" Computer: " & "[" & mClient & "] " & CompNam & " (" & CompMfr & " " & CompMdl & ")")
Set FSO = CreateObject("Scripting.FileSystemObject")
Set wshShell = CreateObject("WScript.Shell")
Set objShell = WScript.CreateObject("WScript.Shell")
Set strWinDir = FSO.GetSpecialFolder(0)
Set strSys32 = FSO.GetSpecialFolder(1)
Set strTempDir = FSO.GetSpecialFolder(2)
strLogsDir = "\\fileserver\shared\logs\"
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Const OverwriteExisting = False
If FSO.FileExists(strLogsDir & CompNam & ".TXT") Then
WScript.Quit
Else
If FSO.FileExists(strLogsDir & CompNam & "_CHKDSK.LOG") Then
WScript.Quit
Else
If FSO.FileExists(strLogsDir & CompNam & "_DEFRAG.LOG") Then
WScript.Quit
Else
strCHKDSKStarted = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
d=(" CHKDSK Started: " & strCHKDSKStarted)
If FSO.FileExists(strSys32 & "\chkdsk.exe") Then
strCHKDSKReturn = objShell.Run("%COMSPEC% /c chkdsk.exe C: > " & chr(34) & strLogsDir & CompNam & "_CHKDSK.LOG" & chr(34), 0, True)
End If
If FSO.FileExists(strLogsDir & CompNam & "_CHKDSK.LOG") Then
Set ChkDskLog = FSO.OpenTextFile(strLogsDir & CompNam & "_CHKDSK.LOG", ForReading, True)
Do While ChkDskLog.AtEndOfStream <> True
Curline = ChkDskLog.ReadLine
If InStr(Curline, "KB in bad sectors.") Then
Curline = Trim(Curline)
strKBpos = InStr(1, Curline, "KB")-2
strBadKB = Left(Curline, strKBpos)
strBadKB = Trim(strBadKB)
If strBadKB > 0 Then
ChkDskFail = "Failed"
e=(" " & strBadKB & "KB Of Bad Sectors Found In Used Space")
f=(" Drive has Failed Integrity Check")
Else
ChkDskFail = "Passed"
e=(" " & strBadKB & "KB Of Bad Sectors Found In Used Space")
f=(" Drive has Passed Integrity Check")
End If
End If
Loop
If strBadKB = "" Then
ChkDskFail = "Failed"
e=(" Check Disk Log Existed But Was Incomplete: " & Date & " # " & Time)
f=(" Drive has Failed Integrity Check")
End If
Else
ChkDskFail = "Passed"
e=(" No Bad Sectors Found in Used Space")
f=(" Drive has Passed Integrity Check")
End If
ChkDskLog.Close
strCHKDSKFinished = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
g=(" CHKDSK Finished: " & strCHKDSKFinished)
strDEFRAGStarted = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
h=(" DEFRAG Started: " & strDEFRAGStarted)
If FSO.FileExists(strSys32 & "\defrag.exe") Then
strDEFRAGReturn = objShell.Run("%COMSPEC% /c defrag.exe C: -a -v > " & chr(34) & strLogsDir & CompNam & "_DEFRAG.LOG" & chr(34), 0, True)
End If
If FSO.FileExists(strLogsDir & CompNam & "_DEFRAG.LOG") Then
Set DefragLog = FSO.OpenTextFile(strLogsDir & CompNam & "_DEFRAG.LOG", ForReading, True)
Do While DefragLog.AtEndOfStream <> True
CurLine = DefragLog.ReadLine
If InStr(CurLine, "Total fragmentation") Then
FragPosition = InStr(1,CurLine," %",1)-2
CurLine = CurLine
strFragAmount = Right(CurLine,4)
strFragAmount = Left(strFragAmount,2)
strFragAmount = Ltrim(strFragAmount)
End If
Loop
Else
DefragFail = "Failed"
i=(" Log File Could not be Located. Please try Again.")
j=(" Drive has Passed Defragmentation Check")
End If
If strFragAmount = "" Then
DefragFail = "Failed"
i=(" An Unknown Error has Occured. Please try Again.")
j=(" Run 'DEFRAG -v' from this machine manually.")
Else
If strFragAmount < 30 Then
DefragFail = "Passed"
i=(" Drive is " & strFragAmount & "% Fragmented")
j=(" Drive has Passed Defragmentation Check")
End If
If strFragAmount >= 30 Then
DefragFail = "Failed"
i=(" Drive is " & strFragAmount & "% Fragmented")
j=(" Drive has Failed Defragmentation Check")
End If
End If
DefragLog.Close
strDEFRAGFinished = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
k=(" DEFRAG Finished: " & strDEFRAGFinished)
strDrivePreCheckFinished = (Year(Date) & "-" & AEZiR(Month(Date)) & "-" & AEZiR(Day(Date)) & " # " & AEZiR(Hour(Time)) & ":" & AEZiR(Minute(Time)) & ":" & AEZiR(Second(Time)))
l=(" Script finished on " & strDRIVEPreCheckFinished)
m=("===================================================================================")
Set TXTLog = FSO.CreateTextFile(strLogsDir & CompNam & ".TXT", True)
TXTLog.WriteLine(strDrivePreCheckStarted & "," & mClient & "," & CompNam & "," & CompMfr & "," & CompMdl & "," & strCHKDSKStarted & "," & strBadKB & "KB" & "," & ChkDskFail & "," & strCHKDSKFinished & "," & strDEFRAGStarted & "," & strFragAmount & "%" & "," & DefragFail & "," & strDEFRAGFinished & "," & strDrivePreCheckFinished)
TXTLog.Close
If FSO.FileExists(strLogsDir & "_FDE.CSV") Then
Set CSVLog = FSO.OpenTextFile(strLogsDir & "_FDE.CSV", ForAppending, True)
CSVLog.WriteLine(strDrivePreCheckStarted & "," & mClient & "," & CompNam & "," & CompMfr & "," & CompMdl & "," & strCHKDSKStarted & "," & strBadKB & "KB" & "," & ChkDskFail & "," & strCHKDSKFinished & "," & strDEFRAGStarted & "," & strFragAmount & "%" & "," & DefragFail & "," & strDEFRAGFinished & "," & strDrivePreCheckFinished)
Else
Set CSVLog = FSO.CreateTextFile(strLogsDir & "_FDE.CSV", True)
CSVLog.WriteLine("Pre-Check Started,Management Client,Asset Tag,Computer Manufacturer,Computer Model,CHKDSK Started,CHKDSK Bad Sectors,CHKDSK Results,CHKDSK Finished,DEFRAG Started,DEFRAG Amount,DEFRAG Results,DEFRAG Finished,Pre-Check Finished")
CSVLog.WriteLine(strDrivePreCheckStarted & "," & mClient & "," & CompNam & "," & CompMfr & "," & CompMdl & "," & strCHKDSKStarted & "," & strBadKB & "KB" & "," & ChkDskFail & "," & strCHKDSKFinished & "," & strDEFRAGStarted & "," & strFragAmount & "%" & "," & DefragFail & "," & strDEFRAGFinished & "," & strDrivePreCheckFinished)
End If
CSVLog.Close
If FSO.FileExists(strLogsDir & "_FDE.LOG") Then
Set InstallLog = FSO.OpenTextFile(strLogsDir & "_FDE.LOG", ForAppending, True)
InstallLog.WriteLine(b)
InstallLog.WriteLine(c)
InstallLog.WriteLine(d)
InstallLog.WriteLine(e)
InstallLog.WriteLine(f)
InstallLog.WriteLine(g)
InstallLog.WriteLine(h)
InstallLog.WriteLine(i)
InstallLog.WriteLine(j)
InstallLog.WriteLine(k)
InstallLog.WriteLine(l)
InstallLog.WriteLine(m)
Else
Set InstallLog = FSO.CreateTextFile(strLogsDir & "_FDE.LOG", True)
InstallLog.WriteLine(a)
InstallLog.WriteLine(b)
InstallLog.WriteLine(c)
InstallLog.WriteLine(d)
InstallLog.WriteLine(e)
InstallLog.WriteLine(f)
InstallLog.WriteLine(g)
InstallLog.WriteLine(h)
InstallLog.WriteLine(i)
InstallLog.WriteLine(j)
InstallLog.WriteLine(k)
InstallLog.WriteLine(l)
InstallLog.WriteLine(m)
End If
InstallLog.Close
FSO.DeleteFile(strLogsDir & CompNam & "_CHKDSK.LOG")
FSO.DeleteFile(strLogsDir & CompNam & "_DEFRAG.LOG")
End If
End If
End If
Function AEZiR(plngValue)
Dim pstrValue
Dim plngChars
Dim i
pstrValue = CStr(plngValue)
plngChars = Len(pstrValue)
If plngChars < 2 Then
For i = 1 to plngChars Step -1
pstrValue = "0" & pstrValue
Next
End If
AEZiR = pstrValue
End Function
What am I missing? Is this task even possible with VBScript? Thank you in advance.
As neither VBScript, nor the FileSystemObject provide locking, someone/thing else has to do it:
a [scripting] language with proper file locks
a data store (DBMS) that allows simultaneous access
a programmer who uses a semaphore (e.g. a renamed file) to control the file access
In my opinion, the third option is the worst, because
the programmer has to do all the work and to take all
the risks/responsibilities. Using a database would solve
the problem of access control out of the box; using a
suitable language would allow a standard/non-hacking
solution.
But if you like to live in interesting times - that's
the code structure I would try to get away with:
While it make sense to continue (# of tries, timeout, successfully written)
rename FileIsFree.log to FileIsLocked.log
If success
open FileIsLocked.log
write to FileIsLocked.log
close FileIsLocked.log
rename FileIsLocked.log to FileIsFree.log
break/exit
End If
End While
If Not successfully written
Panic
End If
ADDED:
Some food for thought:
discussion
code
I hope you come to the conclusion, that using a DBMS is the better idea.

How to run a file using VisualBasicScript (.vbs)

How can I run a file with VisualBasicScript (.vbs)?
The file is 'file.bat' and it's located in the same dir as the .vbs.
yes i want to run it.
Then try this:
CreateObject("WScript.Shell").Run "file.bat"
See many examples on technet Script Center Script Repository.
A simple example is Select and Ping Computers Using a Text File:
On Error Resume Next
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile("c:\scripts\servers.txt", ForReading)
strComputers = objTextFile.ReadAll
objTextFile.Close
arrComputers = Split(strComputers, vbCrLf)
Set objShell = CreateObject("WScript.Shell")
For Each strComputer In arrComputers
strCommand = "%comspec% /c ping -n 3 -w 1000 " & strComputer
Set objExecObject = objShell.Exec(strCommand)
strText = objExecObject.StdOut.ReadAll
If Instr(strText, "Reply") > 0 Then
' =====================================================================
' Insert your code here
' =====================================================================
Set objWMIService = GetObject _
("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery _
("Select * From Win32_OperatingSystem")
For Each objItem In ColItems
Wscript.Echo strComputer & ": " & objItem.Caption
Next
Else
Wscript.Echo strComputer & " could not be reached."
End If
Next
Use the FileSystemObject
Usage to open file:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile(".\File.bat", ForReading)
function getFileInfo(filePath)
dim fso, fileObj, outMsg
set fso = createobject("Scripting.FileSystemObject")
set fileObj = fso.getfile(filePath)
outMsg = ""
outMsg = outMsg & " Created: " & fileObj.DateCreated & vbcrlf
outMsg = outMsg & " Last Accessed: " & fileObj.DateLastAccessed & vbcrlf
outMsg = outMsg & " Last Modified: " & fileObj.DateLastModified & vbcrlf
outMsg = outMsg & " File Type: " & fileObj.Type & vbcrlf
if fileObj.attributes and 0 then
outMsg = outMsg & " File Attributes: Normal File"
else
outMsg = outMsg & " File Attributes: "
if fileObj.attributes and 1 then
outMsg = outMsg & "Read Only "
end if
if fileObj.attributes and 2 then
outMsg= outMsg & "Hidden "
end if
if fileObj.attributes and 4 then
outMsg= outMsg & "System "
end if
if fileObj.attributes and 8 then
outMsg= outMsg & "Volume "
end if
if fileObj.attributes and 16 then
outMsg= outMsg & "Directory "
end if
if fileObj.attributes and 32 then
outMsg= outMsg & "Archive "
end if
if fileObj.attributes and 1024 then
outMsg= outMsg & "Link "
end if
if fileObj.attributes and 2048 then
outMsg= outMsg & "Compressed "
end if
end if
set fileObj = nothing
set fso = nothing
getFileInfo = outMsg
end function
Even simplier
This code works for all OS, I tried it in Windows 10 and it works so well:
Try it by yourself :)
Function BrowseForFile()
BrowseForFile = CreateObject("WScript.Shell").Exec( _
"mshta.exe ""about:<input type=file id=f>" & _
"<script>resizeTo(0,0);f.click();new ActiveXObject('Scripting.FileSystemObject')" & _
".GetStandardStream(1).WriteLine(f.value);close();</script>""" _
).StdOut.ReadLine()
End Function
Jamb Code:
jamb(run) "%PWD%\File.bat" & display box(small) with $OUTPUT
VBS Code:
set runFile (".\file.bat")
mode console
msgbox (runFile)

Resources