Nested loop append to file - vbscript

I'm using a piece of software called Chronoscan which uses VBScript.
I am attempting to create and append to a file, which in part is successful. However, I am trying to place an argument based on a child table as to whether or not to export the data.
Firstly I am counting the number of records contained in a table named 'batch' (unique id) and looping this against the record number I am assigning variables, within this loop I have another loop which is doing the same for a child table named 'Grid' again assigning variables. I am then writing these variables to the file. Therefore I am writing a value for the number of records held in the child table for each record held in its parent.
What I actually want to do is to argue against the child table and only write records if it meets my argument.
The child table has a field called SkuType which has 3 possible values M, A or NULL.
My argument needs to be if the child table has a value of M write the record else don't, if the child table does not have a value of M in any of its records relating to it parent then only write the first record from the child table and ignore the remainder.
Please see below example of my code, which works fine for exporting ALL records from the child table.
Dim fso
Dim FilePathName
Dim FilePath
Dim NumDocs
Dim numRows
Dim Station
Dim StationDate
Dim StationTime
Dim StationName
Dim Exp
GridPanel = 1 'first panel
Set Batch = ChronoApp.GetCurrentBatch
CurrentBatchname = Batch.Getname
NumDocs = Batch.GetDocCount
'Set output filename and path here
Station = ChronoApp.GetVariableValue("station_user")
StationDate = Left(ChronoApp.GetVariableValue("station_date_full"), 10)
StationName = ChronoApp.GetVariableValue("station_name")
Set fso = CreateObject("Scripting.FileSystemObject")
'Loop the batch here
For numDoc = 0 To NumDocs-1
FilePath = "C:\Users\" & Station & "\Google Drive\Driver Scans\ChronoPlans\" &_
Left(Batch.GetUserField(numDoc, "Movement Date"),2) &_
Mid(Batch.GetUserField(numDoc, "Movement Date"),4,2) &_
Right(Batch.GetUserField(numDoc, "Movement Date"),2) & "\"
FilePathName = FilePath & StationName & "_" & StationDate & ".CSV"
If Not fso.FolderExists(FilePath) Then FSO.CreateFolder (FilePath) 'create folder if it does not exist
Set inputFile = fso.OpenTextFile(FilePathName, 8, True)
'Get header level fields here
StationTime= Left(ChronoApp.GetVariableValue("station_date_full"), 16)
'Loop grid here
numRows = Batch.GetXgridRowCount (numDoc, GridPanel)
For row = 0 To numRows-1
'Get grid level fields here
SkuType = Batch.GetXgridFieldValue (numDoc, GridPanel, row, "SkuType")
'Get Serial
SKU = Batch.GetXgridFieldValue (numDoc, GridPanel, row, "SKU")
'Write to file here
inputFile.WriteLine(_
Chr(34) & SKU & Chr(34) & "," &_
Chr(34) & StationTime & Chr(34)& "," &_
Chr(34) & SkuType & Chr(34) )
Next 'loop Grid
inputFile.Close
Next 'Loop Batch
'Now cleanup
inputFile.Close
Set inputFile = Nothing
Set fso = Nothing

Related

WSCRIPT error: subscript out of range

I have a VBScript which tries to merge data from two CSV files based upon common field. When I am running my script, then I get an error on Line 5 char 1:
Subscript out of range.
The two files which I am trying to merge based upon the value of a common field between the two are in the same folder where script is also placed.
My code is :
'Instatiate FSO.
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Open the CSV file for reading. The file is in the same folder as the script and named csv_sample.csv.
Wscript.Echo "Path " & objFSO.GetParentFolderName(WScript.ScriptFullName)
'Open the store locations file first
Set brandCSV = objFSO.OpenTextFile(objFSO.GetParentFolderName(WScript.ScriptFullName) & "\" & "SizeGuideLookup_test.csv",1,False)
'Set header status to account for the first line as the column headers.
IsHeader = True
'Initialize the var for the output string.
OutRecord = ""
'Read each line of the file.
Wscript.Echo "Starting Brand File loop"
Do Until brandCSV.AtEndOfStream
brandLine = brandCSV.ReadLine
If IsHeader Then
OutTxt = "PIM Size Type,PIM Identifier,Structure Group,PIM Size Groupd Value Lookup,Size Group To Upload" & vbCrLf
IsHeader = False
Else
'parse brandrecord and get brand id
brandLineArray=Split(brandLine,";")
brandBrandId = brandLineArray(0)
' loop through Store Location file and get matching data
foundLocation=false
Set storeLocCSV = objFSO.OpenTextFile(objFSO.GetParentFolderName(WScript.ScriptFullName) & "\" & "SizeGuideMapping.csv",1,False)
Do Until storeLocCSV.AtEndOfStream
outLine=""
storeLine=storeLocCSV.ReadLine
storeLineArray=Split(storeLine,";")
storeBrandId = storeLineArray(0)
'if the brand IDs match, append the brand data to the end of the store data.
If brandBrandId = storeBrandId Then
' match found - ouptut data (specific fields from store Line + brand line)
outLine = outLine & brandLineArray(0)
outLine = outLine & "," & brandLineArray(1)
outLine = outLine & "," & brandLineArray(2)
outLine = outLine & "," & storeLineArray(1)
outLine = outLine & "," & storeLineArray(1)
foundLocation=true
'append created line to end of output text data
OutTxt = OutTxt & outLine & vbCrLf
End If
Loop
'Close the store location file.
storeLocCSV.close
'if we havent found the data, add empty fields to end of line
if foundLocation=false Then
' no locations for this brand - create brand-only record
outLine = brandLineArray(0) & ",,,"
outLine = outLine & "," & brandLineArray(1)
outLine = outLine & "," & brandLineArray(2)
outLine = outLine & "," & storeLineArray(1)
outLine = outLine & "," & storeLineArray(1)
'append created line to end of output text data
OutTxt = OutTxt & outLine & vbCrLf
end if
End If
Loop
'Close the brand file.
brandCSV.Close
'Open the output file for writing.
Set objOutCSV = objFSO.CreateTextFile(objFSO.GetParentFolderName(WScript.ScriptFullName) & "\" & "brandfile.csv",True)
'Write the var OutTxt to the file overwriting existing contents.
objOutCSV.Write OutTxt
'Close the file.
objOutCSV.Close
Set objFSO = Nothing
The line the error occurs on doesn't tally with the source you have provided (guessing that's not all of it) but it's likely the
WScript.Arguments(0)
causing the Subscript out of range error as the script is expecting an argument (WshUnnamed or WshNamed objects) being passed but there doesn't appear to be one at index 0 in the WshArguments collection object.
If the argument is indeed a file path, then you want to be calling it with either wscript.exe or cscript.exe something like this (at a Command Prompt);
cscript.exe /nologo "yourscript.vbs" "C:\some\file\path"
Then when
WScript.Echo "Path " & WScript.Arguments(0)
is called you should get (based off this example)
C:\some\file\path
Useful Links
Arguments Property (WScript Object)
WshArguments Object
If i am right, you want to get the folder path of the executing script.
For this purpose, you don't want to use WScript.Arguments(0) but FSO.GetParentFolderName(WScript.ScriptFullName).

Invalid Parameter error when setting a registry multiStringValue using vbscript

This is in reference to an existing question I previously asked but same conditions are not working when doing another sub. All variables below are defined correct and as strings. I am getting error when setting values on this line:
objReg.setMultiStringValue HKCU,IE_Main,mStrSecStartPages,allURLs
The code is below;
return = objReg.getMultiStringValue (HKCU,IE_Main,mStrSecStartPages,multiStringValues)
'If values found in Secondary Start Pages
If return=0 Then
ReDim allURLs(0)
'Read all values and only store non intranet values to array
For Each itemname In multiStringValues
If itemname <> strFunctionIntranet1 And itemname <> strFunctionIntranet2 And itemname <> strFunctionIntranet3 And itemname <> strFunctionIntranet4 Then
ReDim Preserve allURLs(UBound(allURLs)+1)
allURLs(UBound(allURLs)) = itemname
End If
Next
'Remove current key holding existing values
objReg.DeleteValue HKCU,IE_Main,mStrSecStartPages
'Set new values based on values read and user's intranet
if UBound(allURLs)>=0 Then
wscript.echo "in setting"
objReg.setMultiStringValue HKCU,IE_Main,mStrSecStartPages,allURLs
End If
wscript.echo "out setting"
End If
Problem is even if there isn't any values in the REG_MULTI_SZ value you will still get an empty Array returned, which means when you then loop through the array and dynamically expand it using
ReDim Preserve allURLs(UBound(allURLs)+1)
You will always have a blank element in the first position in the array which when passed to
objReg.setMultiStringValue HKCU,IE_Main,mStrSecStartPages,allURLs
if it isn't the only element you will get
SWbemObjectEx: Invalid parameter
Here is some testing I did to prove this
Option Explicit
Const HKEY_LOCAL_MACHINE = &H80000002
Dim oReg
Dim strKeyPath, strValueName, arrStringValues
Dim strComputer: strComputer = "."
Set oReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")
strKeyPath = "SOFTWARE\Microsoft\Internet Explorer\Main"
strValueName = "Default_Secondary_Page_URL"
Dim rtn
rtn = oReg.GetMultiStringValue(HKEY_LOCAL_MACHINE, strKeyPath, strValueName, arrStringValues)
Dim i
If rtn = 0 Then
If IsArray(arrStringValues) Then
For i = 0 To UBound(arrStringValues)
WScript.Echo "arrStringValues(" & i & ") = " & arrStringValues(i)
Next
Else
WScript.Echo "Not Array"
End If
Else
WScript.Echo "Failed to GetMultiStringValue - Return (" & rtn & ")"
End If
rtn = oReg.SetMultiStringValue(HKEY_LOCAL_MACHINE,strKeyPath,strValueName,arrStringValues)
WScript.Echo "SetMultiStringValue - Return (" & rtn & ")"
Output:
arrStringValues(0) =
SetMultiStringValue - Return (0)
Adding the following line to create two blank elements under the IsArray() check
ReDim Preserve arrStringValues(UBound(arrStringValues) + 1)
Output:
arrStringValues(0) =
arrStringValues(1) =
test36.vbs(31, 1) SWbemObjectEx: Invalid parameter
So SetMultiSringValue() will accept an Array that contains an empty element if it is the only element in the array, the minute you try to add more you will get the error as described above.
In relation to the original code
To stop creating the extra blank element at the beginning you could switch to using a For instead of a For Each that way you can tell the loop to only call
ReDim Preserve allURLs(UBound(allURLs)+1)
when the index of the Array is greater then 0
For i = 0 To UBound(multiStringValues)
itemname = multiStringValues(i)
If itemname <> strFunctionIntranet1 And itemname <> strFunctionIntranet2 And itemname <> strFunctionIntranet3 And itemname <> strFunctionIntranet4 Then
'Only expand if we have more then 1 value in multiStringValues
If i > 0 Then ReDim Preserve allURLs(UBound(allURLs)+1)
allURLs(UBound(allURLs)) = itemname
End If
Next
You can do this with a For Each of course but you would have to track the Array index manually using another variable, which in my opinion when you have For already seems pointless.

VBScript - Retrieving a user's nested groups and getting rid of repetitions

For my work, I have to write a script in VBScript that retrieves a list of ALL groups a user belongs to, including nested groups, and take out nested groups that would be repeated throughout the list (as well as indent nested groups, further indent nested groups of nested groups, etc.)
I found a script that fetches the entire list of groups a user belongs to by Monimoy Sanyal on gallery.technet.microsoft.com, and tried to adapt it to my needs. Here is the script as edited by me:
Option Explicit
Const ForReading = 1, ForWriting = 2, ForAppend = 8
Dim ObjUser, ObjRootDSE, ObjConn, ObjRS
Dim GroupCollection, ObjGroup
Dim StrUserName, StrDomName, StrSQL
Dim GroupsList
Dim WriteFile
GroupsList = ""
Set ObjRootDSE = GetObject("LDAP://RootDSE")
StrDomName = Trim(ObjRootDSE.Get("DefaultNamingContext"))
Set ObjRootDSE = Nothing
StrUserName = InputBox("Enter user login", "Info needed", "")
StrSQL = "Select ADsPath From 'LDAP://" & StrDomName & "' Where ObjectCategory = 'User' AND SAMAccountName = '" & StrUserName & "'"
Set ObjConn = CreateObject("ADODB.Connection")
ObjConn.Provider = "ADsDSOObject": ObjConn.Open "Active Directory Provider"
Set ObjRS = CreateObject("ADODB.Recordset")
ObjRS.Open StrSQL, ObjConn
If Not ObjRS.EOF Then
ObjRS.MoveLast: ObjRS.MoveFirst
Set ObjUser = GetObject (Trim(ObjRS.Fields("ADsPath").Value))
Set GroupCollection = ObjUser.Groups
WScript.Echo "Looking for groups " & StrUserName & " is member of. This may take some time..."
'Groups with direct membership, and calling recursive function for nested groups
For Each ObjGroup In GroupCollection
GroupsList = GroupsList + ObjGroup.CN + VbCrLf
CheckForNestedGroup ObjGroup
Next
Set ObjGroup = Nothing: Set GroupCollection = Nothing: Set ObjUser = Nothing
'Writing list in a file named Groups <username>.txt
Set WriteFile = WScript.CreateObject("WScript.Shell")
Dim fso, f
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile("Groups " & StrUserName & ".txt", ForWriting,true)
f.write(GroupsList)
f.Close
WScript.Echo "You can find the list in the Groups " &StrUserName & ".txt file that has just been created."
Else
WScript.Echo "Couldn't find user " & StrUserName & " in AD."
End If
ObjRS.Close: Set ObjRS = Nothing
ObjConn.Close: Set ObjConn = Nothing
'Recursive fucntion
Private Sub CheckForNestedGroup(ObjThisGroupNestingCheck)
On Error Resume Next
Dim AllMembersCollection, StrMember, StrADsPath, ObjThisIsNestedGroup
AllMembersCollection = ObjThisGroupNestingCheck.GetEx("MemberOf")
For Each StrMember in AllMembersCollection
StrADsPath = "LDAP://" & StrMember
Set ObjThisIsNestedGroup = GetObject(StrADsPath)
'Not include a group in the list if it is already in the list (does not work for some reason?)
If InStr(GroupsList, ObjThisIsNestedGroup.CN) = 0 Then
GroupsList = GroupsList + vbTab + ObjThisIsNestedGroup.CN + VbCrLf
End If
'Recursion to look for nested groups and nested groups of nested groups and nested groups of nested groups of nested groups and...
CheckForNestedGroup ObjThisIsNestedGroup
Next
Set ObjThisIsNestedGroup = Nothing: Set StrMember = Nothing: Set AllMembersCollection = Nothing
End Sub
Rather than display a popup for EACH group found like the original script did, I store the entire list in a String (GroupsList = GroupsList + ObjGroup.CN + VbCrLf for direct groups, GroupsList = GroupsList + vbTab + ObjThisIsNestedGroup.CN + VbCrLf for nested groups in the recursive function,) and once the script is done looking for groups, it saves the String in a file. (f.write(GroupsList))
My problem is, despite the If "InStr(GroupsList, ObjThisIsNestedGroup.CN) = 0 in the recursive function, I still find myself with tons of repetitions throughout the results (our AD is kind of bloated with groups, it is a huge structure with many nested groups and nested groups in other nested groups, etc.) and the check doesn't seem to notice that ObjThisIsNestedGroup.CN is already found in GroupsList.
And I have no idea how to implement the indentation properly.
Any ideas? I'm rather new at scripting, so forgive me if the answer is obvious.
Add the groups as keys to a Dictionary, so the list contains only unique names, and Join() the Keys array for output:
Set GroupsList = CreateObject("Scripting.Dictionary")
GroupsList.CompareMode = vbTextCompare 'make keys case-insensitive
...
GroupsList(ObjGroup.CN) = True
...
f.Write Join(GroupsList.Keys, vbNewLine)
I found the solution for both problems. Well, the first problem I'm not sure how I fixed since I only reverted the code after making a modification and then it was magically working.
For the increasing indentation, I declared a global variable named RecurCount that I increment every time I call the recursive procedure, and decrease after the procedure. Then, within the procedure, I added a For i = 0 to RecurCount that adds a varying number of vbTabs depending on RecurCount.
Here's the working procedure:
Private Sub CheckForNestedGroup(ObjThisGroupNestingCheck)
On Error Resume Next
Dim AllMembersCollection, StrMember, StrADsPath, ObjThisIsNestedGroup, TabAdd, i
AllMembersCollection = ObjThisGroupNestingCheck.GetEx("MemberOf")
For Each StrMember in AllMembersCollection
If StrMember <> "" Then
StrADsPath = "LDAP://" & StrMember
Set ObjThisIsNestedGroup = GetObject(StrADsPath)
'If InStr(GroupsList, ObjThisIsNestedGroup.CN) = 0 Then (Uncomment this If and indent lines below to remove groups already in the list)
TabAdd = ""
For i = 0 to Recurcount
TabAdd = TabAdd & vbTab
Next
GroupsList = GroupsList & TabAdd & " " & ObjThisIsNestedGroup.CN & VbCrLf
'End If
'Recursion to include nested groups of nested groups
Recurcount = Recurcount + 1
CheckForNestedGroup ObjThisIsNestedGroup
Recurcount = Recurcount - 1
End If
Next
Set ObjThisIsNestedGroup = Nothing: Set StrMember = Nothing: Set AllMembersCollection = Nothing
End Sub
Don't forget to Dim Recurcount in the main script, and to make it 0 right before calling CheckForNestedGroup for the first time.

VBScript/SQL Formatting Issue

Okay so the script that I have written is almost fully functional but I would like to add two things to it. What it currently does is you type in a CallID number that is associated with other data on a database in SQL Server. When you type in the number into the msgbox it retrieves all the other columns that are associated with that particular number and outputs it to the command prompt and also outputs it to a text file on the hard drive. This is good, but the formatting is horrible. How would I go about improving the formatting of the file so it is more reading. Currently it is just a line with space seperating each piece of data. Also what I would like to be able to do is also have the name of each Column under each piece of data that is associated with that column. Any help would be appreciated. Here is my code with sensitive information omitted:
Dim strQuery
strQuery = InputBox("Enter CallID Please:")
Dim sServer
Dim sLogin
Dim sPwd
Dim sDb
Dim oCn
Dim oRs
Dim strFullQuery
Dim strfield
Const ForReading = 1
sServer = ""
sLogin = ""
sPwd = ""
sDb = ""
Set oCn = CreateObject( "ADODB.Connection" ) ' set oCn to create an object called ADODB.Connection
Set oRs = CreateObject( "ADODB.Recordset" ) ' set oRs to create an object called ADODB.Recordset
oCn.ConnectionString = "PROVIDER=SQLOLEDB" & _
";SERVER=" & sServer & _
";UID=" & sLogin & _
";PWD=" & sPwd & _
";DATABASE=" & sDb & " "
oCn.ConnectionTimeout=600
oCn.open 'Open the connection to the server
strFullQuery = "select * from dbo.CallLog where CallID=" + strQuery 'this is the SQL statement that runs a query on the DB
oRs.Open strFullQuery,oCn 'This opens the record set and has two parameters, strFullQuery and oCn
If oRs.EOF Then 'If open record set is at the end of file then...
wscript.echo "There are no records to retrieve; Check that you have the correct record number." 'echo that there is no records to retrieve.
End if
'if there are records then loop through the fields
oRs.MoveFirst 'move to the first object in the record set and set it as the current record
Do Until oRs.EOF ' Do while not open record set is not end of file
Set objFileSystem = WScript.CreateObject("Scripting.FileSystemObject") 'Set objFileSystem to create object Scripting.FileSystemObject
Set objOutPutFile = objFileSystem.CreateTextFile("c:\test.txt", True) 'Set objOutPutFile to create object objFileSystem.CreateTextFile
objOutPutFile.WriteLine strColumnNames
strfield = oRs.GetString
if strfield <> "" then 'If strfield doesn't equal "" then
WScript.echo strfield
objOutPutFile.WriteLine strfield &"|"
'objFileSystem.close
objOutPutFile.close
end If
'oRs.MoveNext 'Move to the next object in the record set
Loop
oCn.Close
You can add space to make fixed-widths. Let's say you know that every field will be 20 characters or less:
objOutPutFile.WriteLine strfield & String(20-Len(strfield)," ") & "|"

Lookup function in VB6

I have a table in an excel file called " Employee_States_File".This table contains two columns Name and States. Both columns are filled with data. The province table contains abbreviation of the States such as " NE, WA" and so. I have another excel file called " States_File" which contains a table that has two columns: Abbreviation and FullStateName. This table is considered to be a lookup table to look for the full state names based on the abbreviation. Now, I want to write a code in VB6 so that when the user click a button, All the abbreviation names in the table of the excel file " Employee_States" are changes into the full state names based on the lookup table of the excel sheet " States_File".
does it make sense?
Please help,
If you are allowed to convert them to csv files (comma delimited text files) you can use the Jet Database engine and do normal SQL joins.
I use this to setup the connection.
Public Function OpenTextConnection(ByVal FileName As String) As Connection
Dim FSO As FileSystemObject
Dim DBFolder As String
Dim TS As TextStream
Set FSO = New FileSystemObject
DBFolder = FSO.GetParentFolderName(FileName)
If FSO.FileExists(FSO.BuildPath(DBFolder, "Schema.ini")) Then
FSO.DeleteFile (FSO.BuildPath(DBFolder, "Schema.ini"))
End If
Set TS = FSO.CreateTextFile(FSO.BuildPath(DBFolder, "Schema.ini"))
TS.WriteLine "[" & FSO.GetFileName(FileName) & "]"
TS.WriteLine "Format=CSVDelimited"
TS.WriteLine "ColNameHeader = True"
TS.WriteLine "MaxScanRows = 0"
TS.Close
Set OpenTextConnection = New Connection
If FSO.FolderExists(DBFolder) Then
OpenTextConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & DBFolder & ";Extended Properties=""text;HDR=Yes;FMT=Delimited;"";"
Else
MsgBox DBFolder & " Does not exists.", vbExclamation
End If
End Function
Each file is a table in the connection and you can do SQL joins. Note the example is just a simply open up a table. You can use any valid SQL Syntax.
Dim DB1 As Connection
Dim TB As Recordset
Dim FSO As FileSystemObject
Dim tImport As New DBImportList
Set FSO = New FileSystemObject
Set tImport = New DBImportList
If FSO.FileExists(FileName) Then
Set DB1 = OpenTextConnection(FileName)
Set TB = New Recordset
TB.Open "SELECT * FROM [" & FSO.GetFile(FileName).Name & "]", DB1, adOpenKeyset, adLockOptimistic, adCmdText
End If

Resources