What do you mean "cannot use parentheses?"


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.

Comments (56)

  1. For some time I had a printout from your 1999 usenet post about this taped to my wall. Now it occupies a place of honor in my snippet database. Thank you so much for it.

  2. I was talking about reference types vs. by-reference variables a while back. Recall that both JScript and VBScript have reference types (ie, objects) but JScript does not have by-reference variables. COM supports passing variable references around, but unfortunately the intersection of early-bound COM and late-bound IDispatch is a little bit goofy.

  3. It occurs to me that there may be some confusion about what exactly

  4. Curtis says:

    DUDE ! YOU Fn ROCK !

    This document is more informative than the MSDN library on this subject.

    I was banging my head against the wall with this same problem for hours. I had 2 arguments and was calling a sub from an onClick and was getting the error At lines 511-513. 8 pages with the same code.

    OK, I have one more Addition to your List of things that will cause the error.

    after I fixed all 8 pages with this… they all started working, no errors. HOWEVER, the main page (default.asp) that DID previously work, was now giving the same error. I could not finger out the problem, hacked out code chunk by chunk… I mean… I ALREADY fixed it… there must be something else I missed.

    after about 20 minutes I realized that the only difference on the main page was that at the very top in the <BODY> tag I was doing an onLoad="subLoadDefaults()" none of the other pages had that.

    WITH the fixed code, that now triggered the errors way down in lines 511- 513….

    now, the funny part is, that before when I had the code WRONG in those same lines, they worked quite fine and no errors. Somehow the onLoad="subLoadDefaults()" made the rest of the page ignore that it was wrong.

    I got rid of the onLoad all together and the page works fine now.

    THANKS !!!!

  5. Eric Lippert says:

    Dude! As I said earlier, I live to serve!

    I’m not sure why your onload guy was causing a problem — I’d have to actually see the server code in action to figure it out. But I’m glad you’ve managed to sort out your problem.

  6. Jan Lourens says:

    Eric, you are a friggin’ legend! I can’t thank you enough for clearing that up for me – been looking like a fool in front of new collueges for hours until I stumbled upon this site.

    Thanks.

  7. Aaron says:

    Thanks Eric, I know for certain I ‘learned’ this quite some time ago, and you have taught me once again.

  8. anon says:

    Another stupid thing that will trigger this error: using

    DateDiff("n", Date1, Date2)

    by itself (not with Response.Write or assigning it to a variable)

  9. Clinton says:

    I can not beleive that it was that simple yet I had stewed over this for ages!  Mate the lights just went on and a big thank you to you.

  10. Martin says:

    That is the greatest piece of knowledge about sub/function parameters.

    Just one question: Does this byref/byval parens apply as well in VB6 and VBA in which you can actually declared subs and functions by using the byref and byval keywords for parameters?  If it applies, does it overrides default behavior and/or coded behavior?

  11. Eric Lippert says:

    Yes, the syntax is the same for VB6/VBA.  The VBScript syntax and semantics were designed for compatibility with VB6.

  12. Visakh says:

    Hi Eric,

    Thanks a lot for the article. This error was bugging me for an hour!!!  I am calling VBS function inside JSTL and i thot it was mixing of scripts which caused the issue. Does this mean, i have to split the 2 behaviors into 2 functions, if i have to call the Sub with 2 parameters?

    Regards

    Visakh

  13. I am a bit confused right now.

    In your newsgroup posting (1) you say

    n = Foo((z))       ‘ legal, passes z by reference

    and in your blog it is "to pass x byval"

    z = f((x))

    I guess "by value" is the right one?

    (1) http://www.mvps.org/scripting/languages/eric_lippert_parens_in_procedures.txt

  14. Eric Lippert says:

    Jan: yes, the newsgroup posting has a typo. I regret the error.

  15. NikitaP says:

    Just add "Call " before you function call. Worked for me.

  16. vm says:

    kind of relevant.. the error could happen if you don’t include javascript: tag before a javascript function call on a page having both vbscript and javascript.

  17. メモ: サンプル、 PowerShell, etc… (VBScript)

  18. メモ: サンプル、 PowerShell, etc… (VBScript)

  19. k0nvikt says:

    Can you then tell me how do you call a sub when an error occurs in vbs

    Tried :"On Error mySub()" did’nt work

  20. Eric Lippert says:

    VBScript only supports "On Error Resume Next" for error handling, sorry.

  21. DrewGuy says:

    Hey, I just wanted to thank you. I’m incredibly new to VB of any kind, and your site provided the answer after a couple hours of struggling with this problem. You rock!

  22. san says:

    why am i getting this error while trying to set a response.status and response.addheader

  23. Beez says:

    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*

  24. dKes says:

    Hi Eric,

    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)

    dKes

  25. the examples of "to pass x byref, y byref: " and others do not make sense to me? Please help me understand…

    thx

  26. Eric Lippert says:

    I wrote an article about that a mere six hours after I wrote the article above! 🙂

    http://blogs.msdn.com/ericlippert/archive/2003/09/15/53005.aspx

  27. SD says:

    Great explanation.  Thank you!!!

  28. Randy says:

    Eric,

    Great post.

    I have the following in a file show.vbs:

    Sub Show()

      MsgBox "Show"

    End Sub

    call Show()

    call Show

    Show()

    Show

    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.

    Thanks.

  29. Al Dunbar says:

    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.

  30. I talk to VBscript says:

    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.

  31. all says:

    how to write this?

    Set WshShell = WScript.CreateObject("WScript.Shell")

    WshShell.Run(iexplore -k http://support.microsoft.com/kb/154780, 1, true)

  32. JoeNo1 says:

    This has been always confusing for me. Here, I find a perfect answer.

    Thank you so much, Eric.

    Joe

  33. BigJeff5 says:

    >all said:

    >how to write this?

    >

    >Set WshShell = WScript.CreateObject("WScript.Shell")

    >

    >WshShell.Run(iexplore -k http://support.microsoft.com/kb/154780, 1, true)

    You should use quotation marks around the command, first off.  That wasn’t the cause of your problem but it would likely just cause another error after you fixed the parenthesis error.

    There are two ways to correctly call the function, you can either preface the function with "Call" like this:

    Call WshShell.Run("iexplore -k http://support.microsoft.com/kb/154780", 1, true)

    Or you can simply remove the parenthises:

    WshShell.Run "iexplore -k http://support.microsoft.com/kb/154780", 1, true

    This is because, as Eric explained in his article, VBSCRIPT sees the parenthesis in function(myarg) as telling it to pass myarg by reference instead of by value.  It does NOT see the parenthesis as describing where the list of arguments start and end.  Since the comma tells VBSCRIPT where the next argument is, and it is expecting a parenthesis before the comma, it puts two and two together and tells you you can’t use parenthesis (around the list of arguments) when calling a sub.

    If it’s still confusing to you, then just remember to allways use "Call" before calling a subroutine and it will behave the way you expect it to, because Call does require all arguments to be within a set of parenthesis.  You can wrap individual arguments in the parenthesis to set them to by reference instead of by value.

  34. Shaun W says:

    6 1/2 years later and this is still the clearest explanation of the cause behind this on the Internet.  Even with the many other language options out there, somehow VBScript still gets it’s hooks into the developer’s world.  Known how to fix this for some time, but never bothered to learn why.  Now I know.  Thanks, Eric (if you’re even still around!)

  35. kbuglrien says:

    The article is great, but it primarily focuses on calls rather than declarations.  Consider this gem…

    Sub MySub(ByRef Parameter)

     …

    End Sub

    Parameter is passed by value.

    Whitespace matters when there is only one parameter.

    Sub MySub(Parameter) passes by value, but Sub MySub( Parameter ) and Sub MySub(Parameter, AnotherParameter) pass by value.

  36. kbulgrien says:

    Oops… I meant…

    Sub MySub(Parameter) passes by value, but Sub MySub( Parameter ) and Sub MySub(Parameter, AnotherParameter) pass by reference.

  37. kbulgrien says:

    Well, my bad, apparently…  It only seems like it was the declaration…  Somewhere in chasing it down, the whole confusing thing got me:  turns out the declaration of Sub MySub(Parameter) does pass by reference if the call is written well…

    Still, the whole thing with Sub MySub(ByRef Parameter) passing by value when called MySub (Parameter)  is just plain wrong.  The presence of ByRef didn't kick an error, and was not treated as a parameter, so its as though the parser just disappeared it silently.

    I'll slither back into my corner now.

  38. Tosin - nolimit says:

    This documentation is very explanatory.

    Thank

  39. bobobobo says:

    interestingLY, I got this same error for not saving the return value:

    Replace( name, "findStr", "replaceStr" )  ' error

    name = Replace( name, "findStr", "replaceStr" )  ' ok

  40. TheShadowOne says:

    Seriously.

    Change:  MyOtherSub(MyArg1, MyArg2)

    To: Call  MyOtherSub(MyArg1, MyArg2)

    Or:  MyOtherSub MyArg1, MyArg2

  41. Bao says:

    Great explaination…it was confusing to me why passing 1 argument with parentheses works but 2 arguments failed…and you had the answer for it.  Thx

  42. Muzi says:

    thanks for this post.

    simple answer is when using Sub call like:

    MySub arg1, arg2, arg3