Why isn't this a type mismatch? - vba

I answered this question, apparently to OP's satisfaction, but still find their question puzzling. Their question involved an expression in which a workbook object was being concatenated with a string, triggering Run-time Error '438': Object doesn't support this property or method. You can reproduce this sort of error by simply typing
?"Hello, " & ThisWorkbook
In the Immediate Window.
My question is -- why does this raise that error, instead of a error 13 -- type mismatch? A reasonable guess is that VBA tries to find a default property for a workbook object and that a default property doesn't exist. But, if so, I would expect it to be the following error from Microsoft's list of Visual Basic 6.0 error codes: Automation object doesn't have a default value (Error 443).
It is mostly of academic interest, but if the result of concatenating an object without a default property with a string is always Error 438, and that is the only way of triggering Error 438 rather than possibly another error when concatenating a string with an object, then the following code might be of use:
Function HasDefault(O As Variant) As Boolean
Dim i As Long
If Not IsObject(O) Then Exit Function
On Error Resume Next
i = Len("Hello, " & O)
If Err.Number = 438 Then
HasDefault = False
Else
HasDefault = True
End If
End Function
I've tested this on a variety of objects, and for those I've tested it on it has returned False exactly when _Default doesn't show up as a (hidden) member of the object when viewed in the Object Browser. Nevertheless, I don't quite trust this function and am still puzzled by what is going on here.

VBA will try and convert the expressions on each side of the & operator to a data value. The language spec states that:
If the value type of the expression’s target variable is a class:
If the declared type of the target is Variant, runtime error 9
(Subscript out of range) is raised.
If the declared type of the target is not Variant, and the target has
a public default Property Get or function, the data value’s value is
the result of invoking this default member for that target with this
argument list. This consumes the argument list.
Otherwise, runtime error 438 (Object doesn’t support this property or
method) is raised.
As regards your function, I'd just use:
callbyname(O, "_Default", VbGet)
which will raise a 438 error as appropriate.

Related

CallByName works with MsgBox but Not When Assigning a Value ("Object Required" Error)

Using VBA in excel:
I have a function where I want to access class properties using variables for property names. I was able to use a variable with CallByName to MsgBox the property value back to me:
MsgBox CallByName(oThisInvoice, DataType, VbGet)
Where oThisInvoice is a class object and DataType is the variable that contains the property name I want to access. This statement works and is a rewrite of:
MsgBox oThisInvoice.InvoiceDate
However, using the same method doesn't work when assigning a value to the same property:
CallByName(oThisInvoice, DataType, VbGet) = 5
doesn't work, I get an "Run-time Error 424: Object Required" error. Using VbLet and VbSet didn't work either, throwing "Run-time error 446: Object doesn't support named arguments".
It is a rewrite of the following:
oThisInvoice.InvoiceDate = 5
which does work.
Does anyone know what I can use to assign a value to a class property in VBA when using a variable to reference the property name?
CallByName(oThisInvoice, DataType, VbGet) = 5
Of course that doesn't work to set a value. You are calling a Get procedure, as evidenced by the use of VbGet. Get doesn't set values, it gets them.
What you need to do instead is use CallByName with VbLet:
CallByName oThisInvoice, DataType, VbLet, 5

LibreOffice CallFunction Object Not Set

Building a function to evaluate a string and return the results. However, svc and svc2 through an error, "Object Not Set". Though, when I run the code, the print statements clearly show that the objects are not null or nothing as they return FALSE.
What am I missing?
Is there another test that I could preform to make sure svc or svc2 are initialized?
Option VBASupport 1
Option Explicit
Function EVAL (str as String)
dim svc as object
dim svc2 as object
SET svc = createUnoService( "com.sun.star.sheet.FunctionAccess" )
SET svc2 = GetProcessServiceManager().createInstance("com.sun.star.sheet.FunctionAccess")
EVAL = svc2.callFunction ("Evaluate", str)
EVAL = svc.callFunction ("Evaluate", str)
End Function
I now get error 91, cannot coerce argument during core reflection call!
Note: as noted in a response to this problem, there is an unresolved error in LibreOffice when calling Evaluate, so this problem will most likely remain unresolved until OpenOffice resolves their bug. =(
This is a misleading error message. svc and svc2 are initialized correctly.
The issue is that Evaluate does not work as expected. A bug report has been filed at https://bugs.documentfoundation.org/show_bug.cgi?id=102076.
I could not get the following example to work at all.
Option VBASupport 1
Option Explicit
Sub Main()
Evaluate ("A1:A2") ' Works both on Excel and on LibreOffice
Evaluate ("TODAY()") ' Works on Excel, but Runtime Error on LibreOffice
End Sub
How to evaluate a string was asked at http://ooo-forums.apache.org/en/forum/viewtopic.php?f=9&t=3497. There weren't any simple answers.
EDIT:
To fix the new error, use this code.
EVAL = svc2.callFunction ("Evaluate", Array(str))
This produces the error message com.sun.star.container.NoSuchElementException, which I believe is correct because there is no such spreadsheet function EVALUATE.
To verify this, enter =EVALUATE() in a spreadsheet cell, which produces #NAME?.

Error 424: object required when calling external library

I'm writing a VBA macro, and have imported mscorlib.dll in order to refer to System.Math.
Sub draw16mmButtonHole()
Dim test As Double
test = System.Math.ASin(0) 'this is where the error happens
End Sub
Apparently Error 424 usually happens when calls return objects, and can be fixed by appending set to the variable assignment; however, here the same error happens.
In addition to the behavior described above, Error 424 happens when trying to call .NET functions from VBA. This is not something VBA is able to do.

VBA: Run time error '91'?

All I'm trying to do here is save a reference to the currently active window, but it doesn't seem to be working. It gives me a run time error on the last line.
Dim SourceWindow As Window, QACheckWindow As Window
SourceWindow = ActiveWindow
I'm not exactly sure why. Isn't ActiveWindow supposed to return the currently active window? If not, how can I make a reference to it?
EDIT: The above is right at the beginning of my function, so all there is before it is Sub FuncName()
In VB object variables require the Set keyword to be assigned. Object properties that are objects also need to be Set. Runtime error 91 "object variable not set" is raised when the assignment doesn't use that keyword.
This is inherited from legacy Let keyword to assign values, and Set keyword to assign references; the Let eventually was deprecated (although still needed for defining properties) and the Set remained, leaving the VB6/VBA value assignment syntax like [Let] variable = value, where "Let" is optional.
In the declaration and assignment:
Dim SourceWindow As Window, QACheckWindow As Window
'this is like saying "Let SourceWindow = ActiveWindow":
SourceWindow = ActiveWindow
SourceWindow is an object, assigned as if it were a value - this causes VBA to attempt let-coercion through a default member call. If the object wasn't initialized, the member call fails with error 91. If the object was initialized but doesn't have a default member, error 438 is raised.
So in this case error 91 is being raised because of an implicit member call; the .net equivalent would be a NullReferenceException:
Dim SourceWindow As Window, Dim WindowTitle As String
'"SourceWindow" reference isn't set, the object can't be accessed yet:
WindowTitle = SourceWindow.Caption
I'm going to go a bit overboard here, but the legacy Let statement should not be confused with the Let clause (in VB.net) which, in the LINQ query syntax (in VB.net), computes a value and assigns it to a new, query-scoped variable (example taken from MSDN):
From p In products
Let Discount = p.UnitPrice*0.1 '"Discount" is only available within the query!
Where Discount >= 50
Select p.ProductName, p.UnitPrice, Discount
VB.net assigns both values and references, without the need to specify a Let or a Set, because in .net this distinction is a much thinner line, given how everything ultimately derives from System.Object... including System.ValueType. That's why the Set keyword was also deprecated in VB.net, and also why the VB.net syntax for defining properties has dropped the Let in favor of Set - because parameterless default members are illegal in VB.NET, so this ambiguous let-coercion doesn't happen.

Why doesn't an Excel/VBA user defined default property that returns a Range behave like a Range?

A property in an Excel/VBA class I'm writing returns a Range. I made it the default property for the class using the technique described at http://www.cpearson.com/excel/DefaultMember.aspx. I expected to use all the Range class's built-in properties and methods with objects of my class without specifying the property explicitly. It doesn't work. Here are a couple of much simpler classes to illustrate. (These listings are the exported source viewed with a text editor since VBA's editor hides the Attribute statements.)
' clsDefLong: This class just verifies that default properties work as I expected.
Public Property Get DefProp() As Long
Attribute DefProp.VB_UserMemId = 0
DefProp = 125
End Property
' clsDefRange: This class is identical except the default property returns a Range.
Public Property Get DefProp() As Range
Attribute DefProp.VB_UserMemId = 0
Set DefProp = ActiveCell
End Property
Here's a Sub in a normal module to instantiate and test the classes. The comments indicate what happens when I single step through it:
Sub DefTest()
Dim DefRange As New clsDefRange, DefLong As New clsDefLong
Debug.Print DefLong.DefProp '(1) Displays 125. Verifies the class behaves as intended.
Debug.Print DefLong '(2) Same as (1). Verifies VBA uses the DefProp property as the default.
Debug.Print DefRange.DefProp.Value '(3) Displays the ActiveCell content. Verifies that this class works as intended.
Debug.Print DefRange.DefProp '(4) Same as (3). Verifies VBA knows DefProp returns a Range without further prompting.
Debug.Print DefRange '(5) Aborts with the messge "Run-time error '13': Type mismatch"
End Sub
Why doesn't DefRange in statement (5) behave just like DefRange.DefProp in statement (4)?
If I change statement (5) to:
Debug.Print DefRange.Cells(1, 1)
The compiler selects ".Cells", says "Compile error: Method or data member not found" and stops so the problem is in the object model - not just something getting messed up at run-time. Am I doing something wrong? Or isn't it possible to have a default property that returns a Range? How about other built-in classes? User defined classes?
Debug.Print DefRange
This seems like you're asking it to chain default properties and it won't do it. You can only pull the default property from the object you provide. In this case, you're returning a range object and that can't be printed. VBA won't go to the next level to see if the default property returns an object and if that object type has a default property. I suppose if it did, you could create an infinite loop - two objects each the result of the default property of the other.
Debug.Print DefRange.Cells(1, 1)
No default property will insert itself into a dot-chain. I assume this is because if DefRange did have a Cells property of its own, which would it use? I can't think of any objects in Excel's model that behave this way. You can use this
Debug.Print DefRange(1,1)
This seems to be an example of chaining default properties, which I said it wouldn't do. I guess the (1,1) is enough to jump start the chain again. DefRange returns a range object, (1,1) returns a range object, and the Value (default) property is returned.
Interesting question. I wonder if the default property feature was built this way intentionally or it's just the way it worked out.