When you call a function like x = myfunc(a,b,,d) what happens with the 3rd parameter? is it empty? null? nothing?
I'm having issues with my function, say,
function myfunc(p1, p2, p3, p4)
if p3 <> "" then whatever
end function
gives me a dreaded type mismatch
P.S. I'm trying to replace a COM object with vbscript functions, and those empty parameter calls were done to the COM objects which didn't have a problem with those, but vbscript doesn't like them. I can't change the calls, only the functions so I need to treat the empty parameter somehow, just don't know how (tried isnull with no luck, also isempty no luck, and is nothing gives me an object required error)
The data type of missed parameters is ERROR.
I wrote an example, try the following:
Function IsMissing(p)
IsMissing = (VarType(p) = vbError)
End Function
Function Myfunc(p1, p2, p3, p4)
If IsMissing(p1) Then Response.Write "p1 is missing "
If IsMissing(p2) Then Response.Write "p2 is missing "
If IsMissing(p3) Then Response.Write "p3 is missing "
If IsMissing(p4) Then Response.Write "p4 is missing "
End Function
str = myfunc(, , , 1)
'str = myfunc(1, 2, , ) 'causes error
Notice: The last parameter of function / sub cannot be blank because of syntax error.
In the spirit of teaching a man to fish:
The TypeName function will reveal to you what type it is.
MsgBox TypeName(p3) ' Returns string: "Error"
Instead of if p3 <> "" you can test for omitted arguments like this:
If TypeName(p3) = "Error" Then MsgBox "Whatever!"
or, preferably,
If VarType(p) = vbError Then MsgBox "Whatever!"
' vbError is a built-in constant whose value is 10.
But really, for best practice, your program design should avoid omitting arguments. VBScript isn't really made to deal with "optional" arguments.
VBScript doesn't support optional parameters. They can be simulated by passing an array or null values in, which isn't helpful in your situation since you can not change the calls. The next solution might be to consider changing the platform (may I suggest .Net) or an On Error Resume Next hack like the one below.
'Assuming a String Parameter
Dim strOptionalParameter As String
strOptionalParameter = p3
'We set the value of the optional parameter to a variable
'in case it blows up and moves on to this next statement.
If strOptionalParameter <> "" Then
'Rest of Code Here
End If
Related
This question is mostly out of interest to understand the functionality of VBScript better. I recognize that I can simply do some casting to know what to expect from my code, but in my situation I want to understand why casting, or any "workaround", is needed. For simplicity, here's the basic idea of my code:
variable1 = 1
Public Function findSomethingInATextString(par1, par2)
[...searching with a Do Until loop code here...]
Result = 1
If([par2 is found in par1]) Then
Result = 0
End If
Return Result
End Function
variable1 = findSomethingInATextString("Hello World", "Hello")
When I run this I get a Type Mismatch error. I don't understand why that's the case. variable1 is an integer and findSomethingInAString() returns an integer. They appear to be the same data type.
I'm working in a restricted environment where I can't do much debugging (it's painfully slow to code in this program...). So at the moment I'm unable to say what data type this is coming out as - I just know that it's apparently not integer.
After all that, and to make sure my question is clear, I'm intrigued to know what the return type of my function is (if someone happens to know), but my real question is: Why isn't the return type matching with variable1?
Use the minimal script
Return
Output
cscript 36633603.vbs
...36633603.vbs(1, 1) Microsoft VBScript runtime error: Type mismatch: 'return'
to prove to yourself, that just mentioning return in a VBScript will throw a type mismatch error.
Believe JosefZ's comment that VBScript returns function values by assigning to the function's name. Better: Read the docs (before you try to write code).
Evidence:
Function f1()
f1 = 1
End Function
WScript.Echo f1(), TypeName(f1())
Output:
cscript 36633603.vbs
1 Integer
Execute in vbscript is used to parse strings into inline instructions. However, I can't use it to break For loops. The following won't work:
For i = 1 To 10
msgbox i
Execute("Exit For")
Next
Does somebody know any workaround for it to work? Or any way for Exit Sub/Exit Function work? I tried ExecuteGlobal, but it also raises "invalid exit instruction" error.
I used it as a form of "import" throughout my code and it is going to be time consuming to implement another form of import all over again, so I am searching for a way to make it work before considering to change the project.
EDIT: more examples
In short, I tried to do a partial override of a semi-abstract method, but can't use Exit For against a For outside the Execute block.
Dim names_array 'this array will be populated in another Sub
'this method will loop though all elements of an array and insert some items into a database...
'but the method does not know (how to)/(if it will really) insert into the database!
'all it knows is: it has to loop though the array... and, if needed, finish earlier
'the insertion part will have to be described before running, like a 'partial override' of an abstract method
'that is because I know how part of the method must be, but the details will be diferent for each database
'I don't want to override every method entirely!
Sub insert_into_database
For i = 0 To UBound(names_array)
name = names_array(i)
Execute(core_op) 'here comes the core operation, it is what I override!
Next
End Sub
'for some databases, core_op will be simple:
core_op = "objRecordSet.Open ""INSERT INTO videos (title, date_time) VALUES ('"" & name & ""', Now)"", objConnection"
'for others, it may be complex:
core_op = "If name=""18+ only"" Then"
core_op = core_op + " MsgBox ""You tried to insert nasty things. System will now halt."""
core_op = core_op + " Exit For"
core_op = core_op + "Else"
core_op = core_op + " objRecordSet.Open ""INSERT INTO videos (title, date_time) VALUES ('"" & name & ""', Now)"", objConnection"
core_op = core_op + "End If"
'the string won't be loaded this way, it will come from an external file
'there are other methods that operate like this, but not all of them need Exit For
That is closer to my project. The reality is: the code for the overrides will come from an external file and will be stored in a dictionary.
Sorry if it seems a mess, that is the way I tried to make vbscript more java-like regarding reusability.
Exit statements are parts of compound statements (loops, routines) and must be 'compiled' in their contexts. So there is no workaround and you have to redesign your import strategy. Perhaps you should publish your current approach.
Update wrt comment (partial override of the method):
Use GetRef and pass "function pointers", or invest in some classes and send objects to the enclosing function.
This is more of an "ethical" question than a technical question.
It's very obvious why Eval() and Execute() are dangerous if you allow user-defined data/variables to get passed through them. However, I've always gotten the vibe that the use of these functions is frowned upon no matter what and are only to be used as a last resort.
Anyway, I've used them here and there when it can make coding more efficient and dynamic... but I always make sure I know that what gets passed through the functions are controlled and not user-defined. Would you consider this bad coding? Is there a way a hacker could take advantage of those functions even if it's not reading anything defined by Request or Session variables or any other user-defined data?
If what you pass into the Eval() or Execute() is purely your own string, without any input the user can influence whatsoever, then it should be safe.
However, this also renders a lot of the possibilities of Eval() and Execute() useless.
For example, it is very tempting to use Eval() and Execute() for creating API-like functions, where a user calls a function in a querystring, and you simpley Eval() it instead of using a big select...case for every possible call.
I have also seen it used in CSV parsing, where column names are mapped to recordset-columns using eval(), again, very useful, but extremely dangerous, but you have already demonstrated in your question you are aware of that.
If you are absolutely sure the parsed code is under your complete control, it's very poweerful.
If it's any help, I use this function to replace Eval to a certain degree. It allows you to specify which functions are valid in Eval. To use it simply call Calculate(expression).
'Calculation limitations for use with Calculate function...
Const C_VALID_CALC_CHARS = "0123456789.+-*/()^\=,"
'NOTE: Deliberately broken this const so that it is readable in StackOverflow...
Const C_VALID_CALC_FUNCTIONS = " Abs Asc Atn CBool CByte CCur CDate
CDbl Chr CInt CLng Cos CSng CStr Date DateAdd DateDiff DatePart
DateSerial DateValue Day Escape Exp FormatCurrency FormatDateTime
FormatNumber FormatPercent GetLocale Hex Hour InStr InStrRev Int Fix
IsDate IsEmpty IsNull IsNumeric LCase Left Len Log LTrim RTrim Trim
Maths Mid Minute Month MonthName Now Oct Replace Right Rnd Round Second
Sgn Sin Space Split Sqr StrComp String StrReverse Tan Time Timer
TimeSerial TimeValue UCase Unescape Weekday WeekdayName Year "
'Calculate
' Calculates the expression string and returns a value.
'Usage:
' value = Calculate("Sin(43) * Cos(75)")
'Parameters:
' expression (string) - A string value containing the expression to be evaluated.
'Notes:
' This function provides a controlled method for evaluating specific
' functions but has been severly limited to minimise negative effects
' from hacking attempts. A complete list of available functions and
' symbols can be found in the two variables C_VALID_CALC_CHARS and
' C_VALID_CALC_FUNCTIONS.
Function Calculate(expression)
Dim rV
rV = ""
If expression & "" <> "" Then
Dim t, c, v
'Validate first...
t = expression
'Strip out all standard characters...
For c = 1 to Len(C_VALID_CALC_CHARS)
t = Replace(t, Mid(C_VALID_CALC_CHARS, c, 1), " ")
Next 'c
'Strip out multiple spaces...
Do While Instr(t, " ") > 0
t = Replace(t, " ", " ")
Loop
t = Trim(t)
'Check what we're left with...
v = t = ""
If Not v Then
Dim f
f = Split(t, " ")
v = True
For c = 0 To UBound(f, 1)
v = Instr(C_VALID_CALC_FUNCTIONS, f(c)) > 0
Next 'f
End If
'Define the return value...
If v Then
rV = Eval(expression)
Else
rV = "Invalid Expression!"
End If
End If
Calculate = rV
End Function
It may not be the fastest way to do it, especially if you're using it frequently, but you could use it as a way of validating an equation before launching into it.
I did test it a little while ago, but let me know if you have any problems.
I am running into the Type Mismatch error when I attempt to call a function I created.
Example:
Function DoThis(paramA, paramB, paramC)
If paramA = "Something" Then
DoThis = DoSomething
ElseIf paramA = "This" Then
DoThis = DoSomethingDifferent
Else
DoThis = DoThisOtherThing
End If
End Function
Dim result: result = DoThis(valueA, ValueB, ValueC)
Can anyone see what my mistake could be? Other functions are working correctly. I have double checked the spelling by actually copying and pasting the function name where I call it. I have verified that the function name is not used anywhere else, i.e., as a constant or something else.
Note that when debugging this the ValType for all arguments is vbString. Also I am never able to enter the function, so it is not like I am debugging the function, enter it and then get the type mismatch.
ty.
VBScript has only one data type called a Variant. A Variant is a special kind of data type that can contain different kinds of information, depending on how it is used. Because Variant is the only data type in VBScript, it is also the data type returned by all functions in VBScript.
There are some subtypes of data that a Variant can contain (e.g. Empty, Null, string, integer, object, array etc.) You can use some conversion functions to convert data from one subtype to another, if that conversion is not implicit in VBScript. Now, pay your attention to real, factual data subtype of True and vbTrue.
The True keyword (boolean literal) has a value (inner representation) equal to -1.
On the other hand, vbTrue is one of few built-in constants and, in despite of it's name, has a subtype of Integer! It's one of so-called Tristate Constants:
Constant Value Description
vbUseDefault -2 Use default from computer's regional settings.
vbTrue -1 True
vbFalse 0 False
I hope next code could make clear all above statements:
Wscript.Echo _
vbTrue, CStr( vbTrue), VarType( vbTrue), TypeName( vbTrue) , _
vbNewLine, True, CStr( True), VarType( True), TypeName( True)
However, used with If _condition_ Then ..., there are some particularities; in brief:
The Then part of the If ... statement conditionally executes groups of statements only when a single test If condition is not False, i.e. any non-zero number esteems to be true, not only -1. Therefore you are able to use whatever variable or expression (numeric or string) you choose as long as the result is numeric...
Summarizing: If _expr_ Then ... is the same as
If CBool(_expr_) Then ...
The reason why retval is retuning mismatch error because it has a numeric value and an alpha value and wsh does not like that.
A sure way to get a type mismatch error for the published code is to define DoSomething etc. as Subs (which seems probable, given the names).
I cannot explain why this was a problem, but today I reduced the function down to a simple boolean return value and I still got the type mismatch error.
So I then created a new function with the same parameters and such. When I changed the call to the new function the error goes away.
My original function with the simple boolean return:(MISMATCH ERROR)
Function IsInstalledCheck(valueToCheck, expectedValue, checkType)
IsInstalledCheck = vbFalse
End Function
My new function with the a simple return:(Works)
Function IsItemInstalled(valueToCheck, expectedValue, checkType)
IsItemInstalled = vbFalse
End Function
EDIT
Note that I had tried this with the standard True / False values as well. The solution was to simply recreated the same function with a new name and for whatever magical reason that worked. The function signature was the same, the order of variables, variable names, the test conditions, everything in the body of the new function is the same.
I am getting the 800A0414 error in lines 7 and 12 of this script:
Module Module1
Dim p
Sub Main()
CreateObject("Wscript.Shell").Run("program.bat", 0, True)
p = Process.GetProcessesByName("program")
If p.Count > 0 Then
WScript.Sleep(300000)
Else
CreateObject("Wscript.Shell").Run("program clean up.bat", 0, True)
End If
End Sub
Private Function WScript() As Object
Throw New NotImplementedException
End Function
End Module
I am trying to run a batch script, that starts a process, then wait until the process terminates, then run another batch script. I also do not want any command boxes being shown. If their is a easier way please let me know.
Thanks for your help
When you enclose a procedure's argument list in parentheses, you must use the Call keyword:
Call CreateObject("WScript.Shell").Run("program.bat", 0, True)
If you omit the Call keyword, you must also drop parentheses:
CreateObject("WScript.Shell").Run "program.bat", 0, True
To complete what's been said before:
When Call keyword is used to call a procedure (i.e. sub or function) the arguments must be enclosed in parentheses, except when the procedure has no arguments in which case the parentheses are optional. For example all the statements:
Call test()
Call test
Call test(1,2)
are valid, but not this one:
Call test 1
When calling a procedure without using the Call keyword, the parentheses can only be used when either the procedure has zero or one argument or the procedure has a return value (i.e. is a function) and its value is used in the same statement. For example all the statements:
test()
test(1)
test(1,2)
a = test
a = test(1,2)
a = test(test(1,2),2)
are valid, except the third one which has more than one argument. In case it's not clear, the inner call of "test" in the last statement is valid because its return value is used as an argument to another call.
Note that whenever parentheses is used in this text, it is meant to imply the possible comma-separated values as well.
Seems to me this is a VB.NET, not VBScript code.
You have Shell function in VB.NET (and other methods).
Anyway, Run returns any error code returned by the program, and if you
store that result in a variable, you can use parentheses in this case.
Dim lResult As Long
lResult = CreateObject("Wscript.Shell").Run("program.bat", 0, True)
The rest was answered by #Helen.