Trying to insert variable in match function in vba - excel

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.

Related

EXCEL VBA Type mismatch with "Next" highlighted

I'm creating small project in Excel, and because I'm a VBA newbie I do encounter a lot of problems that I'm trying to resolve on my own. However i can't cope with this:
I created Sub that accepts two objects: FormName and ControlName.
What i want it to do, is to loop through every Control in specific UserForm and populate every ListBox it encounters, from another ListBox.
I created this funny string comparison, because I need to operate on objects in order to execute the line with AddItem. This comparison actually works well, no matter how ridiculous it is. However when I launch the program, I got
Type Mismatch error
and to my surprise "Next" is being highlighted. I have no idea how to fix this, nor what is wrong.
Public Sub deploy(ByRef FormName As Object, ByRef ControlName As Object)
Dim i As Integer
Dim O As msforms.ListBox
i = 0
For Each O In FormName.Controls
If Left(FormName.Name & O.Name, 16) = Left(FormName.Name & ControlName.Name, 16) Then
O.AddItem (FormName.PodglÄ…d.List(i))
i = i + 1
End If
Next
End Sub
I call this sub using:
Call deploy(UserForm1, UserForm1.ListBox3)
Above, I use Listbox3 because otherwise i got error saying that variable is not defined. However in my comparison I kinda override this.
If someone can explain in simple words, how to fix this type mismatch issue or how to write it in more elegant way

How should VBA.CVErr() be used if it cannot support the Long vartype of vbObjectError..?

I'm in Access 2007 VBA, trying to return an #ERROR value from a function, as shown in the code below. But I've just discovered the largest number which VBA.CVErr(expression) will accept is 2^15-1, aka an Integer vartype; not a Long.
This seems incomprehensible, since the VBA constant vbObjectError is a Long. Other error functions work with longs; for instance: VBA.Error(vbObjectError) works fine.
In light of this issue, what suggestions are there to properly make use of vbObjectError to return user-defined errors as error objects from user-defined functions..?
Public Sub TesUDE()
Dim v As Variant
v = UDE()
Debug.Print TypeName(v), VBA.CStr(v)
End Sub
Public Function UDE() As Variant
On Error GoTo ErrorHandler
err.Raise 2 ^ 15 - 1 , , "This is a user-defined error." 'Works.
err.Raise 2 ^ 15 , , "This is a user-defined error." 'Overflow.
err.Raise vbObjectError, , "This is a user-defined error." 'It laughed at me.
ErrorHandler:
UDE = VBA.CVErr(err.Number)
End Function
The vbObjectError constant is useful to ensure your custom error numbers never colliding with a "built-in" error number, which makes error handling more robust in a way: it ensures error e.g. #91 consistently means "object reference not set", for example.
It implies custom errors are thrown/raised and handled, though - not returned.
Don't get me wrong: returning an Error-type value does have legitimate uses; like when you're writing a user-defined worksheet function and need Excel to distinguish between e.g. "an invalid reference was provided" (#REF!), "no match was found for the specified value" (#N/A), or "I've no idea what you're talking about" (#NAME?); in the Excel type library each of these errors have a corresponding XlErrXxxxx global constant defined, with an underlying value in the low 2000's.
It's entirely possible there's a similar use case in Access (I'm not all that familiar with Access), meaning the caller receiving the error is the Access query engine, much like the caller of a UDF in Excel is Excel's calculation engine.
Otherwise (i.e. if the caller is other VBA code), returning an error amounts to using the Error type for flow control, and making things return Variant meaning "this function might return a meaningful value of some type, or some error, maybe"... generally makes the code harder to read/follow.
So in use cases where the caller isn't your own VBA code, for error codes you mean to return as Error-type values (which is much cleaner than returning some magic non-zero Long number with the same meaning), you will want to skip the vbObjectError part.
Think of vbObjectError errors as "internal errors" that your VBA code handles, and Error/CVErr errors as "user-facing errors" that your VBA code returns. As come sort of self-inflicted convention =)
If the error you mean to expose is an actual custom VBA error code that you handle elsewhere in your VBA project, you'll want to "map" it to a finite-set of "user-facing" error codes - probably by defining constants, or enums for them:
Private Const ERR_CUSTOM_ERROR_1 = vbObjectError + 42
Public Enum UserFacingError
ErrFooWasNotBarred = &H7E1
ErrSomething
ErrSomethingElse
End Enum
'...
Public Function DoSomething(ByVal foo As Long) As Variant
On Error GoTo ErrHandler
'..."happy path"...
Exit Function
ErrHandler:
Select Case Err.Number
Case 5 'Invalid procedure call/argument
DoSomething = CVErr(ErrFooWasNotBarred)
Case ERR_CUSTOM_ERROR_1
DoSomething = CVErr(ErrSomething)
Case Else
DoSomething = CVErr(ErrSomethingElse)
End Select
End Function

Excel VBA On Error error

I am rather new at programming, and while learning Python also started experimenting with Excel VBA. I have an issue with the last one.
I have some large Excel sheets and tried to validate that data in specific columns matches data on another sheet in certain columns as they will be supposed to relate to each other by these values (and will be connected by a third value). To make this a bit more difficult, both of these columns may contain more than one value separated by "|". So, I have split these values in a list and I try to iterate through them to make sure all these values are set correctly, the connection will work fine.
All is fine as long as all is fine :) I have however an issue where there are two values in one of those columns and only one in the other. I would like this discrepancy to be noted on a sheet and then proceed to the next item.
The way that seemed to be applicable for me is to use "On Error GoTo ErrHandler", then note error on another sheet, and then user Resume to proceed.
Here is what I came up with:
For h = 0 To UBound(Split1())
For j = 1 To GetMaxRow("SpecificSheet", A)
On Error GoTo ErrHandler:
If Sheets("SpecificSheet").Cells(j, 1).Value = Split1(h) And Sheets("SpecificSheet").Cells(j, 2).Value = Split2(h) Then
DependencyOk = DependencyOk + 1
End If
Next j
Next h
ErrProceed:
Also ErrHandler is:
ErrHandler:
Sheets("Issues").Cells(x, 1) = "IssueDescription"
GoTo ErrProceed
It stops at line 2 with Subscript out of range for Split2(h) rather than moving on to ErrHandler and then ErrProceed. I have the feeling this must be something very obvious but I am just unable to get this working, and I am not able to find other way (like a try/except) in Excel VBA.
UPDATE:
Trying to clarify things a bit. The root of the issue is, that the Split2 list is shorter than Split1 - which is an issue with the input data and I'd like to capture this. I get the Split values from cells, where the values are separated by "|" characters:
CellValue = Sheets("SomeSheet").Cells(RowNumber, ColumNumber)
CellValueSplit() = Split(CellValue, "|")
And then iterate as:
For h = 0 To UBound(Split1())
So as Split1 moves on to the for example 3rd value, Split2 throws error and script stops. The best I was able to do so far was, that I let it proceed with the loop, but as this is a rather large sheet, it will fill the same error report ca. 200k times in this case, which I'd like to avoid. So I'd prefer it to proceed from after this loop once it hits out of range error, and proceed examining the next value.
Thank you for your help so far and in advance!
You have an issue with your syntax. The proper Error statement syntax is:
On Error GoTo <string>
On Error Resume Next
On Error GoTo 0
When using On Error GoTo <string> there is no ":" at the end. The ":" doesn't come into play until you create the target location. Example:
On Error GoTo Here
'// ---- Do something ---- //
Here:
'// ---- Handle the error ---- //
If you use On Error Resume Next, then you're telling the machine to ignore errors and proceed on to the next line of code.
When you useOn Error Return To 0, VBA will reset its error handling back to default. It's a good habit when using On Error Resume Next to insert On Error Return To 0 as soon as you no longer need it. On Error Resume Next has a real potential to break your code and make it behave strangely. Not to mention debugging can be a real nightmare. Check out the VBA manual from Microsoft for a more detailed explanation.
Finally, if your question is answered, you should mark it as answered.
vba-excelvbaexcel
The short and quick version is that VBA Error Handling Routine's only handle errors in the actual code execution, they do not fire when conditions expressed by the code are not met.
In your case, you do not need any error handling at all. In most cases it is actually best to avoid On Error GoTo .... There are cases where it's inevitable, but they are rare.
Try this IF THEN ELSE block:
If Sheets("SpecificSheet").Cells(j, 1).Value = Split1(h) And Sheets("SpecificSheet").Cells(j, 2).Value = Split2(h) Then
DependencyOk = DependencyOk + 1
Else
Sheets("Issues").Cells(x, 1) = "IssueDescription"
End If
Actually I have just found the issue. It was caused by a ":" left after an If statement a few rows earlier. I still don't really understand what it did, but I suggest not to reproduce it :)

VBA that finds duplicate information hitting Run-time error 91

Trying to use a form to create a row that stringes together two values in a concat using a string. Upon clicking the finish button the macro checks several values to determine if it can place in the information. One of these rules I am trying to set is detecting if the string/value already exists.
ID = txtStory.Value & "." & txtTask.Value
If Range("A7:A98").Cells.Find(what:=ID, LookAt:=xlWhole) > 0 Then
MsgBox "Story ID already exists.", vbExclamation, "Duplicate Found"
Exit Sub
End If
The code operates correctly when the information violates the rule. However, if the result is false (the values are not duplicate with anything in the column) then I receive "Run-time error '91': Object variable or With block variable not set"
What needs to be adjusted to fix the issue?
Find returns a Range object reference. When nothing matches the criteria, the function returns Nothing - a null reference.
This is indeed very very close to the linked would-be-duplicate, with the following nuance - this:
Range("...").Cells.Find(...) > 0
Is really doing this:
Range("...").Cells.Find(...).Value > 0
You're implicitly calling into the Range object's default member, which points to its Value.
It's that implicit member call that's throwing runtime error 91, because Find returned Nothing so you have no object to get a value from, to perform the > 0 comparison.
The solution for the error 91 is, as in the linked Q&A, to first verify that Find returns a valid object reference.
The solution to avoid similar bugs in the future and in many other circumstances, is to avoid implicit default member calls - i.e., write code that means what it says and that says what it means.
Set result = Range("...").Find(...)
If Not result Is Nothing Then
'.Find call was successful
If result.Value > 0 Then '<< explicit Range.Value member call
'...
End If
Else
'.Find call failed
End If
Note that the .Cell member call is redundant.

Excel error 1004 "Unable to get .... property of WorksheetFunction class" appearing inconsistently

I have a VBA function within a spreadsheet which operates on another spreadsheet that is opened in an earlier stage of my macro. The macro used to work fine but just recently has started causing a 1004 error ("Unable to get RoundDown property of the WorksheetFunction class") when it runs.
I believe I understand what the error would be caused by (a problem running RoundDown) but I cannot see why it is getting triggered in my macro and the odd part is that when I go into Debug mode and step through the code in the VBE the error does not recur (despite nothing obviously changing).
Does anyone have a similar experience of this sort of error occuring inconsistently and know what I could do to resolve it?
I'm reasonably VBA/Excel-savvy, but any suggestions on further steps to diagnose it would be appreciated. I am wondering if there is some issue with the opened spreadsheet not being ready but I cannot see how.
The code is here. The error occurs on the line marked with a comment.
Public Function GetDatesA(sWorkbookname As String, sSheetname As String, sCell As String) As Variant
Dim vDateList() As Variant
Dim currentCell As Range
Dim n As Long
Set currentCell = Workbooks(sWorkbookname).Worksheets(sSheetname).Range(sCell)
n = 0
Do
If Trim(currentCell.Value) = "" Then
Exit Do
Else
ReDim Preserve vDateList(0 To 1, 0 To n)
vDateList(0, n) = WorksheetFunction.RoundDown(currentCell.Value, 0) 'error occcurs on this line
vDateList(1, n) = currentCell.Column
'Debug.Print currentCell.Value
End If
Set currentCell = currentCell.Offset(0, 1)
n = n + 1
Loop While currentCell.Column < XL_LAST_COLUMN
GetDatesA = vDateList
End Function
Other details are:
Excel version: 2010
File being opened resides locally on my C: drive; my macro is in a spreadsheet on the network
File format for both files is .xls (i.e. Excel 2003) - I don't have the option of changing this
Windows 7 (not that I think it would be relevant)
Two points I've tried already are:
Substitute a different worksheet function (e.g. Min(currentCell)) and that also causes the same problem
Having the file open already seems to stop the problem - I wonder if there is some way that the workbook which is being opened (rather than my main workbook with the macro in it) is not enabled for macros and this is interfering. But even if this is the cause I'm not sure how to get around it!
Any ideas?
This error occurs often when any argument passed to the worksheet function is not of the correct type or simply doesn't make sense.
For example, I've had this problem when calling WorksheetFunction.Asin with an argument bigger than 1. In your case, I'd guess currentCell.Value is a non-numeric value or one not according to your region settings regarding numbers.
Yes, the error message is really misguiding.
I got the "Unable to get * property of WorksheetFunction Class" error using Transpose, MMult,MDterm, and MInverse functions.
I was able to get my code to run by putting "Option Base 1" in the Declarations (before the actual code) section of the particular Module in the Editer.
Excel assumes "Option Base 0" which will add an extra row and column of empty cells. This will cause the error to occur and isn't immediately obvious to see.
I have come accross this before, and for me it was becase the criteria range made no sense, as Andre said above.
See example formula below:
.Cells(11, i).Formula = Application.WorksheetFunction.CountIfs(Sheets("Sheet1").Range("AC8:C" & n), "S")
Have a look at the Range... it makes no sense. Amended the range from "AC8:C" to "AC8:AC" and it will work perfectly

Resources