VBA function "Object variable missing" error. Number 91 - excel

Excel keeps lighting it up with an "Object variable missing" error. Number 91.
Function GetMonthRange(sheetMonth) As Range
GetMonthRange = ActiveCell.Range("A1:AB1")
End Function
I'm pretty sure that Excel is maintaining its own clipboard.
Here's the link to the whole file.
https://github.com/okamura1967/Directors_project_sheet/blob/master/project-sheet-for-directors.vbs

There are several things wrong with your function.
1. If you want to return a range you have to use Set because Range is an object.
2. The parameter sheetMonth is not used
3. The function will return different results depending on whatever the activecell happens to be when the function is executed.
4. If this is a UDF it will not recalculate whenever anything in A1:B1 changes, because the A1:B1 is not a parameter.
What are you actually trying to do?

I changed your function to:
Function GetMonthRange() As Range
Set GetMonthRange = ActiveSheet.Range("A1:AB1")
End Function
This seems to work for me now.

Related

VBA Code to look up function in cell, store in variable and copy somewhere else

I'm trying to come up with a script that looks up a function in named range "Hard_Constr_Cost". Saves the function in variable "HC_Baseline" and then copies it to another range.
The reason why I don't just set one range equal to another is that after the function is stored I want to run a separate loop overwriting the value in range "Hard_Constr_Cost". At the end I want to overwrite with the original function stored in the variable "HC_Baseline".
Below is the script I created so far but I'm getting an out-of-range error. Does anyone know how to fix this? Any help is appreciated.
Sub HC_Sensitivity()
Dim HC_Baseline As String
HC_Baseline = Sheets("SU").Range("Hard_Constr_Cost").Formula
Sheets("SU").Range("T5") = HC_Baseline
End Sub

Trying to insert variable in match function in vba

I am getting an error while running this code, cannot understand what's wrong with this:
i = ActiveCell.End(xlUp).Offset(0, 1).Value
ActiveCell.Offset(0, 1).Value = Application.WorksheetFunction.VLookup(r, source, WorksheetFunction.Match("i", msource, 0), 0)
Application.WorksheetFunction invokes worksheet functions, but in the context of VBA code: normally in VBA, when a function throws an error, it comes in the form of a run-time error, and that's exactly what these functions do.
So you have two options.
One, handle (or swallow) VBA runtime errors:
On Error Resume Next
ActiveCell.Offset(1, 0).Value = Application.WorksheetFunction.VLookup(...)
On Error GoTo 0
Like this, when the right-hand side of the assignment throws an error (i.e. when the lookup fails), the left-hand side won't be affected and the target cell keeps whatever value it had before. Note the On Error GoTo 0, which restores runtime errors. This is critically important; without it On Error Resume Next will have your code running in some unhandled error state, and that is the single best way to hide bugs and make them pretty much impossible to diagnose later.
Two, use the late-bound version.
When invoked directly against Application, worksheet functions are late-bound. You don't get compiler assistance so watch out for any typos and make sure the parameters are good (you don't get a tooltip with a parameters list for late-bound member calls).
ActiveCell.Offset(1, 0).Value = Application.VLookup(...)
It's the same VLookup function, but now it behaves like it would on a worksheet, returning an error value instead of throwing a run-time error like a VBA function would. That makes the target cell value hold a #N/A worksheet error when the lookup fails.
Same applies to the inner Match function; now while nesting worksheet functions is how we do things in a worksheet cell's formula, doing that in code makes everything much harder than necessary - split things up, evaluate the Match separately, validate whether it returned an error value, then pass it to VLookup only if it didn't.
Dim matchResult As Variant
matchResult = Application.Match(i, msource, 0)
If IsError(matchResult) Then Exit Sub
Note that your code is passing the literal string value "i" to the Match function; you probably intend to pass the value of the i variable: you must remove the double quotes around it to do that.

Excel VBA Function stops execution before completion without error message

I set out to write a simple function to determine the length of a string in points. Having googled around I decided to avoid the font metrics problem by having excel do the work for me.
Here is the code.
Option Explicit
Function txtWidthPts(MyText As String) As Integer
'A cell on a working worksheet has been named "WidthTest" for easy reference & to ensure data is not overwritten.
'set WidthTest word wrapping off so that strings placed in there aren't wrapped
Application.ScreenUpdating = False
With [WidthTest]
.WrapText = False
.Value = MyText
'autofit WidthTest
.Columns.AutoFit
'get the width of the column
txtWidthPts = .Width
.ClearContents
End With
End Function
I tested the function by placing it in a cell on a working worksheet thus:
=txtWidthPts("Test123")
When I have this working I will be using it in code not as a worksheet function.
My problem is that the function does not throw an error and stops execution on the line:
.Value = MyText
I have placed the code and name into an empty workbook to ensure no interaction with other workbook contents / code.
I have searched extensively and tried various suggestions (DoEvents, Application.Update = False, etc, etc.) to no result.
I have cleared all breakpoints, closed and opened the workbook & restarted. I have tested with options set to Break on All Errors.
No result.
I suspect I am missing something obvious but it has me beat at the moment.
Any and all suggestions will be most welcome.
So, #YowE3K was right on the money. After fixing the error in the original code (Corrected code above) this runs fine from vba. I knew I was missing something obvious.
Curiosity sub-question: the function works as desired and indeed, as #YowE3K observed, it does not modify the Excel environment. However the result returned is dependent on it appearing to have modified the Excel environment. Seriously WTF. Just wanting to understand.
Thanks again YoWE3K.

Checking if cell is empty with VBA

Nom_1 is the name of a cell in my worksheet, and is used in the following code.
Someimes that cell is left empty and I don't want the NormInv function to get called because it will return an error if the inputs are empty.
I get the error: Unable to get the NormInv property of the WorksheetFunction class
This leads me to believe my if statement is incorrect and it is allowing for the code to enter and execute even if Nom_1 is empty.
If Not IsEmpty(Nom_1) Then
internal_1 = Application.WorksheetFunction.NormInv(Rnd(), N_1, Std_1)
End If
Am I correctly checking if the cell is empty?
Just Expanding on Barranka's comment,
You need to make sure all cells with the names Nom_1 ,N_1,Std_1,internal_1 exist in the workbook
then use Range("Nom_1") instead of Nom_1
Sub MyNormInv()
If Not IsEmpty(Range("Nom_1")) Then
Range("internal_1") = Application.WorksheetFunction.NormInv(Rnd(), Range("N_1"), Range("Std_1"))
End If
End Sub
You could use
If Range(Nom_1).Value <> vbNullString Then
internal_1 = Application.WorksheetFunction.NormInv(Rnd(), N_1, Std_1)
End If
However, know that this will return False if you have a formula returning a zero length string in the cell. Even though the cell is not "empty" it will still be seen as blank because the value is "".

Stop VBA Evaluate from calling target function twice

I am having trouble getting VBA's Evaluate() function to only execute once; it seems to always run twice. For instance, consider the trivial example below. If we run the RunEval() subroutine, it will call the EvalTest() function twice. This can be seen by the two different random numbers that get printed in the immediate window. The behavior would be the same if we were calling another subroutine with Evaluate instead of a function. Can someone explain how I can get Evaluate to execute the target function once instead of twice? Thank you.
Sub RunEval()
Evaluate "EvalTest()"
End Sub
Public Function EvalTest()
Debug.Print Rnd()
End Function
This bug only seems to happen with UDFs, not with built-in functions.
You can bypass it by adding an expression:
Sub RunEval()
ActiveSheet.Evaluate "0+EvalTest()"
End Sub
But there are also a number of other limitations with Evaluate, documented here
http://www.decisionmodels.com/calcsecretsh.htm
I don't know of a way to stop it, but you can at least recognize when it is happening most of the time. That could be useful if your computation is time consuming or has side effects that you don't want to have happen twice and you want to short circuit it.
(EDIT: Charles Williams actually has an answer to your specific quesion. My answer could still be useful when you don't know what data type you might be getting back, or when you expect to get something like an array or a range.)
If you use the Application.Caller property within a routine called as a result of a call to Application.Evaluate, you'll see that one of the calls appears to come from the upper left cell of of the actual range the Evaluate call is made from, and one from cell $A$1 of the sheet that range is on. If you call Application.Evaluate from the immediate window, like you would call your example Sub, one call appears to come from the upper left cell of the currently selected range and one from cell $A$1 of the current worksheet. I'm pretty sure it's the first call that's the $A$1 in both cases. (I'd test that if it matters.)
However, only one value will ever be returned from Application.Evaluate. I'm pretty sure it's the one from the second eval. (I'd test that too.)
Obviously, this won't work with calls made from the actual cell $A$1.
(As for me, I would love to know why the double evaluation happens. I would also love to know why the evaluator is exposed at all. Anyone?)
EDIT: I asked on StackOverflow here: Why is Excel's 'Evaluate' method a general expression evaluator?
I hope this helps, although it doesn't directly answer your question.
I did a quick search and found that others have reported similar behavior and other odd bugs with Application.Evaluate (see KB823604 and this). This is probably not high on Microsoft's list to fix since it has been seen at least since Excel 2002. That knowledge base article gives a workaround that may work in your case too - put the expression to evaluate in a worksheet and then get the value from that, like this:
Sub RunEval()
Dim d As Double
Range("A1").Formula = "=EvalTest()"
d = Range("A1").Value
Range("A1").Clear
Debug.Print d
End Sub
Public Function EvalTest() As Double
Dim d As Double
d = Rnd()
Debug.Print d
EvalTest = d + 1
End Function
I modified your example to also return the random value from the function. This prints the value a second time but with the one added so the second print comes from the first subroutine. You could write a support routine to do this for any expression.
I face the same problem, after investigation i found the function called twice because i have drop down list and the value used in a user defined function.
working around by the code bellow, put the code in ThisWorkbook
Private Sub Workbook_Open()
'set the calculation to manual to stop calculation when dropdownlist updeated and again calculate for the UDF
Application.Calculation = xlCalculationManual
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, _
ByVal Source As Range)
'calculte only when the sheet changed
Calculate
End Sub
It looks like Application.Evaluate evaluates always twice, while ActiveSheet.Evaluate evaluates once if it is an expression.
When the object is not specified Evaluate is equivalent to Application.Evaluate.
Typing [expression] is equivalent to Application.Evaluate("expression").
So the solution is to add ActiveSheet and to make that an expression by adding zero:
ActiveSheet.Evaluate("EvalTest+0")
After seeing there is no proper way to work around this problem, I solved it by the following:
Dim RunEval as boolean
Sub RunEval()
RunEval = True
Evaluate "EvalTest()"
End Sub
Public Function EvalTest()
if RunEval = true then
Debug.Print Rnd()
RunEval = False
end if
End Function
problem solved everyone.

Resources