The necessity of "Exit Function" - vbscript

I am new to VBScript, and I learned that VBScript does not have return and that to return a value, you assign the value to the name of the procedure.
When I was doing research on how to return a value, I found two different programs that both return a value, and I am not sure what the difference is.
Function Test1()
Dim value
'Do something'
If value < 10 Then
Test1 = value * 2
Else
Test1 = value
End If
End Function
Function Test2()
Dim value
'Do something'
If value < 10 Then
Test2 = value * 2
Exit Function
Else
Test2 = value
Exit Function
End If
End Function
It seems like Exit Function exits the procedure immediately when this program comes to this line, but what is the necessity of this line of code?
I have been learning other major programming languages such as C#, Java, etc, and in those programming languages, once the program comes to the line return or return something, the program exits that function/method even if there is mode code after that.
Does this mean, in VBScript, assigning a value to the name of its own procedure serves as return but it still keeps going without exiting until the end of the procedure unless you use Exit Function?

VBScript needs the Exit Function command if you want to instruct the function to return IMMEDIATELY instead of continuing the function. To assign the return value in VBScript, you use FUNCTIONNAME=VALUE_TO_RETURN. But in VBScript, that type of statement DOES NOT EXIT THE FUNCTION (although in C-like languages the assignment to the return value (return X;) exits the function immediately.)
For example, say you're trying to generate a filename that doesn't exist yet.
Function GenName( ROOT, file )
targetPath = ROOT & "\" & file.Name
If Not ofso.FileExists( targetPath ) Then
' ASSIGN RETURN VALUE THEN..
GenName = targetPath
Exit Function ' __GET OUT OF THE FUNCTION__
' If you neglect EXIT FUNCTION here, then
' THIS FUNCTION WILL CONTINUE RUNNING TO THE END
' OF IT, even though YOU'VE ALREADY ASSIGNED THE FUNCTION
' A RETURN VALUE
End If
For num = 1 To 10000
' append numbers until you find one that doesn't exist or quit
targetPath = ROOT & "\" & ofso.GetBaseName( file ) & "-" & num & "." & ofso.GetExtensionName( file )
If Not ofso.FileExists( targetPath ) Then
GenName = targetPath ' WANTS to be "return targetPath"
' but you can't do that in VBSCRIPT. So ASSIGN VALUE to
' function name.. AND THEN..
Exit Function '' MUST EXIT FUNCTION otherwise function
' will just continue to run (for loop will keep executing)
' else can break loop with EXIT FOR
End If
Next
End Function

Specifying the function's return value and returning to the caller (via reaching the end of the function's body or an explicit statement) are clearly different things. Being able to express both distinctly is a pro:
>> Function preinc(ByRef i) : i = i + 1 : preinc = i : End Function
>> Function postinc(ByRef i) : postinc = i : i = i + 1 : End Function
>> i = 0
>> WScript.Echo i
>> WScript.Echo preinc(i), i
>> WScript.Echo postinc(i), i
>>
0
1 1
1 2
Languages that combine setting the value and leaving the function (return(x), returning the value of the 'last' expression) don't let you do work or cleanup (cf. here) after determining the return value.

Related

QTP: Function returns empty string from excel/text file

The application converts Excel files to txt. I have to verify each line matches. Below is the function to verify that, but the problem is it sometimes returns empty string from txt file or excel file, while both files have text in those rows/lines.
I get file and folder names, as well as what excel sheet to use (as TabUse) from database
Function excelcomparison (ByRef ObjFolder, ByRef OrgFolder, ByRef originalfile, ByRef targetFile, ByRef TabUse)
print originalfile&":::"&TabUse&" -=VS=- "&targetFile
Dim fsox : Set fsox = CreateObject("Scripting.FileSystemObject")
Dim TargFileRead : Set TargFileRead = fsox.OpenTextFile(targetFile)
Dim OrgExcel : Set OrgExcel = CreateObject("Excel.Application")
'Application.DisplayAlerts = False
OrgExcel.Workbooks.Open(originalfile)
Set vSheet = OrgExcel.ActiveWorkbook.WorkSheets(TabUse)
For rc = 1 To vSheet.UsedRange.Rows.Count
For cc = 1 To vSheet.UsedRange.Columns.Count
vtext = (vSheet.cells(rc,cc))
If vstring="" Then
vstring=vtext
Else
vstring = vstring&vbTab&vtext
End If
Next
"Trim" any leading and trailing tabs:
Do
If Left(vstring , 1)=ChrW(9) Then
vstring = MID(vstring, 2)
Else
Exit Do
End If
Loop
Do
If RIGHT(vstring, 1)=ChrW(9) Then
vstring= REPLACE(RIGHT(vstring, 1),ChrW(9), ChrW(32))
vstring=Trim(vstring)
Else
Exit Do
End If
Loop
vstring = Trim(vstring)
Some cells are united in Excel and have height of two or more row. So, skip those excel rows and txt lines:
If len(vstring)>0 Then
TargFileText = TargFileRead.ReadLine
Do
If Left(TargFileText, 1)=ChrW(9) Then
TargFileText = MID(TargFileText, 2)
Else
Exit Do
End If
Loop
Do
If RIGHT(TargFileText, 1)=ChrW(9) Then
TargFileText = REPLACE(RIGHT(TargFileText, 1),ChrW(9), ChrW(32))
TargFileText=Trim(TargFileText)
Else
Exit Do
End If
Loop
TargFileStr = Trim(TargFileText)
If trim(vstring) = trim(TargFileStr) Then
' print "match"
Else
print "Not Match"
print "+"&trim(TargFileStr)
print "*"&trim(vstring)
End If
Else
print "Lenth=0"
End If
vstring = ""
vtext = ""
TargFileStr=""
Next
OrgExcel.ActiveWorkbook.Close
TargFileRead.Close
fsox = Nothing
TargFileRead = Nothing
vSheet = Nothing
OrgExcel = Nothing
End Function
Problem 1: It does not read some text or excel files, randomly (returns empty string from excel/text file)
Problem 2: It does not close opened Excel and they take huge memory (up to 50 files to be verified)
Question: What needs to be fixed?
I think the problem is arising when you are trying to remove the vbtab from the Right end side of the string in both the excel and the text file.
For Excel, you have used:
If RIGHT(vstring, 1)=ChrW(9) Then
vstring= REPLACE(RIGHT(vstring, 1),ChrW(9), ChrW(32)) 'This may be the source of your problem
vstring=Trim(vstring)
Else
Exit Do
End If
Explanation:
You are replacing ChrW(9) with chrw(32) in a string[RIGHT(vstring, 1)] which contains nothing but chrw(9). After you have done this replacement, you are assigning the result[which is chrw(32) or a space] to the variable vstring. After this line, you trim this variable due to which vstring=""
For Text file you have used:
If RIGHT(TargFileText, 1)=ChrW(9) Then
TargFileText = REPLACE(RIGHT(TargFileText, 1),ChrW(9), ChrW(32)) 'This may be the source of the problem
TargFileText=Trim(TargFileText)
Else
Exit Do
End If
Explanation:
You are replacing ChrW(9) with chrw(32) in a string[RIGHT(TargFileText, 1)] which contains nothing but chrw(9). After you have done this replacement, you are assigning the result[which is chrw(32) or a space] to the variable TargFileText. After this line, you trim this variable due to which TargFileText=""
SOLUTION:
In both the cases, you need to remove the tab from the right side just like the way you removed vbTab from the left end side of the string i.e, by using the MID function:
If RIGHT(vstring, 1)=ChrW(9) Then
vstring= mid(vstring,1,len(vstring)-1) 'If there is a tab in the right side of the string, just capture till second last character of the string thus excluding the vbTab.
Else
Exit Do
End If
Similarly, for the text file:
TargFileText= mid(TargFileText,1,len(TargFileText)-1)
Also, you are reading the text file only when the vstring is not blank. So, if the vstring is blank, the text file "pointer" remains at the same line where as excel row increments by 1. Due to this, you may have incorrect comparisons. To avoid this, you can use Skipline method in the else part as shown below:
If len(vstring)>0 Then
'----your code----
'...
'...
else
TargFileRead.Skipline 'so that it skips the line corresponding to the case when vstring is ""
'--remaining code---
End If
For closing the excel, use the Quit method of the Excel application.
OrgExcel.Quit

Getting "variable undefined" error when trying to reset the variable's value

I'm trying to run through a set of documents and reset the value of cur_pg, but get the following error:
Script Variable is undefined: 'cur_pg' ...
My code is such:
option explicit
'----------------- OnStartProcess -----------------
function OnStartProcess
Line.value = empty
Page.value = empty
cur_pg = 0
cur_line = 0
end function
''----------------- OnRecord -----------------
function OnRecord
dim s: s = this.GetLine()
dim pgn
pgn = this.GetPageNumber()
if cur_pg.value <> pgn then cur_line.value = 1
trg.Message s
trg.Message dictin.P1.value
if trg.strlib.InStr(s, Search.value )>0 then
Line.value = Line.value & cur_line.value & ","
Page.value = Page.value & this.GetPageNumber() & ","
end if
cur_line.value = cur_line.value + 1
end function
'----------------- OnFinishProcess -----------------
function OnFinishProcess
line.value = trg.strlib.Chop( line.value )
page.value = trg.strlib.Chop( page.value )
trg.Message line.value & " - " & page.value
end function
Gow do I define this variable?
You are getting this error because you have Option Explicit set on your code.
This causes VB to make sure that all variables you use throughout your code have been declared.
If you remove Option Explicit (or set Option Explicit Off) you will be able to use variables that have not been explicitly declared.
My personal preference is to always use explicit declaration though. If you wish to go down this route, simply ensure that you Dim your variables before using them.
For more information visit the MSDN article.

The statement contains one or more invalid function argument in QTP

I have wrote this below function. However, i keep getting invalid function argument in QTP.
The object is a link in a webtable and i keep getting this error message of "The statement contains one or more invalid function agrument."
Can someone help me please?
Function CheckData(Value_To_Match, Rpttext, ByRef Row_Num)
Dim tab_val, arr_val(2), rowNum
tab_val = Split(Value_To_Match,";")
arr_val(0) = Browser("xyz").Page("abc").WebTable("xsw").getColumnIndex(tab_val(0))
If UBound(tab_val ) > 0 Then
arr_val(1) = tab_val(1)
arr_val(2) = Browser("xyz").Page("abc").WebTable("xsw").getMatchingRow(arr_val)
Row_Num = arr_val(2)
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Verify"
Else
arr_val(1) = tab_val(0)
arr_val(2) = Browser("xyz").Page("abc").WebTable("xsw").getMatchingRow(arr_val)
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Verify"
arr_val(2) = Row_Num
Browser("xyz").Page("abc").WebTable("xsw").MatchVal arr_val, Rpttext, "Check"
End If
End Function
If Trim(Parameter("DraftsViewDraftIcon")) <> "" And _
LCase(Trim(Parameter("DraftsViewDraftIcon"))) <> "[null]" And _
LCase(Trim(Parameter("Wrapper_Mode"))) ="set" Then
Set ViewDraftLink = Browser("xyz").Page("abc").WebTable("xsw").ChildItem(Row_Num, 4, "Link", 0)
ViewDraftLink.click
End If
This function below for getting matching Row
Public Function getMatchingRow(byRef object, byVal arraydata)
Dim introw
Dim varComp
On Error Resume Next
If UBound(arraydata) = 0 Then
Reporter.ReportEvent micFail, "Array Data", "No Data found in input parameter Array"
getMatchingRow = 0
Exit Function
End If
For introw = 1 to object.RowCount
varComp = StrComp(Trim(object.GetCellData(introw, arrayData(0))), Trim(arrayData(1)), 0)
If varComp = 0 And Not(isNull(varComp)) Then
getMatchingRow = introw
Exit Function
End If
Next
getMatchingRow = 0
On Error Goto 0
If you want to register the getMatchingRow function as a method, the first formal argument must be a byVal parameter:
Public Function getMatchingRow(byVal object, byVal arraydata)
You are using a byRef argument:
Public Function getMatchingRow(byRef object, byVal arraydata)
This might cause the trouble you´re experiencing.
I don't have a chance to verify this right now, but please give it a try anyways.
(I have some doubt about this answer of mine, especially because you say the error line is the Set ViewDraftLink assignment, while it should happen upon the .getMatchingRow call. But you also mention the ViewDraftLink.Click and End If lines in that comment, so you are being unprecise in that regard anyway.
It really would help if you merge all this info into the question, further clean up the code in the question, and delete the then-obsolete comments.)

Object required of a parameter in VBScript?

I'm new to VBScript, and I have a function that allows me to pull synchronizing preferences from a preferences file, and it looks like this:
Function IsSync(SyncFolder)
If FS.FileExists(PrefFilePath) Then
Set objFile = FS.OpenTextFile(PrefFilePath, 1)
PrefLine = "start"
Do Until Prefline.Substring(0, SyncFolder.Length) = SyncFolder
PrefLine = objFile.Readline
Loop
If PrefLine.Substring(PrefLine.Length - 6) = "nosync" Then
IsSync = False
Else
IsSync = True
End If
Else
IsSync = True
End If
End Function
But when I try to run it, Windows throws me an error of "Object required: SyncFolder" whenever it gets to this function. Why is this? SyncFolder is just a parameter?
In VBScript, every variable has not some built-in methods. And if a variable has a property or method this means it's an Object. But your parameter does not seems like an object, this is why the error occurred.
So, there is no built-in methods such as SubString or another for the string variables in the VBScript.
Use Len function to get length of string instead of
.Length.
Use Mid, Left or Right functions which one if you need to istead of .SubString.
I guess you need to use -with order- Len, Left and Right functions in this case.
Consider this :
Function IsSync(SyncFolder)
If FS.FileExists(PrefFilePath) Then
Set objFile = FS.OpenTextFile(PrefFilePath, 1)
PrefLine = "start"
Do Until Left(Prefline, Len(SyncFolder)) = SyncFolder 'starts with SyncFolder
PrefLine = objFile.Readline
Loop
If Right(PrefLine, 5) = "nosync" Then 'ends with "nosync"
IsSync = False
Else
IsSync = True
End If
Else
IsSync = True
End If
End Function

Check if a record exists in a VB6 collection?

I've inherited a large VB6 app at my current workplace. I'm kinda learning VB6 on the job and there are a number of problems I'm having. The major issue at the moment is I can't figure out how to check if a key exists in a Collection object. Can anyone help?
My standard function is very simple. This will work regardless of the element type, since it doesn't bother doing any assignment, it merely executes the collection property get.
Public Function Exists(ByVal oCol As Collection, ByVal vKey As Variant) As Boolean
On Error Resume Next
oCol.Item vKey
Exists = (Err.Number = 0)
Err.Clear
End Function
#Mark Biek Your keyExists closely matches my standard Exists() function. To make the class more useful for COM-exposed collections and checking for numeric indexes, I'd recommend changing sKey and myCollection to not be typed. If the function is going to be used with a collection of objects, 'set' is required (on the line where val is set).
EDIT: It was bugging me that I've never noticed different requirements for an object-based and value-based Exists() function. I very rarely use collections for non-objects, but this seemed such a perfect bottleneck for a bug that would be so hard to track down when I needed to check for existence. Because error handling will fail if an error handler is already active, two functions are required to get a new error scope. Only the Exists() function need ever be called:
Public Function Exists(col, index) As Boolean
On Error GoTo ExistsTryNonObject
Dim o As Object
Set o = col(index)
Exists = True
Exit Function
ExistsTryNonObject:
Exists = ExistsNonObject(col, index)
End Function
Private Function ExistsNonObject(col, index) As Boolean
On Error GoTo ExistsNonObjectErrorHandler
Dim v As Variant
v = col(index)
ExistsNonObject = True
Exit Function
ExistsNonObjectErrorHandler:
ExistsNonObject = False
End Function
And to verify the functionality:
Public Sub TestExists()
Dim c As New Collection
Dim b As New Class1
c.Add "a string", "a"
c.Add b, "b"
Debug.Print "a", Exists(c, "a") ' True '
Debug.Print "b", Exists(c, "b") ' True '
Debug.Print "c", Exists(c, "c") ' False '
Debug.Print 1, Exists(c, 1) ' True '
Debug.Print 2, Exists(c, 2) ' True '
Debug.Print 3, Exists(c, 3) ' False '
End Sub
I've always done it with a function like this:
public function keyExists(myCollection as collection, sKey as string) as Boolean
on error goto handleerror:
dim val as variant
val = myCollection(sKey)
keyExists = true
exit sub
handleerror:
keyExists = false
end function
As pointed out by Thomas, you need to Set an object instead of Let. Here's a general function from my library that works for value and object types:
Public Function Exists(ByVal key As Variant, ByRef col As Collection) As Boolean
'Returns True if item with key exists in collection
On Error Resume Next
Const ERR_OBJECT_TYPE As Long = 438
Dim item As Variant
'Try reach item by key
item = col.item(key)
'If no error occurred, key exists
If Err.Number = 0 Then
Exists = True
'In cases where error 438 is thrown, it is likely that
'the item does exist, but is an object that cannot be Let
ElseIf Err.Number = ERR_OBJECT_TYPE Then
'Try reach object by key
Set item = col.item(key)
'If an object was found, the key exists
If Not item Is Nothing Then
Exists = True
End If
End If
Err.Clear
End Function
As also advised by Thomas, you can change the Collection type to Object to generalize this. The .Item(key) syntax is shared by most collection classes, so that might actually be useful.
EDIT Seems like I was beaten to the punch somewhat by Thomas himself. However for easier reuse I personally prefer a single function with no private dependencies.
Using the error handler to catch cases when the key does not exists in the Collection can make debugging with "break on all errors" option quite annoying. To avoid unwanted errors I quite often create a class which has the stored objects in a Collection and all keys in a Dictionary. Dictionary has exists(key) -function so I can call that before trying to get an object from the collection. You can only store strings in a Dictionary, so a Collection is still needed if you need to store objects.
The statement "error handling will fail if an error handler is already active" is only partly right.
You can have multiple error handlers within your routine.
So, one could accommodate the same functionality in only one function.
Just rewrite your code like this:
Public Function Exists(col, index) As Boolean
Dim v As Variant
TryObject:
On Error GoTo ExistsTryObject
Set v = col(index)
Exists = True
Exit Function
TryNonObject:
On Error GoTo ExistsTryNonObject
v = col(index)
Exists = True
Exit Function
ExistsTryObject:
' This will reset your Err Handler
Resume TryNonObject
ExistsTryNonObject:
Exists = False
End Function
However, if you were to only incorporate the code in the TryNonObject section of the routine, this would yield the same information.
It will succeed for both Objects, and non-objects.
It will speed up your code for non-objects, however, since you would only have to perform one single statement to assert that the item exists within the collection.
Better solution would be to write a TryGet function. A lot of the time you are going to be checking exists, and then getting the item. Save time by doing it at the same time.
public Function TryGet(key as string, col as collection) as Variant
on error goto errhandler
Set TryGet= col(key)
exit function
errhandler:
Set TryGet = nothing
end function
see
http://www.visualbasic.happycodings.com/Other/code10.html
the implementation here has the advantage of also optionally returning the found element, and works with object/native types (according to the comments).
reproduced here since the link is no longer available:
Determine if an item exists in a collection
The following code shows you how to determine if an item exists within a collection.
Option Explicit
'Purpose : Determines if an item already exists in a collection
'Inputs : oCollection The collection to test for the existance of the item
' vIndex The index of the item.
' [vItem] See Outputs
'Outputs : Returns True if the item already exists in the collection.
' [vItem] The value of the item, if it exists, else returns "empty".
'Notes :
'Example :
Function CollectionItemExists(vIndex As Variant, oCollection As Collection, Optional vItem As Variant) As Boolean
On Error GoTo ErrNotExist
'Clear output result
If IsObject(vItem) Then
Set vItem = Nothing
Else
vItem = Empty
End If
If VarType(vIndex) = vbString Then
'Test if item exists
If VarType(oCollection.Item(CStr(vIndex))) = vbObject Then
'Return an object
Set vItem = oCollection.Item(CStr(vIndex))
Else
'Return an standard variable
vItem = oCollection.Item(CStr(vIndex))
End If
Else
'Test if item exists
If VarType(oCollection.Item(Int(vIndex))) = vbObject Then
'Return an object
Set vItem = oCollection.Item(Int(vIndex))
Else
'Return an standard variable
vItem = oCollection.Item(Int(vIndex))
End If
End If
'Return success
CollectionItemExists = True
Exit Function
ErrNotExist:
CollectionItemExists = False
On Error GoTo 0
End Function
'Demonstration routine
Sub Test()
Dim oColl As New Collection, oValue As Variant
oColl.Add "red1", "KEYA"
oColl.Add "red2", "KEYB"
'Return the two items in the collection
Debug.Print CollectionItemExists("KEYA", oColl, oValue)
Debug.Print "Returned: " & oValue
Debug.Print "-----------"
Debug.Print CollectionItemExists(2, oColl, oValue)
Debug.Print "Returned: " & oValue
'Should fail
Debug.Print CollectionItemExists("KEYC", oColl, oValue)
Debug.Print "Returned: " & oValue
Set oColl = Nothing
End Sub
See more at: https://web.archive.org/web/20140723190623/http://visualbasic.happycodings.com/other/code10.html#sthash.MlGE42VM.dpuf
While looking for a function like this i designed it as following.
This should work with objects and non-objects without assigning new variables.
Public Function Exists(ByRef Col As Collection, ByVal Key) As Boolean
On Error GoTo KeyError
If Not Col(Key) Is Nothing Then
Exists = True
Else
Exists = False
End If
Exit Function
KeyError:
Err.Clear
Exists = False
End Function

Resources