vb6 when For Each loop HOW can i assign variables - for-loop

The problem I am having is I am requesting a WMI query in VB 6 for Modem Names & Ports
I have a FOR EACH LOOP, and there is more than 1 value for each (2 Ports show, so I have 2 values for each). How can I assign a variable so I can assign it to a Label or TextBox?
I would like a VB 6 code sample of how to assign a variable through the loop and how to call the variable?
This is my code (when I use MsgBox I can see it, it just pops up twice separately, but I want variables so I can assign them)
For Each objItem In colItems
MsgBox ("Test -" & objItem.Name)
Next
I tried this, and I get a number, but I don't know how to reference it
For Each objItem In colItems
Dim myCount
myCount = myCount + 1
Debug.Print objItem.Name & myCount '** i just tested with Debug.Print
Next
Form1.TextBox1.Text = myCount(1) '** THIS DOES NOT WORK
Form1.TextBox2.Text = myCount(2)
How Can I assign objItem.Name (it brings back 2 different objects)? This is what I get:
1SAMSUNG Mobile Modem #2
2SAMSUNG Mobile Modem Diagnostic Serial Port (WDM) (COM1)
(the 1 & 2 are from myCount)
Without using myCount, I just want to assign each value its own variable.

Assuming you have 100 or less objects, with each object having 2 values, here is one way to store a pair of values into a 2 dimensional array:
Dim myVar(100,2) As String
Dim myCount as Integer
myCount = 0
For Each objItem In colItems
If myCount Mod 2 = 0 Then
'read the first value
myVar(myCount,1) = objItem.Name
Else
'read the second value then move to the next object
myVar(myCount,2) = objItem.Name
myCount = myCount + 1
End If
Next
'Now if you want to print the value of the fifth object:
MsgBox("(Object #5) has first value: " & myVar(5,1) )
MsgBox("And the second value is: " & myVar(5,2) )

From your description I assume that the .Name property contains several fields you want to store separately?
I don't know how the fields in .Name are separated, so in the example below I just consider them space delimited:
Option Explicit
Private Type ModemData
strField() As String
End Type
Private mudtModems() As ModemData
Private Sub ReadModems()
Dim intCount As Integer
Dim strName As String
ReDim mudtModems(31) As ModemData
intCount = 0
For Each objItem In colItems
strName = objItem.Name
mudtModems(intCount).strField = Split(strName, " ")
intCount = intCount + 1
Next
ReDim Preserve mudtModems(intCount - 1) As ModemData
End Sub
Initially it creates an array to hold 32 modems, and in the end redims the array to the actual size
The strField array in each udtModem will have various lengths, depending on the number of fields in .Name
You will probably need another routine to split the fields of .Name correctly, use that routine instead of Split(strName, " ")

Actually, you already have your data in a variable. That variable is named colItems.
colItems is a variable of type Collection. You can read more about collections on MSDN.
If you know that your collection contains 2 items and your collection is 1-based, you can use your collection like this:
myTextbox1.Text = colItems(1).Name
myTextbox2.Text = colItems(2).Name
or, if you want to assign them to variables:
Dim myString1 as String
Dim myString2 as String
myString1 = colItems(1).Name
myString2 = colItems(2).Name
The difficult part is that you rarely know how many items your collection will contain. Usually, the developer of the API you are using is giving you a collection because there is no way of knowing how many elements the function will return. In such cases, a Collection is a good fit.
When given a collection as a return value from a function, displaying it in a couple of textboxes is rarely a sufficient way of handling the data. A listbox of some kind is usually a better fit. If there is a good reason for using Textbox, then a control array of textboxes is a possible solution.

Related

Use VBScript to VLOOKUP values, or evaluate formula?

For processing orders we're using VBScripts to import them into accounting software. There are several suppliers, each with their own file format, mostly CSV and XML. The first step is to extract all the order lines (custom function per supplier), do some additional processing and then write it to the database, which is the same for all suppliers.
One new supplier uses Excel files with all the order lines in one sheet, except for the corresponding VAT percentage value which are available in another sheet. The VAT percentage per item can be looked up using the itemcode from the order sheet.
The company only has LibreOffice Calc and I understand you could do something like this in macro. However, it is a fully automated process and every other file is already handled by VBScript so I'd rather not make an exception or handle just this one order type manually (opening Calc and running the macro). So it has to be VBS and LibreOffice in this case.
Here is the VBScript code I have so far:
Option Explicit
' variables
Dim oSM, oDesk
Dim sFilename
Dim oDoc
Dim oSheet
Dim iLine
Dim sCode, iCount, sDesc, fCost, Perc
Set oSM = WScript.CreateObject("com.sun.star.ServiceManager")
Set oDesk = oSM.createInstance("com.sun.star.frame.Desktop")
sFilename = "file:///C:/orders/import/supplier_orderlist_08-01-2019.xls"
set oDoc = oDesk.loadComponentFromURL( sFilename, "_blank", 0, Array() )
set oSheet = oDoc.getSheets().getByName("Orderlist")
For iLine = 11 to 12 ' testing first 2 lines
sCode = oSheet.getCellByPosition(1, iLine).getString()
iCount = oSheet.getCellByPosition(2, iLine).getString()
sDesc = oSheet.getCellByPosition(5, iLine).getString()
fCost = oSheet.getCellByPosition(8, iLine).getString()
'lookup doesn't work
Perc = Macro_VLOOKUP(sCode, oDoc)
WScript.Echo sCode & " - " & iCount & "x - " & sDesc & " => " & fCost & ", " & Perc & "%"
Next 'iLine
WScript.Quit 1
Function Macro_VLOOKUP(SearchValue, oDocGlob)
Dim oSheetLook, CellRange
Dim Column, Mode, svc, arg, Value
Set oSheetLook = oDocGlob.getSheets().getByName("Itemlisttotal")
Set CellRange = oSheetLook.getCellRangeByName("A1:B10000")
Column = 1
Mode = 0
svc = createUnoService("com.sun.star.sheet.FunctionAccess") '<- error: variable not defined
arg = Array(SearchValue, CellRange, Column, Mode)
Value = svc.callFunction("VLOOKUP", arg)
Macro_VLOOKUP = Value
End Function
It gives an error on the line with createUnoService:
Variable not defined 'createUnoService'
which is probably a LibreOffice Basic function and needs to be translated to the VBScript equivalent. There isn't much documentation or examples on this, so I can only guess, but Set svc = WScript.CreateObject("com.sun.star.sheet.FunctionAccess") also doesn't work and gives a "class name not found" error.
Is it possible to do a VLOOKUP (or something similar) from VBScript in LibreOffice Calc?
Or is there a way to evaluate a cell formula from a string at runtime?

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.

add multiple multi string values to registry using array

Code is cleaned and changed from previous post since old logics had various errors that have been corrected and narrowed down to one error in one condition that I cant find an answer to. Currently getting error when my url is being read as only value and throwing Subscript Out of range error even though array is initialized. Other conditions when user has preset items or no key at all works perfectly. Thanks.
option explicit
'on error resume next
Dim ObjName,oADSysInfo,strComputer
Dim objReg,IE_Main,mstrValName,strFunctionIntranet,strNYHomepage,multiStringValues(),allURLs(),itemname,a,return
Set oADSysInfo = CreateObject("ADSystemInfo")
Set ObjName = GetObject("LDAP://" & oADSysInfo.UserName)
strComputer = "."
Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
Const HKCU = &H80000001
IE_Main = "Software\Microsoft\Internet Explorer\Main"
mstrValName = "Secondary Start Pages"
strNYHomepage = "http://www.google.com"
strFunctionIntranet = "www.mycompany.com"
SetHomePage
Sub SetHomepage
objReg.setStringValue HKCU,IE_Main,"Start Page",strNYHomepage
'Reading MultiStringValue of "Secondary Start Pages" for HKCU and continuing if it has something preset.
return = objReg.getMultiStringValue (HKCU,IE_Main,mstrValName,multiStringValues)
If return=0 Then
a=0
'Reading all items currently set to make sure users retain their existing URLs.
For Each itemname In multiStringValues
'Only continue if any of the existing URLs DO NOT MATCH what we are enforcing as the URL.
If itemname <> strFunctionIntranet Then
WScript.Echo itemname
WScript.Echo "itemname is NOT equal intranet"
a = a + 1
ReDim Preserve allURLs(a)
allURLs(a) = itemname
'a = a + 1
End If
Next
objReg.DeleteValue HKCU,IE_Main,mstrValName
'Enforce our URL to always be the first item.
allURLs(0)=strFunctionIntranet
'Set the new MultiStringValue registry key back.
objReg.setMultiStringValue HKCU,IE_Main,mstrValName,allURLs
WScript.echo "finished setting all secondary tabs... "
Else
strFunctionIntranet = Array(strFunctionIntranet)
objReg.setMultiStringValue HKCU,IE_Main,mstrValName,strFunctionIntranet
End If
End Sub
Wscript.Quit
Your array contains an empty element, because you create it one field too big.
Change this line:
ReDim Preserve allURLs(a+1)
into this:
ReDim Preserve allURLs(a)

Output array to MsgBox

I do PowerShell not VBScript, so I am a little lost. I am trying to list all mapped drives (drive letter and share path) in a MsgBox. I get a type mismatch error when running the script. If I change "Dim myArray()" to "Dim myArray" I get only one item from the variable.
Set objNetwork = WScript.CreateObject("WScript.Network")
Set colDrives = objNetwork.EnumNetworkDrives
Dim myArray()
For i = 0 to colDrives.Count-1 Step 2
myArray = colDrives.Item(i) & vbTab & colDrives.Item (i + 1)
Next
MsgBox(myArray)
How can I get the data saved to an array, then output to a MsgBox?
The reason why your code doesn't work is because you're creating fixed-size array without an actual size (Dim myArray()), and then try to assign values to that array. In VBScript you must assign values to array positions (myArray(pos) = val), and you cannot append to the built-in arrays (at least not without some additional work).
The most straightforward approach in your case would be the method #Bond suggested. However, you can do this with arrays if you want. You just need a resizable array like this:
ReDim myArray(-1) 'empty array
For i = 0 to colDrives.Count-1 Step 2
ReDim Preserve myArray(UBound(myArray)+1)
myArray(UBound(myArray)) = colDrives.Item(i) & vbTab & colDrives.Item(i+1)
Next
MsgBox Join(myArray, vbNewLine)
or (using an ArrayList), like this:
Set myArray = CreateObject("System.Collections.ArrayList")
For i = 0 to colDrives.Count-1 Step 2
myArray.Add colDrives.Item(i) & vbTab & colDrives.Item(i+1)
Next
MsgBox Join(myArray.ToArray, vbNewLine)
Since the size of the array can already be determined before entering the loop you could also dimension the array with the proper size right away to avoid repeated redimensioning (which tends to perform poorly for VBScript built-in arrays):
ReDim myArray(colDrives.Count \ 2 - 1)
For i = 0 to colDrives.Count-1 Step 2
myArray(i\2) = colDrives.Item(i) & vbTab & colDrives.Item(i+1)
Next
MsgBox Join(myArray, vbNewLine)
Another option would be using a Dictionary:
Set myArray = CreateObject("Scripting.Dictionary")
For i = 0 to colDrives.Count-1 Step 2
myArray(colDrives.Item(i)) = colDrives.Item(i) & vbTab & colDrives.Item(i+1)
Next
MsgBox Join(myArray.Items, vbNewLine)
You can use a string and keep appending (&) to it.
Dim s
For i = 0 To colDrives.Count-1 Step 2
s = s & colDrives.Item(i) & vbTab & colDrives.Item (i + 1) & vbCrLf
Next
MsgBox s

Algorithm for filling in numbers in series

I have a Recordset obtained by the following query:
SELECT DISTINCT [Number] FROM NUMBERS WHERE CODE = 7 ORDER BY [Number]
The Recordset will therefore be a list of ordered numbers, eg. [6,14,37,59,81,145]
I would like to generate a long string of numbers made up of only 0s and 1s, where it is all 0s except in positions designated by the numbers in the recordset: eg. 6,14,37,59, etc.
The result would therefore look something like:
000000000000000000100000000000000000000001000000000000000000001000000 etc
Assuming that rs is the Recordset, I have the following code so far. Would this work?
intLower = 1
While Not (rs.BOF Or rs.EOF)
intUpper = rs!Number
For intSlot = intLower To intUpper
strOutput = strOutput & IIf(rs!Number = intSlot, 1, 0)
Next intSlot
rs.moveNext
intLower = intUpper + 1
Wend
Note: I realise this is similar to an earlier question of mine, but I am now asking how to do this when the numbers are contained in a Recordset. Also note that I do not want to use a function that converts a recordset to an array, because I am using DAO and apparently the GetRows is problematic.
How about;
dim value as long
dim result as String
do while not rs.EOF
value = rs!Number
If (value > Len(result)) then result = result & String$(value - Len(result), "0")
Mid$(result, value, 1) = "1"
rs.moveNext
loop
msgbox result
Edit as they are sorted;
dim result as String
do while not rs.EOF
result = result & String$(rs!Number - Len(result) - 1, "0") & "1"
rs.moveNext
loop
msgbox result

Resources