Type mismatch on double array - vba

Having some trouble dealing with a compile error. I have a class which runs some processes and updates some of its global variables. I also have another class which needs access to these variables so I've made a Scripting.Dictionary to hold {key, value} pairs.
This all works fine, I'm able to assign the variables in class a and use them in class b simply by referring to the Dictionary with it's key.
The problem I am having is: inside class b I make a function call which takes a Double() parameter in which I've referenced my Dictionary to pull the Double().
Here's the function call:
TransMatrix = BuildMatrix(i, Control_O(), Control(), Control_Surv(), ClassCoreVariables("All_ACM")())
Note that ClassCoreVariables(All_ACM) is referring to my Dictionary.
For completeness, here is the above functions declaration:
Public Function TransMatrix(Treat As Integer, OverS() As Integer, OverSur() As Integer, Survi() As Variant, ACM() As Double)
When I try to execute this code, I get the following error:
Compile error: Type mismatch: array or user-defined type expected
This is confusing me because, I've checked the type:
Debug.Print TypeOf(ClassCoreVariables("All_ACM")()
Returns: Double()
I've also checked to make sure the array isn't empty (it isn't), and I've also tried to change the type to Variant after researching the web..
I'm unsure how to further proceed and would appreciate some help.
If I've missed any relevant information please ask and thanks in advance.
Update
Debug.Print TypeName(ClassCoreVariables("All_ACM")())
is returning a run time error:
Run-time error '9': Subscript out of range

ClassCoreVariables("All_ACM") is not a Double() its a Variant containing one (anything in a Dictionary is stored like that).
TypeName() is resolving the type from the Variant and showing you the underlying type but the runtime will not do this when you call the function.
You would need to pass ACM As Variant:
Function BuildMatrix(ACM As Variant)

Related

VBA: function that takes multiple types of input?

I have some user-defined types and a function for each that takes those types and does something to them.
The thing is though, I'd like to write one, generic function that takes any of those types, and then just uses TypeName to figure out which one it is, and treats it like it needs to.
How do I do this? I tried taking the parameter as Function Foo(Param As Object), but then it fails when I try to pass in a user-defined type into the function.
User Defined Types (UDT) are not classes (they are similar to Structures in C or Records in Pascal), therefore instances of an UDT are not objects - that's the reason you can not use them as an argument to your function foo.
VBA has a universal data type Variant. So when you have Function Foo(Param As Variant), you can pass anything to the function, Foo 3 or Foo ActiveSheet or Foo "Hello world". Anything, except a UDT... If you try so, you will get a compiler error stating "Only user defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions.":
Public Type tMyType
i As Integer
s As String
End Type
Sub Test
Dim bar as tMyType
' Compiler complains
foo bar
Dim myUniversalVariant as Variant
' Compiler complains also
myUniversalVariant = bar
End Sub
The official documentation of Variant states that "Variant types now support user-defined types.", however at least on Excel 2016 this is not working (or at least I was not able to do so).
Your best bet is to use Classes. If you're lazy, just create a class module (per Type) and put all members of the type as public members, nothing more needed (I will not start to describe the pros and cons of getter and setter). You just need to remember that now you have to use the New statement to create and Set to assign an instance.
In class module (I named it clsMyType):
public i As Integer
public s As String
In the regular module:
Sub Test
Dim bar as clsMyType
Set bar = new clsMyType
foo bar
Dim myUniversalVariant as Variant
Set myUniversalVariant = bar
foo myUniversalVariant
End Sub
Now depending if your function wants to receive only object or also other stuff (like integers or strings), parameter type can be Variant or Object
Function foo(Param as Object)
Debug.Print TypeName(Param)
End Function

VBA - CallByName won't accept variant arguments

Solution: Just put brackets around Value in the CallByName statement to force evaluation of it.
Ex. CallByName MobClass, TargetData, vbLet, (Value)
Credit goes to Rory from the other post, which I will probably be deleting since it is no longer relevant and a possible duplicate.
I've spent a long time trying to figure out what was wrong with how I was using CallByName. I finally realized that its fourth argument (Args) will throw a type mismatch if the input is not either EXACTLY the same type as the input argument of what its calling or its hard-coded in.
(I don't even understand how, or why, it does this since VarType(Variant/Integer) = VarType(Integer))
So I either need a way to make it accept variant inputs or convert variables from Variant/Integer to Integer (or create a new variable) without a giant select case.
Edit: So my question wasn't clear so I'll explain it in more detail. I have a bunch of classes that I want to cycle through and call the Let property on. My simplified setup is:
Dim AllClasses as Collection
Sub SetAll(TargetProperty as String, Value as Variant)
For each ClassX in AllClasses
CallByName ClassX, TargetProperty, vbLet, Value
Next ClassX
End Sub
The problem is Value when it is initialized as Variant. The only time I can get it to not throw a type mismatch exception is when I initialize Value as the exact same type that the property wants, but I can't do that since the data types in the class vary.
Edit 2: I'm going to ask another question about the whole problem since no one seems to know much about CallByName
Edit 3: Here's a summary of what we have so far:
CallByName's fourth argument (Args) throws a type mismatch when trying to call the Let property on a class.
This only happens when the value trying to be assigned is stored in a Variant data type. It works perfectly if the variable is initialized to the same type the Let property is expecting OR if the value is hard-coded into the argument.
The Let property works fine on its own. It accepts Variant data types just fine.
My question is: Is there a way to stop this exception? I'm creating another post about other possible solutions to my overall problem.
The Problem is that you pass the properties-arguments by reference not by value, but you can't pass a reference to a different datatype (variant -> long) as the types don't match and it can't be converted as this would change the data type in the caller too. By using brackets, you force the argument to be passed by value and it can be casted as typeLong.
You can avoid this by using ByValin theProperty Letterinstead ofByRef(what is implicit used if not set). You are aware that by referencing a variable, changes made in the property change the callers value too?
Example:
Class PassExample
'Save as class module PassExample
Public Property Let PropByVal(ByVal NewVal As Long)
NewVal = 1
End Property
Public Property Let PropByRef(ByRef NewVal As Long)
NewVal = 1
End Property
Module with test sub:
'save as standard module
Sub test()
Dim v As Variant
v = 0
Dim ExampleInstance As New PassExample
CallByName ExampleInstance, "PropByVal", VbLet, v 'this works
CallByName ExampleInstance, "PropByRef", VbLet, v 'type mismatch
CallByName ExampleInstance, "PropByRef", VbLet, (v) 'this works as ByRef is changed to byval
Debug.Print v ' shows 0, not 1 as it should be if referenced
CallByName ExampleInstance, "PropByRef", VbLet, CVar(v) ' works too as it passes a function-result that can't be referenced
End Sub
Thanks to Rory and chris neilsen for providing the solution!

VBA: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to a late-bound functions

Compile Error:
Compile Error: Only user-defined types defined in public object
modules can be coerced to or from a variant or passed to a late-bound
functions.
I'm new to VBA and I was tasked with debugging some code for a custom screen in Dynamics SL. The first thing I did was to see if it compiled and I got the above message.
When I read the built-in help reference I found the following for the above error:
You attempted to use a public user defined type as a parameter or
return type for a public procedure of a class module, or as a field of
a public user defined type. Only public user defined types that are
defined in a public object module can be used in this manner.
I also went through these similar questions:
How to put user defined datatype into a Dictionary
Only user-defined type defined in public object modules can be coerced when trying to call an external VBA function
They have the same error but I don't see a collection object that the above two questions focused on.
If you may have any idea what may be causing this error please don't hesitate to suggest it.
Code:
Private Sub cpjt_entity_Chk(ChkStrg As String, retval As Integer)
Dim ldDate As Sdate
Dim xStrDailyPost As Sdate
ldDate.val = GetObjectValue("cpe_date")
'xStrDailyPost = DateToStr(ldDate)
'Call MsgBox("Daily Post Date: " & xStrDailyPost, vbOKOnly, "TEST")
serr1 = SetObjectValue("cld_id08", xStrDailyPost) <- Error highlights "xStrDailyPost"
End Sub
Definition for SetObjectValue:
Declare Function SetObjectValue Lib "swimapi.dll" Alias "VBA_SetObjectValue" (ByVal ctlname$, newval As Variant) As Integer
Thank you in advance!
You are probably working with code that was originally written with the Dynamics SL (actually it was Solomon IV at the time) Basic Script Language (BSL) macro language instead of VBA.
Regardless... the fix is, pass results of the "val" method of your xStrDailyPost instance of SDate. SO the code should look like:
serr1 = SetObjectValue("cld_id08", xStrDailyPost.val)
I've not actually tested this but I'm pretty sure this will address your issue.
If you want a little more background, "Sdate" is really just a very thin wrapper of an integer (actually I think it's a short, but I've never found I really needed to know for sure). the "Val" method returns the underlying integer in the SDate variable.

ByRef arugement type mismatch VBA Discussion

I am writing a method in VBA and I received a ByRef argument type mismatch. After doing some research I found a quick fix--store my value in another variable and then pass that new variable into my method. Can anyone explain to me the method behind the madness? What is going on under the hood in VBA? Why will it not accept my original variable?
Context:
For Each excl In excelFiles
Dim temp As String
temp = excl
Call uploadExcelFile(temp, schema)
Next excl
Method used:
Private Sub uploadExcelFile(excelWb As String, schema As Worksheet)
excelFiles is an Array of Strings. Please let me know if you guys would like anymore info. Excited to learn what is going on here.
Even though you're looping through a string array, the VBA for-each loop expects a variant as the loop "counter" (excl in your case).
You can use
Call uploadExcelFile(Cstr(excl), schema)
to avoid that intermediate temp variable.

VBA assigning new object to variable?

I'm trying create new object from a module class in VBA, and I have a small diffcult. Two line of assigning code, look like the same, but result is different.
I got a error message:
After that, I switch to use (1) instead of (2), error was fixed.
But I dont understand; Why do they have this difference?
Dim declares a variable, Set instantiates it.
So, it's a good practice to always have Dim before Set.
If you do not use Dim to declare the specific type of a variable you may subsequently change the variable to another type, for example after;
set aosh = new AOSHRatioQuery
You could mutate the variable to a string;
aosh = "A pint of milk"
As the sendAsyncRequest method expects a AOSHRatioQuery as its 2nd argument & the VBA compiler knows that it cannot guarantee that the aosh variable will actually contain an instance of that type, type safety is violated & the Type Mismatch error is raised to prevent sendAsyncRequest from receiving garbage it cannot interpret.
Explicitly typing with Dim aosh as new AOSHRatioQuery tells the compiler that aosh is guaranteed to always be AOSHRatioQuery instance or Nothing (attempting to assign it to another type will raise an error) so it can be passed safely.
In VBA, you have to declare variables using the Dim keyword, and then defining their data types with the As keyword. That's just how its syntax works. As a general form:
Dim <variableName> As <dataType>