CallByName not working for all objects/functions - excel

In VBA, most Excel functions are either accessible through Application.WorksheetFunction or VBA
Take sinh and sin for example:
Worksheet Function
Debug.Print Application.WorksheetFunction.sinh(5)
74,2032105777888
VBA Function
Debug.Print VBA.sin(5)
-0,958924274663138
Question:
Why does CallByName not work on both Worksheet functions and VBA functions?
Worksheet Function
Debug.Print CallByName(Application.WorksheetFunction, "sinh", VbGet, 5)
74,2032105777888
VBA Function
Debug.Print CallByName(VBA, "sin", VbGet, 5)

In VBA, most Excel functions are either accessible through Application.WorksheetFunction or VBA
No. Excel functions are accessible as late-bound member calls against the global Excel.Application object (if you're hosted in Excel), and then some have an early-bound "equivalent" (error handling strategy will need to differ) in the Excel.WorksheetObject interface (you get it from Application.WorksheetFunction indeed).
The members of the VBA library, global-scope or not, have nothing to do with Excel: the VBA standard library is referenced by every VBA project, regardless of its host application (Word, Excel, Access, ...SolidWorks, Sage300, etc.). If a function looks like it exists in both the VBA and the Excel libraries, the VBA function should probably/theoretically be preferred.
Use the object browser (F2) to discover the members of the VBA standard library, including and perhaps particularly its Math module.

The call signature of CallByName... which is actually a member of VBA.Interaction as seen below (so your snippet is equivalent to VBA.Interaction.CallByName(VBA, "sin", VbGet, 5) or just VBA.CallByName..., in any case a side point):
is
CallByName(Object As Object, ProcName As String, CallType As VbCallType, Args() As Variant)
As VBA is not an Object, but the standard VBA library, this throws a type mismatch error.

Related

Defining variables across Subs (w/in module) in VBA

Would like to define reference variables (calls a value from a cell in the sheet using ActiveSheet.Cells[row, col]) in one location in a module, for use across multiple subs in an MS Excel file. The file is an action tracker, the subs automate some of the emailing (each subs opens emails under given conditions). All reference variables are the same for each sub - defining in one place will make maintaining the spreadsheet much simpler.
Tried to define variables above first sub, error message appears on first value (as detailed below). I've searched (a) Global Variables and (b) how to define above the subs. However (a) variables all in the same module (b) error message as detailed below. I haven't located a guide on defining variables using ActiveSheet.Cells() references.
Option Explicit
'Defines variables for all macros:
'Defining Reference Variables
Today = ActiveSheet.Cells(2, 4)
ActionLogTitle = ActiveSheet.Cells(3, 3)
IPT_Leader = ActiveSheet.Cells(7, 7)
(On Today = ActiveSheet.Cells(2,4) error highlights on "2")
Compile error:
Invalid outside procedure
As the compiler is hinting, you cannot write assignments outside of a Sub/Function.
You can declare a function for each variable:
Function MyValue()
MyValue = ActiveSheet.Cells(2, 4).Value
End Function
Ideally you don't use ActiveSheet unless that's really what you want though.
There are a lot of ways to define today, but using a word, which is used by Excel English formula =TODAY(), is probably a discussable idea (although it will work!). In general, consider declaring the variable like this somewhere in the modules:
Public myToday as Date
Then, you may reset it everytime the worksheet is openned:
Private Sub Workbook_Open()
myToday = Date
'or
myToday = Worksheets("Name").Range("D2").Value
End Sub
Anyway, working with Public variables is in general discouraged in any programming language, thus it is probably a better idea to come up with a dedicated function or class for it.

How to run my macro in an excel application created by vba Access 2016

I have an excel application that is created by VBA access. I want to create a certain macro in this excel application so that certain cells (also determined by vba access) can call it.
Say I want to add a macro called myMacro with three integer parameters. I know I have to create it like so somewhere:
Function myMacro(A as Integer, B as Integer, C as Integer) As Double
...
End Function
And I know to call that I have to have my program write something like this into the cell:
=myMacro(1,2,3)
But how do I make this macro actually known to the excel application?
My gut feeling is that it has to be inside of the excel's application vba code but is there anyway to do this from the Excel.Application object in vba access?

Which is faster in Excel VBA: InStr or Find?

I've got a function which uses InStr() to locate a character in a string, but I know there is a built in function in Excel called FIND(). Can anyone advise which is faster or more cpu efficient?
hours_position = InStr(1, value, " ")
vs
hours_position = Application.WorksheetFunction.Find(" ", value, 1)
Both are same and it does the same action.
Most (but not all) worksheet functions can also be called from VBA. For example, you can use the VLOOKUP worksheet function in VBA by calling Application.WorksheetFunction.VLookup (or Application.VLookup).
Similarly, you can use Application.WorksheetFunction.Find and Application.WorksheetFunction.Search. You can use them to emulate the way the worksheet functions work in your VBA code.
These functions are only available in Excel VBA, whereas InStr is a generic VBA function, available in all Office applications (and in VB6, VB.NET etc.)
Apart from that, the Range object in Excel VBA has a Find method, and the Worksheet object has a Find object. These, however, serve a different purpose: you can't use them to search for text within a string, but to search for cells with specified content.

Spreadhseet code reuse

Does anyone know of a way to wrap up a worksheet either as a UDF function?
Essentially I'd like to create a worksheet or workbook which carries out certain calculations and then reuse this code in other worksheet or workbooks. Ideally the UDF would set the value of certain input cells and return a value from a certain output cell.
There is a hack in the answer to this question, but it doesn't work well.
Using a UDF in Excel to update the worksheet
Ideally I'd like to do this in Excel, but am receptive to suggestions of alternative spreadsheet software, third party excel tools or alternative platforms entirely.
UDFs are not designed to change the value of any cell other than the one it is being used in.
There are hacks for this that work in some use-cases. That is not a design feature of the UDF, however, but rather clever manipulation of other designs in Excel. In any case, I think most will agree that these types of hacks can be unstable and surely not recommended for production use.
If you want to change more than one cell at the time, you are best of writing a Sub. This gives you more control, the behavior is well-documented and overall your calculations do not rely on unofficial work-arounds that may or may not break in any given patch.
I've found an answer to my own question. It appears that UDFs cannot change cell values in the excel instance they are called from. The behaviour I want can be achieved by creating a new instance of Excel and openinf a copy of the current workbook in the second instance. The first instance can then call a UDF which modifies the second instance. Thus the calculations within a spreadsheet can be successfully wrapped up in a UDF.
Option Explicit
Public xl As Excel.Application
Public wb As Workbook
Public ws As Worksheet
Function calc(x As Double) As Double
If xl Is Nothing Then
Set xl = CreateObject("Excel.Application")
Set wb = xl.Workbooks.Open(ThisWorkbook.FullName)
xl.Visible = False
Set ws = wb.Worksheets("CalcluationModule")
End If
ws.Range("i").Value = x
wb.Application.Calculate
calc = ws.Range("PV").Value
End Function
That's true ' sort of...
However, your UDF can call a function that actives Win API timer, using the cell reference you called it from. The callback function can then do what you want to that cell...

Possible to tell which workbook called a function in an Excel Add-In (xla)

I want to write a little logging function in an excel add-in that I will be calling from many different workbooks. I'd like to be able to just call it by passing only the log text, and the log function itself could handle the timestamp, workbookname, etc.
However, I cannot use either ThisWorkbook or ActiveWorkbook to determine which workbook was responsible for making the call, as Thisworkbook will return a reference to the add-in itself, whereas VBA code running in a workbook other than the workbook with active focus in Excel could make the call, but the ActiveWorkbook will return the one that has focus in the window.
Application.Caller looked like a possible solution, but this seems to work only when the function is called from a cell, not from VBA.
Is what I'm trying to do impossible?
Update
According to > 1 person, this is in fact impossible. If anyone happens to know some clever workaround please speak up.
Ok, so having read the question properly I'll try again...
So, to state the problem:
you want a routine written in an addin, that when called from vba in another workbook can work out (among other things) which workbook contains the vba that made the call, without having to pass this information explicitly.
As stated this is not possible (this is a similar question to accessing the call stack from code: something that is to my knowledge not possible)
However you can almost get what you want like this
Declare your log function like this:
Sub MyLogger(wb as Workbook, LogText as String)
Dim CallerName as String
CallerName = wb.name
' your code...
End Sub
Then wherever you call the sub use
MyLogger ThisWorkbook, "Log Text"
Not quite as good as passing nothing, but at least its always the same
To get the name of the calling workbook, use
Application.Caller.Worksheet.Parent.Name
Application.Caller returns information about how Visual Basic was called. If called from a custom function entered in a single cell, a Range object specifying that cell is returned
Having got a reference to the cell, .Worksheet.Parent.Name gives you the name of the workbook
Note that Application.Caller will return other things depending on how your function is called (see VBA help for details)
In an Add-In Function called by an Excel Worksheet Array Entered Function Call, I find that "Application.Caller.Parent.name" gives the Sheet Name (Tab Name, not sheet number).
I had the same issue when coding a custom function. Function works well, but anytime another workbook is calculated or activated, all cells using that function revert to #value. It can be very frustrating when working with multiple files using this formula.
To get the Workbook I used:
Dim CallingWb As Workbook
Set CallingWb = Application.Caller.Parent.Parent
This should work if your function is in a cell.
Too late for the original post, but might help others!

Resources