vba worksheetfunction memory address - vba

I have written a dll which requires a pointer to a function with a given signature, say
function func(x as double) as double
...
end function
I can write such a function (say Asinh) in vba and pass it with the AddressOf... vba method, and it works with no problems.
But Asinh is a function already existing in the Excel object WorksheetFunction, and it looks silly to me to write (many) proxy vba functions just to call existing functions. It would be nicer to pass directly the address of such worksheetfunctions. However:
AddressOf does not work for (say) WorksheetFunction.Asinh
I can get the address of WorksheetFunction with ObjPtr, but from there I'm not able to go on to the Asinh function address.
A related problem is to reuse a function (with same signature) defined in an external dll, but at least in this case it is possible to resolve it with LoadLibrary / GetProcAddress, sort of
function ExternalAddressOf (dllname, funcname) as LongPtr
....
end function
Or, in case of own-written dll, to implement a function table and use in vba symbolic constants. However, I'd really like to be able to reuse functions already defined in WorksheetFunction. Need help.

Related

Calling Excel's worksheet function from vba code

With worksheetfunction's methods I can call from vba code a lot of excel's function without reinvent the wheel.
Unfortunately not all function are available there but other simple function can be find under vba library.
Now I need to use two functions:
address()
indirect()
But none of two is available as method of vba or worksheetfunction
(here what is available: https://msdn.microsoft.com/en-us/library/office/ff822194(v=office.14).aspx)
Using the object browser on the editor I can't find those functions...
how can I do?
INDIRECT is a way of resolving a string, this can be done in VBA easily. ADDRESS can also be found as a member of a Range object. That's why they are not available.
Here is a tiny example:A1 contains the text B1B1 contains the text GoldC1 contains the formula:=INDIRECT(A1):
Running this macro:
Sub UsingEvaluate()
MsgBox Evaluate("INDIRECT(A1)")
End Sub
will produce:

Excel VBA: How to return value without making a function that is available in the formula bar

From what I understand out of subs, functions, and labels the only thing that returns values is functions right?
However when you make a function is will make it available in the spreadsheet when you type = in the formula bar. Is there anyway around this? I don't necessarily want an accessible function, I just want somewhere to stick repetitively used code & return a value.
Add The keyword Private in your function declaration
Private Function DoSomething()
...
End Function
The default is Public
You can read more on VBA Functions & Subroutines

Excel VBA store functions or subroutines in an array

In C/C++, when I have a bunch of functions (pointers), I can store them in an array or a vector and call some of them together in a certain order. Can something similar be done in VBA?
Thanks!
Yes, but I don't recommend it. VBA isn't really built for it. You've tagged this question with Excel, so I will describe how it is done for that Office Product. The general concept applies to most of the Office Suite, but each different product has a different syntax for the Application.Run method.
First, it's important to understand the two different methods of dynamically calling a procedure (sub/function) and when to use each.
Application.Run
Application.Run will either run a subroutine or call a function that is stored in a standard *.bas module.
The first parameter is the name of the procedure (passed in as a string). After that, you can pass up to 30 arguments. (If your procedure requires more than that, refactor for the love of code.)
There are two other important things to note about Application.Run.
You cannot use named arguments. Args must be passed by position.
Objects passed as arguments are converted to values. This means you could experience unexpected issues if you try to run a procedure that requires objects that have default properties as arguments.
Public Sub Test1()
Application.Run "VBAProject.Module1.SomeFunction"
End Sub
The takeaway:
Use Application.Run when you're working with a standard module.
VBA.Interaction.CallByName
CallByName executes a method of an object, or sets/gets a property of an object.
It takes in the instance of the object you want to call the method on as an argument, as well as the method name (again as a string).
Public Sub Test2()
Dim anObj As SomeObject
Dim result As Boolean
result = CallByName(anObj, "IsValid")
End Sub
The takeaway:
Use CallByName when you want to call a method of a class.
No pointers.
As you can see, neither of these methods use actual pointers (at least not externally). They take in strings that they then use to find the pointer to the procedure that you want to execute. So, you'll need to know the exact name of the procedure you want to execute. You'll also need to know which method you need to use. CallByName having the extra burden of requiring an instance of the object you want to invoke. Either way, you can stores these names as strings inside of an array or collection. (Heck, even a dictionary could make sense.)
So, you can either hard code these as strings, or attempt to extract the appropriate procedure names at runtime. In order to extract the procedure names, you'll need to interface with the VBIDE itself via the Microsoft Visual Basic for Applications Extensibility library. Explaining all of that here would require far too much code and effort, but I can point you to some good resources.
Articles & SE Questions:
Chip Pearson's Programming The VBA Editor
Extending the VBA Extensibility Library
Ugly workaround to get the vbext_ProcKind is breaking encapsulation
Automagic testing framework for VBA
How to get the procedure or function name at runtime
Import Lines of Code
Meta Programming in VBA: The VBIDE and Why Documentation is Important
The code from some of my Qs & As:
vbeCodeModule
vbeProcedure
vbeProcedures
A workaround is to enumerate and use a switch statement. You can store enumerated types (longs) in an array. E.g.:
Enum FType
func1
func2
func3
End Enum
Sub CallEnumFunc(f As FType, arg As String)
Select Case f
Case func1: MyFunction1(arg)
Case func2: MyFunction2(arg)
Case func3: MyFunction3(arg)
End Select
End Sub
Dim fArray(1) As FType
fArray(0) = func1
fArray(1) = func2
CallEnumFunc fArray(1), "blah"

VBA: passing variables (from worksheet) into functions

A quick one for noob like me!
So usually I pass in some range of data from worksheet into function like that:
public function foo (someRange as range)
dim someData as double
if IsNumeric(someRange.value), do some crap
end function
The problem occur when I try to code some function that uses this function foo. Instead of range i would like to pass in a double(). So if I were to fix this, i can either:
a. I have seen some other site uses "someRange as Variant" instead (which then avoid problem like i face). However, is it "good coding practice"?
b. use a wrapper function foo_wrap instead
You could use a Variant and then TypeName to find out what kind of argument was supplied:
?TypeName(range("A1"))
Range
?TypeName(45.66)
Double
?TypeName(array(34.5,56.7))
Variant()
Personally, I would just create two separate Functions. Using one function would just be messy to me. For a single function NOT to just duplicate code, it would have to extract all the Range values into a Double array. This just tells us that it should be two functions.
Creating a wrapper-function is an option, but I'll leave you to decide whether this is a good solution for you.
If you did want to pursue a multi-purpose function then you could investigate ParamArray:
Used only as the last argument in arglist to indicate that the final
argument is an Optional array of Variant elements.
This would allow you to create a Function that behaves similarly to the built-in functions (SUM, etc.) which can accept a variable number of arguments, which can be Ranges and/or values. However, particularly as you are a noob, I would ignore this possibility for a while. The KISS principle.

What is a public object module in VBA?

I'm trying to get as close to function pointers / abstract classes as I can in VBA.
I have a class called VerificationManager and verifies a bunch of cells in a couple of spreadsheets match up. This will be done in different ways depending on the information and spreadsheets being used with it.
I'd like to be able to make the code reusable by specifying a method to be called in a string using the Application.Run function. So I can rewrite the function that changes.
Now if I was using Java or C# I would be able to extend an abstract class and rewrite the internals to the function. If I was using JavaScript I could store a function in a variable and pass the variable to the class and call it from there.
Inside my class I have a public property called "verificationModule" which I set to the name of the function I want it to call.
Sub VerifyWorkLocations(empLoc As EmployerLocation)
...
For i = 0 To empLoc.numOfEmp
Application.Run verificationModule, empLoc.taxdescmatch, empLoc.employees(i)
Next i
...
End Sub
However, when I try to call Application.Run I receive the following error:
Compile Error:
"Only user-defined types defined in public object modules can be
coerced to or from a variant or passed to late-bound functions"
I already tried placing my User Defined Types in a Class Module but it basically said that a class module was the wrong place for a type.
The error comes from full-fledged VB, where you can create an ActiveX dll project, create a public class there and put a UDT into that class.
In VBA, you use classes instead of UDTs when you need to coerce to or from a variant.
So just declare a class with all the fields you have in your UDT, and delete the UDT.
Alternatively, create a DLL in VB6 that would only contain the declaration of the type, and reference that dll from VBA. Or, if you're comfortable with IDL, just create a TLB file directly.
To add a module to a VBA application, try the following steps (assuming you've already entered the VBA IDE):
From the Project Explorer, right-click on your project.
Click on "Insert..."
Select "Module."
If no modules exist, a new folder within the VBA/Excel project will be created, named "Modules," and a module with a default name of "Module1" will be created.
Double-click the module to open it in the source editor.
This is where you have to place UDT's. I believe this is because of the difference in the way such types are stored internally by VBA relative to the COM-style structure of elements/objects declared/manipulated as Classes...