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
Anonymous
September 16, 2003
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.Anonymous
September 16, 2003
I live to serve!Anonymous
February 18, 2004
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.Anonymous
May 06, 2004
It occurs to me that there may be some confusion about what exactlyAnonymous
May 24, 2005
The comment has been removedAnonymous
May 25, 2005
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.Anonymous
July 20, 2005
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.Anonymous
August 09, 2005
Thanks Eric, I know for certain I 'learned' this quite some time ago, and you have taught me once again.Anonymous
January 09, 2006
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)Anonymous
April 17, 2006
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.Anonymous
June 28, 2006
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?Anonymous
June 28, 2006
Yes, the syntax is the same for VB6/VBA. The VBScript syntax and semantics were designed for compatibility with VB6.Anonymous
November 25, 2006
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 VisakhAnonymous
December 10, 2006
The comment has been removedAnonymous
December 12, 2006
Taken from: http://blogs.msdn.com/ericlippert/archive/2003/09/15/52996.aspx What do you mean "cannotAnonymous
December 12, 2006
Jan: yes, the newsgroup posting has a typo. I regret the error.Anonymous
February 12, 2007
Just add "Call " before you function call. Worked for me.Anonymous
April 17, 2007
PingBack from http://www.vleck.com/index.php/2007/04/17/vbscript-recursive-list-of-files-in-all-folders-and-subfolders-v2/Anonymous
April 25, 2007
PingBack from http://blog.vleck.com/?p=13Anonymous
May 11, 2007
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.Anonymous
June 01, 2007
メモ: サンプル、 PowerShell, etc... (VBScript)Anonymous
June 01, 2007
メモ: サンプル、 PowerShell, etc... (VBScript)Anonymous
June 12, 2007
Can you then tell me how do you call a sub when an error occurs in vbs Tried :"On Error mySub()" did'nt workAnonymous
June 13, 2007
VBScript only supports "On Error Resume Next" for error handling, sorry.Anonymous
August 21, 2007
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!Anonymous
September 14, 2007
why am i getting this error while trying to set a response.status and response.addheaderAnonymous
October 02, 2007
PingBack from http://www.advancedqtp.com/2007/10/vbscript-nitpicking-part-2/Anonymous
October 04, 2007
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 cryAnonymous
October 07, 2007
PingBack from http://www.advancedqtp.co.il/2007/10/vbscript-nitpicking-part-2/Anonymous
January 04, 2008
PingBack from http://famousquotes.247blogging.info/?p=114Anonymous
February 12, 2008
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) dKesAnonymous
March 03, 2008
the examples of "to pass x byref, y byref: " and others do not make sense to me? Please help me understand... thxAnonymous
March 03, 2008
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.aspxAnonymous
March 30, 2008
PingBack from http://qtp.solmarkn.com/first-steps/articles-first-steps-knowledge-base/vbscript-techniques/vbscript-nitpicking-the-good-kind-part2/Anonymous
May 14, 2008
Great explanation. Thank you!!!Anonymous
January 20, 2009
PingBack from http://www.hilpers.com/230755-fehler-nr-13-aAnonymous
January 21, 2009
PingBack from http://www.keyongtech.com/1164748-response-writelineAnonymous
January 22, 2009
PingBack from http://www.hilpers.fr/932108-evaluation-dexpression-arithmetiquesAnonymous
January 28, 2009
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.Anonymous
May 07, 2009
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.Anonymous
June 10, 2009
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.Anonymous
November 30, 2009
how to write this? Set WshShell = WScript.CreateObject("WScript.Shell") WshShell.Run(iexplore -k http://support.microsoft.com/kb/154780, 1, true)Anonymous
December 04, 2009
This has been always confusing for me. Here, I find a perfect answer. Thank you so much, Eric. JoeAnonymous
December 07, 2009
>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.Anonymous
March 31, 2010
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!)Anonymous
June 11, 2010
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.Anonymous
June 11, 2010
Oops... I meant... Sub MySub(Parameter) passes by value, but Sub MySub( Parameter ) and Sub MySub(Parameter, AnotherParameter) pass by reference.Anonymous
June 11, 2010
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.Anonymous
November 14, 2010
This documentation is very explanatory. ThankAnonymous
December 23, 2010
interestingLY, I got this same error for not saving the return value: Replace( name, "findStr", "replaceStr" ) ' error name = Replace( name, "findStr", "replaceStr" ) ' okAnonymous
December 29, 2010
Seriously. Change: MyOtherSub(MyArg1, MyArg2) To: Call MyOtherSub(MyArg1, MyArg2) Or: MyOtherSub MyArg1, MyArg2Anonymous
November 11, 2011
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. ThxAnonymous
August 16, 2012
thanks for this post. simple answer is when using Sub call like: MySub arg1, arg2, arg3