Lua - Local variable scope in function - scope

I have the following function
function test()
local function test2()
print(a)
end
local a = 1
test2()
end
test()
This prints out nil
The following script
local a = 1
function test()
local function test2()
print(a)
end
test2()
end
test()
prints out 1.
I do not understand this. I thought declaring a local variable makes it valid in its entire block. Since the variable 'a' is declared in the test()-function scope, and the test2()-function is declared in the same scope, why does not test2() have access to test() local variable?

test2 has has access to variables which have already been declared. Order matters. So, declare a before test2:
function test()
local a; -- same scope, declared first
local function test2()
print(a);
end
a = 1;
test2(); -- prints 1
end
test();

You get nil in the first example because no declaration for a has been seen when a is used and so the compiler declares a to be a global. Setting a right before calling test will work. But it won't if you declare a as local.

Related

Making a function with mutually exclusive pre defined argument list

I know what an optional argument list is and how to set default vales.
Public Function argTest(arg1 As String, Optional arg2 As String = "TEST", Optional arg3 As String = "TEST") As Long
'Some logic and stuff
End Function
But thats not exactly what I'm after. Here is an example
If you add the UIAutomationClient reference and paste the following code into a sub
Dim uiElement As IUIAutomationElement
Dim uiProp As IUIAutomationCondition
uiElement.FindFirst(TreeScope_Children, uiProp) = 0
Where you define the TreeScope_Children is the functionality I'm after. right after typing "uiElement.FindFirst(" you get a drop down menu of options.
How do I make a function call do THAT?
Using an enumeration:
Enum Foo
bar = 1
baz = 2
End Enum
Public Function argTest(grr As Foo)
End Function
If you want to create a method parameter which references an existing enum from a library referenced by your VBA project, you can do it like this:

Using Subroutine for output instead of a function

I thought at this point that the role of functions and subroutines was very clear to me. But now I am not so sure... I see it written all the time
"Functions can return values / subroutines cannot return a value."
and
"a function can only return a single value" (I realize they can return arrays and such too).
But it seems as though I can effectively "return a value from a subroutine" if I pass the "result" variable into the subroutine... Is this considered a "poor practice?" or am I missing some other key concept here...
Method # 1 (Using a Function):
Sub test1()
Dim x As Integer
Dim y As Integer
Dim z As Integer
x = 2
y = 3
z = test2(x, y)
End Sub
Function test2(var1 As Integer, var2 As Integer) As Integer
test2 = var1 + var2
End Function
Method # 2 (Using a Subroutine):
Sub test3()
Dim x As Integer
Dim y As Integer
Dim z As Integer
Call test4(x, y, z)
End Sub
Sub test4(var1 As Integer, var2 As Integer, var3 As Integer)
var1 = 2
var2 = 3
var3 = var1 + var2
End Sub
Usually, it is bad practice to change the value of a parameter. Just look at you examples - it is obvious that your function does something with the 2 parameters and returns a value (which you write to z). In the second example, you don't see what will happen unless you look to the subroutine - and not only to the function definition, you need to read the complete code so that you can tell what parameter will manipulated and what not.
In software development, when you call a subroutine, you don't want to look at this subroutine - and often it is even not available for you. Let the subroutine do it's job, but without any side effects.
Use a function whenever possible, it keeps your code much more readable. Trust me...
There are (very few) cases when you want to receive more than one result from a subroutine. In that case, I would advice to put explicitly the keyword ByRef in front of the parameter (even if in VBA this is technically not necessary because it's the default). And put a comment that tells why it is the case. You will thank yourself when you look at your code weeks, months or years later.

VBA Calling function with / without Parenthesis [duplicate]

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.

How to restrict VBA function only on module

I have very few knowledge of VBA and trying to learn it. I have made a VBA script which is something like this:
Function doIt()
Dim c As int...
.............
.............
.............
c = callFunction
...............
..............
End Function
Function callFunction(byVal num As Int)
..........
..........
..........
End Function
as you can see callFunction is a function which is called from the main function doIt. Suppose callFunction calculates square of one integer. The whole VBA scripts is kept on an addIn Module under respective AddIns folder in C drive. The function doIt works well while called from an excel worksheet. But the problem is if I call the function callFunction from worksheet it also works. How can I restrict callFunction only to the addIn module so that only module can use it and if someone call callFunction(2) from worksheet it will not give the square of 2 in worksheet?
Note: even if I make it Private, it can still be called from the worksheet.
I don't think you can block function functionality which allows to access it both in VBA and Excel cell.
However, I have some workaround idea which allows you to create function which gives different results when it is called in Cell, e.g. some info (or standard error) could be returned instead of calculation result.
Here is the code presenting this functionality. I think it's quite clear and understandable so you would not need additional comments.
Function callFunction(ByVal num As Integer)
On Error Resume Next
Dim tmpAdd
tmpAdd = Application.ThisCell.Address
If Err.Number = 0 Then
If Left(Application.ThisCell.Formula, 13) = "=callFunction" Then
'called from excel cell, but this function is called!
'returns any type of standard function error
callFunction = CVErr(XlCVError.xlErrNull)
Exit Function
End If
End If
'called from VBA/IDE environment or from other function
'standard calculation
callFunction = num ^ num
End Function
Edit Inspired by #DanielDusek answer (but a bit incomplete one) I mixed both Daniel and mine solution into one. So, new and rather complete code:
Function callFunction(ByVal num As Integer)
If TypeName(Application.Caller) = "Range" Then
If Left(Application.ThisCell.Formula, 13) = "=callFunction" Then
'called from excel cell, but this function is called!
'returns any type of standard function error
callFunction = CVErr(XlCVError.xlErrNull)
Exit Function
End If
End If
'called from VBA/IDE environment or from other function
'standard calculation
callFunction = num ^ num
End Function
Both solution will give num ^ num result if used in any VBA function/subroutine whichever the place of calling (indirect use). It will give Error value when called in Excel cell (direct use).
With Application.Caller property you can determine who called your function and if it was called from worksheet you can raise error or return what ever you want but different from you propper calculated result.
Private Function callFunction(ByVal num As Integer) As Variant
If (TypeName(Application.Caller) = "Range") Then
' function was called from worksheet
callFunction = "Invalid procedure call" ' or raise error Err.Raise Number:=5
Exit Function
End If
' continue ...
callFunction = num * num
End Function
About Application.Caller: http://msdn.microsoft.com/en-us/library/office/ff193687.aspx

Returning user-defined types on Excel VBA

I've been fiddling with Excel VBA for some time, but I never had any formal training whatsoever. I have the following piece of code on a XLSM module :
Public Type ks_solution
W As Integer
NF As String
ID As Integer
End Type
Public Sub MySub()
//does some things
MyKSSolution = MyFunction(params)
End Sub
Public Function MyFunction(params) as ks_solution
//does stuff and returns a ks_solution
End Function
When I try to run the code, VBA compiler returns a "Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions" error.
Any help would be greatly appreciated.
--Yuri
Er, nevermind. Looks like the problem was in the var declarations.
I thought
Dim v1, v2 as ks_solution
was the same as
Dim v1 as ks_solution, v2 as ks_solution
but apparently, it isn't. In the first case, v1 gets declared as a Variant. Sorry to take your time.

Resources