How to define a Function in VBS with Default Valued parameter [duplicate] - vbscript

This question already has answers here:
Is there ways to create optional arguments to functions in vb script?
(5 answers)
Closed 2 years ago.
How to define a Function in VBS with Default Valued parameter?
I had tried many different ways, nothing seems working
Function Calculate(operator1 = 20, operator2 = 30)
' logic goes here
End Function
Function Calculate(Optional ByVal operator1 As Int = 20, Optional ByVal operator2 As Int = 30)
' logic goes here
End Function

I don't think VBScript supports 'Optional'. So you might have create a custom class like so:
class CustomClass
Dim operator1
Dim operator2
function Calculate
'set default values here
if IsEmpty(operator1) then operator1 = 20
if IsEmpty(operator2) then operator2 = 30
msgbox operator1
msgbox operator2
end function
end class
Dim myCustomClass : Set myCustomClass = new CustomClass
'override values here if required
myCustomClass.operator1 = 11
myCustomClass.operator2 = 12
myCustomClass.Calculate
Set myCustomClass = Nothing

Related

How to pass an array with fixed length strings to a function

I am trying to pass an array of fixed length strings to a function. However, the function will not know the size of the strings. The parameter should take any size strings. How would I go about this? Here is my code:
Public Type myRecord
names(15) As String * 250
End Type
Function def:
Function HasQuotes(textArr() As String) As String
End Function
Usage:
HasQuotes(rowRec.names)
where rowRec is a MyRecord.
When I run this code I get the following error Type Mismatch: array or user defined type expected. This is because the parameter is not defined as textArr() As String * 250. How can I make the parameter accept any string length?
You Have to follow Proper syntax you can Try Following updated Code
Private Type myRecord
names(15) As String * 250
End Type
Private Sub Command1_Click()
Dim rowRec As myRecord
rowRec.names(0) = ("Vaibhav")
Dim v As String
v = HasQuotes(rowRec.names)
End Sub
Function HasQuotes(ByRef textArr() As String * 250) As String
MsgBox textArr(0)
End Function
You are going to have trouble passing an array of fixed-length strings of any length. One simple solution is to convert them to an array of variable-length strings:
Private Sub Test()
Dim rowRec As myRecord
Dim names(15) As String
Dim i As Integer
Dim s As String
rowRec.names(0) = "Brian"
rowRec.names(15) = "Stafford"
'move the data to an array of variable-length strings
For i = 0 To 15
names(i) = rowRec.names(i)
Next
s = HasQuotes(names)
'move the data back to the original array if needed
For i = 0 To 15
rowRec.names(i) = names(i)
Next
End Sub

Difference between Function and Sub procedures in VB6

I cant understand about the difference between Function and Sub procedures in VB.NET.
The one with Function :
Private Function remainder (intno1 As Integer, intno2 As _ Integer) As Integer
Dim intresult As Integer
intresult = intno1 Mod intno2
remainder = intresult
End Function
and then by this way i call it :
Private Sub cmdrem _Click()
Dim intm As Integer, intn As Integer
Dim intmod As Integer
intm = Val (txtno1.Text)
intn = Val (txtno2.Text)
intmod = remainder (intm, intn)
lblres.text = "Answer Is = " + Str(intmod)
End Sub
would you please help me ?
some main differences between procedure and function in vb are that the function returns a value but sub procedure never returns value.
the return type must be defined in function declaration.
A function always is declared with keyword Function and a sub procedure is declared with keyword Sub.
Function ends with the keyword end function
and procedure ends with keyword end sub.

VBScript Function as Parameter, or similar Construct

I'm trying to put together tests in HP Unified Functional Testing
the way a programmer would.
For those unaware, the tool uses VBScript as its driver.
Because I want to use data from the same DataTable across multiple UFT actions
-- and because the Global table already has a different set of data on it
-- I want to retrieve data from an external file.
UFT happily supports this function.
My current plan is that, depending on which test I'm running,
I will iterate through only a range of rows in that table.
This is the script I've come up with:
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet "PersonFile.xlsx", 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstPersonIndex")
lastRow = Parameter("LastPersonIndex")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
' begin payload
MsgBox(DataTable.Value("LastName", dtLocalSheet))
' end payload
Next
End if
I don't want to have to repeat all this boilerplate
every time I want to use this pattern.
I'd really like to have something like:
In a Function Library:
sub LoopThroughSheetAnd(sheetFile, doThis)
' targets the local sheet, but
' not the same value as dtLocalSheet
Const sheetNum = 2
dim sheetRowCount
DataTable.ImportSheet sheetFile, 1, sheetNum
sheetRowCount = DataTable.GetSheet(sheetNum).GetRowCount
dim firstRow, lastRow
firstRow = Parameter("FirstRow")
lastRow = Parameter("LastRow")
If sheetRowCount < lastRow Then
lastRow = sheetRowCount
End If
If sheetRowCount >= firstRow Then
Dim i
For i = firstRow To lastRow
DataTable.SetCurrentRow i
call doThis()
Next
End if
end sub
In the original action...
sub Payload1()
MsgBox(DataTable.Value("LastName", dtLocalSheet))
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload1
In a separate action, 3 or 4 steps later...
sub Payload2()
' compare the data against another data source
end sub
LoopThroughSheetAnd "PersonFile.xlsx", Payload2
The above code doesn't work in VBScript.
A type mismatch error is thrown
as soon as we try to pass Payload1 as a parameter.
How could one reasonably pull this off in VBScript?
Bonus points if the answer also works in UFT.
You can pass functions as parameters with the GetRef() function. Here's a utility map function, like you'd find in JavaScript that accepts an array and calls a function for each element of the array:
Sub Map(a, f)
Dim i
For i = 0 To UBound(a)
' Call a function on each element and replace its value with the function return value
a(i) = f(a(i))
Next
End Sub
Map MyArray, GetRef("SomeFunc")
Now you could write SomeFunc so that it operates on a value and returns an updated value:
Function SomeFunc(i)
SomeFunc = i + 1
End Function
This works fine. map calls SomeFunc using the function "pointer" we passed to it.
You could do something similar with your LoopThroughStreetAnd function:
LoopThroughStreetAnd "PersonFile.xlsx", GetRef("Payload2")
The standard way of callbacks in VBScript uses GetRef, as in this demo.
When using objects, you can wrap a call to a method in an object, and then you can pass the object. (This is approximately what happens in other languages already, you just have to do it manually in VBScript.)
The only issue is that any method called this way has to be Public.
I would use a naming scheme of something like "Func1", "Func2", "Action1", "Action2", etc., depending on the arity of the functions and whether they return values or not.
Dim s : Set s = New Something : s.Run
Class Something
Public Sub HowToPassMe(pValue)
WScript.Echo pValue
End Sub
Public Sub Run
Dim action : Set action = New Action1Wrapper
Set action.Target = Me
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
End Sub
End Class
Class SomethingElse
Public Sub DoSomethingElse(pAction1)
pAction1.Action1("something")
End Sub
End Class
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Public Sub Action1(p1)
mTarget.HowToPassMe(p1)
End Sub
End Class
Using Execute, Action1Wrapper can also be written something like the following. You can also write a factory class for easier use.
Class Action1Wrapper
Private mTarget
Public Property Set Target(value) : Set mTarget = value : End Property
Private mName
Public Property Let Name(value) : mName = value : End Property
Public Sub Action1(p1)
Execute "mTarget." & mName & "(p1)"
End Sub
End Class
Class Action1Factory_
Public Function Create(pTarget, pName)
Dim a1 : Set a1 = New Action1Wrapper
Set a1.Target = pTarget
a1.Name = pName
Set Create = a1
End Function
End Class
Dim Action1Factory : Set Action1Factory = New Action1Factory_
Used as:
Dim action : Set action = Action1Factory.Create(Me, "HowToPassMe")
Dim se : Set se = New SomethingElse
se.DoSomethingElse action
And as I write the question, my memory gets jogged,
and I begin researching a "feature" I once discovered.
This fails to work in the context of HP UFT,
but if you're running cscript, or working with Classic ASP,
you can either declare a function late, or replace a previous declaration,
to change how it works.
VBScript lets you declare the same function or subroutine
multiple times in a program.
It treats the last declaration as the correct one.
You can get around this in cscript and ASP by physically separating
the different versions of the function,
so that one doesn't get clobbered by the other.
You'll have to be careful not to put the two anywhere near each other,
or you(r successor) might have an aneurysm trying to debug the outcome.
Honestly, you're probably better served refactoring your code some other way.
Now, with the disclaimers out of the way,
the following example is for use with cscript or wscript.
Code
Since this won't work in UFT anyway, I'll write from scratch.
In WrapperSub.vbs:
' Sub WrapperSub_Payload doesn't exist in this file.
' It must be declared by the calling file or the program will crash.
Sub WrapperSub()
wscript.echo("This begins the wrapper.")
WrapperSub_Payload
wscript.echo("This ends the wrapper.")
End Sub
In WrapperSubUseA.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload A.")
End Sub
WrapperSub
In WrapperSubUseB.vbs:
With CreateObject("Scripting.FileSystemObject")
call ExecuteGlobal(.openTextFile("WrapperSub.vbs").readAll())
End With
Sub WrapperSub_Payload
wscript.echo("This is payload B.")
End Sub
WrapperSub
Output
>cscript wrappersubusea.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload A.
This ends the wrapper.
>cscript wrappersubuseb.vbs
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
This begins the wrapper.
This is payload B.
This ends the wrapper.
Note that if a placeholder for WrapperSub_Payload
were declared in the source file,
that placeholder would always execute instead of the intended subroutine.
This is probably due to ExecuteGlobal
executing after the current file is parsed,
causing the placeholder to load after the local declaration.
When you try this in UFT --
placing the contents of WrapperSub.vbs in a function library --
the function library rightfully ignores the caller's scope.
It will then fail because WrapperSub_Payload doesn't exist in scope.

VBScript: FormatNumber() / rounding issue

dim num
num = 4895390000000005
msgbox FormatNumber(num, 0, -2, -2, false)
the output is 4895390000000010
instead of 4895390000000005
Thanks for the help
Seems like your "number" is larger than integer, declare as decimal works for me:
Dim num As Decimal
num = 4895390000000005
MsgBox(FormatNumber(num, 0, -2, -2, False))
Here is working example using com visible .net class.
With CreateObject("System.IO.StringWriter")
.Write_7 4895390000000005
MsgBox .GetStringBuilder().ToString()
End With
And here is reference link for more info.
True, if you're looking to do this in plain old vbscript (.vbs) you could easily create a library to handle math functions and types not available in vbscript. Below is a short working example. Let me know if it's helpful.
--In visual studio:
Imports System
Public Class mathLibrary
Public Function roundNumber(number As String) As String
Dim roundedNumber As String = ""
Dim largeNumber As Decimal = 0
If IsNumeric(number) Then
largeNumber = Convert.ToDecimal(number)
roundedNumber = FormatNumber(largeNumber, 0, TriState.UseDefault, TriState.UseDefault, TriState.False)
End If
Return roundedNumber
End Function
End Class
--now update your .vbs:
dim mathLib
set mathLib = CreateObject("sampleMathLibrary.mathLibrary")
Dim num
num = "4895390000000005"
msgbox mathLib.roundNumber(num)

Overload constructors in VBScript

I found a way to extend classes in VBScript, but are there any ways to pass in parameters or overload the constructor? I am currently using an Init function to initialize the properties, but would like to be able to do this when I create the object.
This is my sample class:
Class Test
Private strText
Public Property Get Text
Text = strText
End Property
Public Property Let Text(strIn)
strText = strIn
End Property
Private Sub Class_Initialize()
Init
End Sub
Private Sub Class_Terminate()
End Sub
Private Function Init
strText = "Start Text"
End Function
End Class
And I create it
Set objTest = New Test
But would like to do something like this
Set objTest = New Test(strInitText)
Is this possible, or does the object have to be created and initialized in two setps?
Just to alter slightly on svinto's method...
Class Test
Private m_s
Public Default Function Init(s)
m_s = s
Set Init = Me
End Function
Public Function Hello()
Hello = m_s
End Function
End Class
Dim o : Set o = (New Test)("hello world")
Is how I do it. Sadly no overloading though.
[edit]
Though if you really wanted to you could do something like this...
Class Test
Private m_s
Private m_i
Public Default Function Init(parameters)
Select Case UBound(parameters)
Case 0
Set Init = InitOneParam(parameters(0))
Case 1
Set Init = InitTwoParam(parameters(0), parameters(1))
Else Case
Set Init = Me
End Select
End Function
Private Function InitOneParam(parameter1)
If TypeName(parameter1) = "String" Then
m_s = parameter1
Else
m_i = parameter1
End If
Set InitOneParam = Me
End Function
Private Function InitTwoParam(parameter1, parameter2)
m_s = parameter1
m_i = parameter2
Set InitTwoParam = Me
End Function
End Class
Which gives the constructors...
Test()
Test(string)
Test(integer)
Test(string, integer)
which you can call as:
Dim o : Set o = (New Test)(Array())
Dim o : Set o = (New Test)(Array("Hello World"))
Dim o : Set o = (New Test)(Array(1024))
Dim o : Set o = (New Test)(Array("Hello World", 1024))
Bit of a pain though.
You can work around it by having your Init function returning the object itself...
Class Test
Private m_s
Public Function Init(s)
m_s = s
Set Init = Me
End Function
Public Function Hello()
Hello = m_s
End Function
End Class
Dim o
Set o = (New Test).Init("hello world")
Echo o.Hello
You have to do it in two steps. VB Script doesn't support overloading so you can't modify the default constructor with new parameters. Same goes for Vb6
A bit hackish, for sure, but when I need varargs in calls, one of my parameters I pass in as an array, i.e.
Rem printf done poorly
sub printf(fmt, args)
dim fp, vap:
dim outs:
dim fini:
fini = 0:
vap = 0:
while (not fini)
fp = index(fmt,"%"):
if (not(isNull(fp))) then
' do something with %f, %s
select case(fp)
case 'c':
outs = outs & charparse(args(vap)):
case 's':
outs = outs & args(vap):
' and so on. Quite incomplete but you get the idea.
end select
vap = vap + 1
end if
wend
end sub
printf("%s %d\n",array("Hello World", 42)):

Resources