Calling a Sub or Function contained in a module using "CallByName" in VB/VBA - vba

It is easy to call a function inside a classModule using CallByName
How about functions inside standard module?
''#inside class module
''#classModule name: clsExample
Function classFunc1()
MsgBox "I'm class module 1"
End Function
''#
''#inside standard module
''#Module name: module1
Function Func1()
MsgBox "I'm standard module 1"
End Function
''#
''# The main sub
Sub Main()
''# to call function inside class module
dim clsObj as New clsExample
Call CallByName(clsObj,"ClassFunc1")
''# here's the question... how to call a function inside a standard module
''# how to declare the object "stdObj" in reference to module1?
Call CallByName(stdObj,"Func1") ''# is this correct?
End Sub

I think jtolle's response addressed the question best - the small reference to Application.Run may be the answer. The questioner doesn't want to use simply func1 or Module1.func1 - the reason one would want to use CallByName in the first place is that the desired function.sub name is not known at compile time. In this case, Application.Run does work, e.g.:
Dim ModuleName As String
Dim FuncName As String
Module1Name = "Module1"
FuncName = "func1"
Application.Run ModuleName & "." & FuncName
You can also prepend the Project Name before the ModuleName and add another period ".".
Unfortunately, Application.Run does not return any values, so while you can call a function, you won't get its return value.

Although it is an old question and OP asked for CallByName in a standard module, the correct pieces of advice are scattered through answers and comments, and some may not be that accurate, at least in 2020.
As SlowLearner stated, Application.run DOES return a Variant, and in that way both branchs below are equivalent, except by handling errors, as commented around Horowitz's answer:
Dim LoadEnumAndDataFrom as Variant
'FunctionName returns a Variant Array
if fCallByName then
LoadEnumAndDataFrom = CallByName(ClassObj, "FunctionNameAtClass", VbMethod)
else
'After moving back function for a standard module
LoadEnumAndDataFrom = Application.Run("StandardModuleName" & "." & "FunctionNameAtStandard")
endif
I actually just did this above and had no errors at all, tested in Word, Excel and Access, and all return the same Array.
Unfortunately, there is an exception: Outlook's object Model is too protected and it does not have the Run method.

CallByName works only with class objects.
If your subroutine is in a standard module, you can do this:
Sub Main()
Module1.Func1
End Sub
If it's a function, then you'll probably want to capture the return value; something like this:
Sub Main()
Dim var
var = Module1.Func1
End Sub

Modules in VB6 and VBA are something like static classes, but unfortunately VB doesn't accept Module1 as an object. You can write Module1.Func1 like C.Func1 (C being an instance of some Class1), but this is obviously done by the Compiler, not at runtime.
Idea: Convert the Module1 to a class, Create a "Public Module1 as Module1" in your Startup-module and "Set Module1 = New Module1" in your "Sub Main".

Unfortunately it is not possible to prepend the ProjectName before the ModuleName and add another period "." In MS Word this throws a runtime error 438. The call is restricted to the use of simply ModuleName.ProcName.

Related

Custom Function Disable in Excel

I have created a custom function that I am using excplicitly in another module in VBA. Function looks something like this:
Function Coverts(ByVal inputString As String) As String
'Coverts code here
End Function
It works perfectly fine in both VBA and Excel UI. However, I do not want it to work or appear in Excel UI as I only want to use it in VBA. Is there a way to do this?
Thanks
Put
Option Private Module
at the top of the module containing your function.
From MSDN:
When a module contains Option Private Module, the public parts, for example, variables, objects, and user-defined types declared at module level, are still available within the project containing the module, but they are not available to other applications or projects.
you can add the keywords Public or Private to your functions, subs or global variable to have that specified.
so if you want to only want this function to be accessible by your code and not in excel sheets you can add private:
Private Function Coverts(ByVal inputString As String) As String
'Coverts code here
End Function
You can make the function inoperable if called from the worksheet by identifying the Application.Caller. If called as a function from the XL UI, this will be Range (i.e. the cell the function is within).
Function Coverts(ByVal inputString As String) As String
If TypeName(Application.Caller) = "Range" then
Coverts = cverr(xlerrna)
exit function
end if
'Coverts code here
End Function

Call VBA Class Module in Excel formula

I know that a function of a Module can be called in a formula like this:
=GetMyModuleFunction()
Now I would like to call a Class Module function. I don't know if a Class Module can be instantiated in a formula at all so I've created a function in a Module so I can call it just like =GetMyModuleFunction():
' Class Module MyClassModule:
Public Property Get MyProperty() As String
MyProperty = "Hello World!"
End Property
Public Function GetMyFunction() As String
GetMyFunction = "Hello World!"
End Function
' End Class Module MyClassModule
' Module MyModule:
Public Function GetMyClassModule() As MyClassModule
Set GetMyClassModule = New MyClassModule
End Function
' End Module MyModule
So after that I tried in the formula bar:
=GetMyClassModule().GetMyFunction()
=GetMyClassModule().MyProperty
Which shows an error dialog that the formula is invalid. Is it not possible what I'm trying to achieve here? Currently I use Modules instead but functions and subs with duplicate names are confusing and error prone to use in Modules..
Your question is similar to the question asked here: Call VBA function that returns custom type from spreadsheet
You can only return data types that Excel understands from a user-defined function. Excel does not understand custom data types.
But you can wrap your class properties or functions with a regular module function (UDF) like so:
Public Function GetMyClassModuleFunction() As String
GetMyClassModuleFunction = GetMyClassModule.GetMyFunction()
End Function

Can an Excel VBA dictionary be used to call a function?

I have a large number of functions that I need to call which each have the same arguments and I'd like to be able to centralize them to avoid unwieldy blocks of code. Obviously I could use a wrapper function to just call all the others, but I don't always call all of them. My next thought is that I could place the functions in a list or dictionary and call them from there, much like in Python:
def foo():
return "foo"
myDict = { "foo": foo }
myDict["foo"]()
Returns foo
Is something similar possible in VBA? If so, what is the simplest way of doing it?
If the functions are inside objects/Classes then you can call them by name directly.
The below code works inside SHEET1 because it's an object.
It will not work in a MODULE as that is outside the capabilities of CALLBYNAME function.
So you can store the Names in the Key of the dictionary and then just use the key (no function pointer is needed)
Public Sub Something(arg1 As String)
MsgBox arg1
End Sub
Public Sub test()
CallByName Sheet1, "Something", VbMethod, "data 1"
End Sub
There does not appear to be a really straight forward way of doing this, however, it can be done using a combo of a list and the CallByName function.
You can make a list which contains all the function names as strings:
myFunctions = ( "foo" "bar" "baz" )
You can then iterate over the array to call the functions:
For Each functionName in myFunctions
CallByName Sheet1, functionName, VbMethod, listOfArguments
Next functionName
Functions can be filtered out with If statements

VB6.0 call a Sub written in a BAS Module from a Class Module CLS not working

I am writing VB6.0 projects (DLL with COM+) starting from previously written code.
I have a "main" Class Module CLS file with "main" Functions, and the Process flow etc.
I have also a "side" Module BAS where I save all the Functions / Subroutines to use as tools in my "main" Class Module.
I have written a very very straightforward logging system (because I feel very unconfortable with App.LogEvent("blablabla") ) but I am not able to compile the DLL. The message points me to the CLS call and I think the problem is related to the Sub that should return a value, but I do not want to return any value from that Sub!
I am quite new to VB6.0 and improving existing source code it is quite difficult.
Here is my Module BAS
Public Sub LogMyApp(ByVal sFunctionName As String, ByVal sLogEntry As String)
Dim sLogPath As String
sLogPath = "C:\Temp\MyLog.txt"
Dim fn As Integer
fn = FreeFile
Open sLogPath For Append As #fn
Write #fn, Now & "|" & sFunctionName & "|" & sLogEntry
Close #fn
End Sub
Here is my Class Module CLS call to that Sub inside the BAS Module
LogMyApp ( "FunctionBlaBla" , "blablabla" )
Any help is really appreciated!
Thanks a lot!
Simple fix, remove the parens as you are not calling a function;
LogMyApp "FunctionBlaBla" , "blablabla"
(Or as purely a visual thing prefix with the Call keyword; call LogMyApp(...))

What does the Call keyword do in VB6?

There's some code in our project that looks a bit like this:
Private Sub Method1()
Call InnerMethod
End Sub
Private Sub Method2()
InnerMethod
End Sub
Private Sub InnerMethod()
'' stuff
End Sub
What's the advantage of doing Method1 over Method2?
From the MSDN:
You are not required to use the Call
keyword when calling a procedure.
However, if you use the Call keyword
to call a procedure that requires
arguments, argumentlist must be
enclosed in parentheses. If you omit
the Call keyword, you also must omit
the parentheses around argumentlist.
If you use either Call syntax to call
any intrinsic or user-defined
function, the function's return value
is discarded.
For example:
Sub Proc1()
Debug.Print "Hello World"
End Sub
Sub Proc2(text As String)
Debug.Print "Hello " & text
End Sub
In the immediate window, if you enter
Proc1
then "Hello World" prints. If you enter
Call Proc1
then "Hello World" prints. If you enter
Proc2 "World"
then "Hello World" prints. If you enter
Call Proc2 "World"
you get a compile error. You would have to enter
Call Proc2("World")
Call does nothing special other than call the method. It is a hang over from the old days of Basic when all lines had to start with a keyword. "Let" is another of these keywords, which was always put before an assignment, but is no longer required.
Method1 and Method2 do the exact same thing.
I have found a major difference about 'call' keyword with functions that having, ByRef Arguments (I have found this in MS-Access VBA editor). If you are calling the function without 'Call' keyword, ByRef aruments will not set for the calle. For Ex:
Private Function Test(Optional ByRef refArg As String) As Boolean
refArg = "Test"
Test = True
End Function
If you call the function without the Call keyword like
Dim a As String
Test(a)
a will be an empty string, after the call returns
If you call the function with the Call keyword like
Dim a As String
Call Test(a)
a will contain the string Test
The detailed explanation provided in the following link:
Cannot use parentheses when calling a Sub
There's no difference.
Here's a post which describes when you need to use call vs not using it and when to parentheses around your parameters.
You can also read more about call from MSDN. Essentially the main difference is that when you use call to call a function you can't access the return value.