Fabulous Adventures In Coding
Eric Lippert is a principal developer on the C# compiler team. Learn more about Eric.
Every now and then someone will ask me what the VBScript error message "Cannot use parentheses when calling a Sub" means. I always smile when I hear that question. I tell people that the error means that you CANNOT use PARENTHESES when CALLING a SUB -- which word didn't you understand?
Of course, there is a reason why people ask, even though the error message is perfectly straightforward. Usually what happens is someone writes code like this:
Result = MyFunc(MyArg) MySub(MyArg)
and it works just fine, so they then write MyOtherSub(MyArg1, MyArg2) only to get the above error.
Here's the deal: parentheses mean several different things in VB and hence in VBScript. They mean:
1) Evaluate a subexpression before the rest of the expression: Average = (First + Last) / 2 2) Dereference the index of an array: Item = MyArray(Index) 3) Call a function or subroutine: Limit = UBound(MyArray) 4) Pass an argument which would normally be byref as byval: Result = MyFunction(Arg1, (Arg2)) ' Arg1 is passed byref, arg2 is passed byval
That's confusing enough already. Unfortunately, VB and hence VBScript has some weird rules about when #3 applies. The rules are
3.1) An argument list for a function call with an assignment to the returned value must be surrounded by parens: Result = MyFunc(MyArg) 3.2) An argument list for a subroutine call (or a function call with no assignment) that uses the Call keyword must be surrounded by parens: Call MySub(MyArg) 3.3) If 3.1 and 3.2 do not apply then the list must NOT be surrounded by parens.
And finally there is the byref rule: arguments are passed byref when possible but if there are “extra” parens around a variable then the variable is passed byval, not byref.
Now it should be clear why the statement MySub(MyArg) is legal but MyOtherSub(MyArg1, MyArg2) is not. The first case appears to be a subroutine call with parens around the argument list, but that would violate rule 3.3. Then why is it legal? In fact it is a subroutine call with no parens around the arg list, but parens around the first argument! This passes the argument by value. The second case is a clear violation of rule 3.3, and there is no way to make it legal, so we give an error.
These rules are confusing and silly, as the designers of Visual Basic .NET realized. VB.NET does away with this rule, and insists that all function and subroutine calls be surrounded by parens. This means that in VB.NET, the statement MySub(MyArg) has different semantics than it does in VBScript and VB6 -- this will pass MyArg byref in VB.NET, byval in VBScript/VB6. This was one of those cases where strict backwards compatibility and usability were in conflict, and usability won.
Here's a handy reference guide to what's legal and what isn't in VBScript: Suppose x and y are vars, f is a one-arg procedure and g is a two-arg procedure.
to pass x byref, y byref: f x call f(x) z = f(x) g x, y call g(x, y) z = g(x, y)
to pass x byval, y byref: f(x) call f((x)) z = f((x)) g (x), y g ((x)), y call g((x), y) z = g((x), y)
The following are syntax errors: call f x z = f x g(x, y) call g x, y z = g x, y
Ah, VBScript. It just wouldn't be the same without these quirky gotchas.
This is evil. I wonder if this is a Basic artifact? I hated this and just spent an afternoon trying to figure out why my subroutine's change to the value of it's parameter did not persist when the subroutine returned *cry*
PingBack from http://www.advancedqtp.co.il/2007/10/vbscript-nitpicking-part-2/
PingBack from http://famousquotes.247blogging.info/?p=114
I get the error message above. Here is a part of script - what is wrong?
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run ("ftp -s:" & chr(34) & strFTPScriptFileName & chr(34), , True)
the examples of "to pass x byref, y byref: " and others do not make sense to me? Please help me understand...
I wrote an article about that a mere six hours after I wrote the article above! :-)
PingBack from http://qtp.solmarkn.com/first-steps/articles-first-steps-knowledge-base/vbscript-techniques/vbscript-nitpicking-the-good-kind-part2/
Great explanation. Thank you!!!
PingBack from http://www.hilpers.com/230755-fehler-nr-13-a
PingBack from http://www.keyongtech.com/1164748-response-writeline
PingBack from http://www.hilpers.fr/932108-evaluation-dexpression-arithmetiques
I have the following in a file show.vbs:
All of these invocations seem to work. I would have thought that two of these would be incorrect. Or is there something I'm missing with the no arguments scenario? e.g. in VBA if you were to try Show() it would tell you that "Expected: =" which makes sense under 3.1 -- VBA sees the parentheses and assumes that the call should be a Function so there should be an assignment.
show(a) does not error out, however it may not do exactly as you'd expect for the reasons given long ago by Eric. Anyway, I'm not surprised that none of the four examples above errors out, as I see the issue mainly showing up when the number of arguments is greater than one.
I read your explaination once and didnt understand it.
i tried it out and nothing worked.
I read it again and swore at the screen severl times and still nothing worked.
I read it again and tried it again and swore a lot more and then started talking to my reflection in the screen and things started to work.
I owe you beer for the good of my mental health.
how to write this?
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run(iexplore -k http://support.microsoft.com/kb/154780, 1, true)