Conditional guard IF Not (x = Empty) Then - vba

I have the following
Public Sub BreakAllLinks(ByRef aWkBook As Excel.Workbook)
Dim Link As Variant
Dim myLinks As Variant
myLinks = aWkBook.LinkSources(Type:=Excel.xlLinkTypeExcelLinks)
If Not (myLinks = Empty) Then
For Each Link In myLinks
aWkBook.BreakLink Name:=Link, Type:=Excel.xlLinkTypeExcelLinks
Next Link
End If
End Sub 'BreakAllLinks
If myLinks is empty then it works well and avoids the For Each loop but if myLinks contains some links then I get the following error
Runtime error '13'
What is wrong with If Not (myLinks = Empty) Then?

LinkSources returns either Empty or an array.
You cannot compare an array with Empty using the equality operator.
The documentation shows you the right way of checking the result - using the IsEmpty function.
The function succeeds regardless of the value type stored in your Variant.
If e.g. you had a Nothing in there, you would get error 91.
Or, if you had an object reference in there, your comparison would try to fetch the default property of the stored object and compare that to Empty.
Which is why you should never check for = Empty really, and only use IsEmpty.

Related

Using the Find VBA function with Defined String

Hey maybe I'm not seeing something obvious here but how can you use the Find VBA function with a predefined variable. I'm using a concatenation of a string assigned from a user form and just "total " in front of it, yet I can't return the row.
Below is my code
Dim HBWS As Worksheet
Dim TickerString As String
TickerString = "Total " & TTB
Set HBWS = Sheets("Hoenheimm Worksheet")
BorrowColumn = HBWS.Cells.Find(What:="Borrow").Column 'Works just fine
TickerRow = HBWS.Cells.Find(What:=TickerString).Row 'Throws an error
Note that TTB is set to a ticker ex. AAPL, and I can check in my local windows that Tickerstring is in fact = to "Total AAPL"
I would expect the .Row column to give me the row on my worksheet as to where this string is located.
EDIT: the error being thrown is as follows...
"Run-Time error '91':
Object Variable or With block variable not set"
Any throughts,
Thanks
You're invoking Range.Find. That method returns a Range object reference - and when it does not find what it's told to look for, it returns Nothing, i.e. a null reference.
TickerRow = HBWS.Cells.Find(What:=TickerString).Row 'Throws an error
What this code is doing (and the working instruction just above it), is assuming that Find returns a valid object reference.
Apparently HBWS.Cells does not contain "Total " & TTB (whatever TTB is), so your code is effectively trying to invoke Range.Row against a Range reference that's Nothing... which is illegal, and raises run-time error 91 as you're experiencing.
You shouldn't assume that Find will return a valid reference, ever. Split it up, and validate the returned object reference with an If ... Is Nothing check:
Set tickerResult = HBWS.Cells.Find(What:=TickerString)
If Not tickerResult Is Nothing Then
tickerRow = tickerResult.Row
Else
'tickerString was not found.
'watch for leading/trailing/extra spaces if the string does *look* legit.
End If
When calling Range.Find, you should always provide a value for the optional parameters, because its implementation "remembers" values from previous invocations, and this is easily bug-prone.

Generic Way to Determine if Invoking a Property Throws an Error

Say you have one slide with one chart on it, and you run this code(in a version of Office later than 2007):
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
Set result = pptWorkbook.ContentTypeProperties
You will generate an error:
Application-defined or object-defined error
I believe this is because "Smart tags are deprecated in Office 2010."(Source), Generally to avoiding this sort of issue from throwing an error and exiting your VBA you can take one of two different approaches:
//Method 1
If OfficeVersion <= 2007
Set result = pptWorkbook.ContentTypeProperties
//Method 2
On Error Resume Next // or GOTO error handler
Set result = pptWorkbook.ContentTypeProperties
Method one requires that you know the specific reason why the property would cause an error, which is easy in this case but may not be as easy with other properties. Method two requires that you use some form of error handling to deal with the error AFTER the fact, my understanding of most other Microsoft languages is that is typically discouraged(example, another example). Is this standard practice in VBA?
In VBA, is there any other way to determine whether a property of an object would throw an error if invoked, BEFORE invoking that property, and without knowing the specifics of that invoked property?
What I like to do for this situation is create a separate function that checks if the property exists and returns a Boolean. In this case it would look something like this:
Public Function CheckIfExists(targetObj As Object) As Boolean
Dim testObj As Object
On Error GoTo failedTest:
Set testObj = targetObj.ContentTypeProperties
CheckIfExists = True
Exit Function
failedTest:
CheckIfExists = False
End Function
Which would return false if that property causes an error and true if not-
Then modify your sub to be:
Public Sub FooSub()
Dim pptWorkbook As Object
Dim result As Object
Set pptWorkbook = ActivePresentation.slides(1).Shapes(1).Chart.ChartData.Workbook
If CheckIfExists(pptWorkbook) Then
Set result = pptWorkbook.ContentTypeProperties
End If
... rest of your code or appropriate error handling...
Hope this helps,
TheSilkCode

`Is` operator does not return true for two range variables that point to the same cell

I have a custom class named imera in which I include a range property named date_cell.
When creating a collection of imera's, every imera's date_cell is set to reference to a specific cell in excel.
While attempting to search within the collection by date_cell:
Option Explicit
Public imeraCol as Collection
Sub searchByDateCell()
Dim day As imera
Dim LastMetrisi As Range
Set LastMetrisi = Range("C27")
For Each day In imeraCol
If day.date_cell Is LastMetrisi Then
'Do something
End If
Next day
Set day = Nothing
End Sub
the "Is" operator doesn't seem to work as expected and return true, although I have tested by debug.print that in my colection exists an imera with date_cell set to range("C27").
And as a result the 'Do something section above, never executes.
Is there any explanation why might this happening?
The Is operator will only return true when comparing the same instance of an object. From this MDSN article:
The Is operator determines if two object references refer to the same
object. However, it does not perform value comparisons. If object1 and
object2 both refer to the exact same object instance, result is True;
if they do not, result is False.
You could compare day.date_cell.address instead to check for the same range.
If day.date_cell.Address = LastMetrisi.Address Then
'Do Something...

Property reflection - How to get value?

I have a need to get properties and their values dynamically. My code below is failing. Can someone give me a hand? I have tried numerous examples but nothing so far.
Dim seriesName As String = s.SeriesName
If model.Settings.ShowNativeLanguage Then
Dim propInfo As System.Reflection.PropertyInfo = s.GetType().GetProperty(model.Country)
seriesName = CStr(propInfo.GetValue(s, Nothing))
End If
This code produces the error "Object does not match target type."
The question was already answered here for C# Object does not match target type using C# Reflection
The solution is to change this line of your code:
seriesName = propInfo.GetValue(propInfo, Nothing).ToString()
to this:
seriesName = propInfo.GetValue(s, Nothing).ToString()
You need to pass the object of which you want to get the value. (More information in MSDN)
Update:
You should always check reflection results for Nothing values. So first store the output of propInfo.GetValue(s, Nothing) in a temporary variable and later on only call the ToString()-function if the object is not Nothing
Surely that should be:
... propInfo.GetValue(s) ...
Normally you must pass the object representing the this instance as the first parameter. You are getting that error because it's expecting the instance s, not a PropertyInfo instance.

How can I evaluate a string into an object in VBA?

In my previous question, How do I assign a value to a property where the property name is supplied at runtime in VBA?, I learned to use CallByName to set a property in a class at run time.
This time, however, I'm trying to figure out how to get an object at run time from a string.
For example, let's say I have a string with the following data: Worksheets("RAW DATA").Range("A1").QueryTable.
Here's what I might try to do where the data above is the input for strParam below:
Function GetObject(strParam As String) As Object
GetObject = SomeFunction(strParam)
End Function
In this case, GetObject should return a QueryTable when evaluated against Worksheets("RAW DATA").Range("A1").QueryTable. Is there anything in VBA that could take the place of SomeFunction from the example above?
Active Scripting Engine can help you. Instantiate ScriptControl ActiveX, use .AddObject() method to add reference to Excel's Application object to the script control's execution environment, set the third parameter to True to make all Application's members accessible too. Then just use .Eval() method to evaluate any property or method, which is the Application's member. The example below shows evaluation of Worksheets() property:
Sub TestQueryTable()
Dim objQueryTable As QueryTable
Dim strEvalContent As String
strEvalContent = "Worksheets(""RAW DATA"").Range(""A1"").QueryTable"
Set objQueryTable = EvalObject(strEvalContent)
objQueryTable.Refresh
MsgBox objQueryTable.Connection
End Sub
Function EvalObject(strEvalContent As String) As Object
With CreateObject("ScriptControl")
.Language = "VBScript"
.AddObject "app", Application, True
Set EvalObject = .Eval(strEvalContent)
End With
End Function
If you are on 64-bit Office, this answer may help you to get ScriptControl to work.
This time you're out of luck. There is no VBA equivalent of eval (not in Excel anyway...there is in Access VBA).
(Application.Evaluate() evaluates strings as Excel expressions, not as VBA code.)
There's the "Evaluate" method (or [ ] brackets). I don't think it will do exactly what you expect - as in run VBA code found in a string. You can look it up in the VBA help menu.