I am using windows 7, Excel 2010, VBA. I am getting an error
"ByRef Argument Type Mismatch". I am assuming it is a problem with my variable type. I found lots of questions like mine but I can't find anything that has helped me figure out my problem.
Variable Declarations
'Force explicit variable declaration
Option Explicit
Private dptData(8) As String
Private TSdata(8) As String
Private fiscalYear(8) As String
Calling Function
parseUserData fiscalYear, dptData, TSdata
Called function Prototype
Function parseUserData(fiscalYear As String, dptDataAs String, TSdata As String)
You're passing an array to a String. Change the function's signature to accept a Variant instead.
Public Function parseUserData(fiscalYear As Variant, dptDataAs Variant, TSdata As Variant)
Debug.Assert IsArray(fiscalYear) And IsArray(dptDataAs) And IsArray(TSdata)
A String parameter can only ever accept a String argument1. Variant on the other hand, can accept anything - including an array - but then you'll want to Assert that you're dealing with an array, so that you halt execution (and prevent a bug) if that's not the case.
Why use a Variant over a typed array?
Using a typed array would work, but a typed array can't be coerced from a Variant parameter - which means this:
Public Sub DoSomething(ByRef args() As String)
...can't be invoked with this otherwise perfectly valid array of strings:
DoSomething Array("string1", "string2", "string3") ' can't pass a variant array!
Changing the signature to DoSomething(ByRef args As Variant) makes it work. All you need to do is to use a meaningful, descriptive, pluralized name to your variant array parameters, so that when IntelliSense is shown when you invoke that procedure, the name tells you everything you need to know.
But... 'Variant' is evil!
No different than many other languages, type safety in VBA is essentially smokes and mirrors. Variant is a very powerful tool and does have its uses - avoid it when you can doesn't mean unlearn its existence. Using it to pass array references around between procedures doesn't hurt the code's readability, maintainability, or stability. Variant enables duck typing and late-binding, and is a legitimate COM type.
It's a hammer. Just make sure not everything becomes a nail, and you'll do great.
1VBA will implicitly convert other value types to a String, but an array can't be coerced into a string, implicitly or explicitly.
It seems you wanted a string array after all but it may be worth mentioning that you can declare a fixed width string var.
Dim dptData As String * 8
dptData = "abc"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abc |
dptData = "abcdefghijk"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abcdefgh|
Related
This may sound trivial, but what is the difference between
Dim v As String()
and
Dim v() As String
in VB.NET?
No difference. From the VB.NET Language Specification on Arrays:
Array types are specified by adding a modifier to an existing type name. The modifier consists of a left parenthesis, a set of zero or more commas, and a right parenthesis.
...
A variable may also be declared to be of an array type by putting an array type modifier or an array initialization modifier on the variable name. In that case, the array element type is the type given in the declaration, and the array dimensions are determined by the variable name modifier. For clarity, it is not valid to have an array type modifier on both a variable name and a type name in the same declaration.
Originally, in Basic, you had to define arrays, but not variables. And the types of variables were defined by a suffix character: A$ was a string, while A% was an integer and A# was double precision. (and all three were distinct and could be used at the same time) (For single-precision, you could use A!, but that was the default if you just used A)
Eventually, programmers came to realize that those were incredibly bad design choices.
To rectify this, Microsoft added "Option Explicit" which required you to predefine every variable. To lessen the effect on the language, they hijack the "DIM" command, which was used to define arrays, to define scalar variables as well.
So originally:
DIM A(50) ' define 51-element single-precision array
Then
DIM A(50) ' define 51-element single-precision array
DIM A$ ' define a string
Then to get rid of the suffixes, they added the "As {type} syntax"
DIM A(50) ' define 51-element single-precision array
DIM B as String
DIM C(50) as String ' define 51-element string array.
Then they made array size variable.
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
This left a conflict in definition style, so they allowed both:
DIM A() ' define single-precision array
DIM B as String
DIM C() as String ' define string array.
DIM D as String() ' define string array.
There is no difference.
Both Dim v As String() and Dim v() As String will create a string array
Traditionally, in Basic, you would put the parenthesis after the variable name. In VB.Net it is allowed to put them after the type instead if you wish. The result is the same so there is no difference using either syntax. The reason for this addition though is because how you can constuct an array. Consider the following code:
Public Sub MethodThatExpectsAnArray(ByVal arr() As String)
'...
End Sub
Public Sub Main()
Me.MethodThatExpectsAnArray(New String() {"Hello", "World"})
End Sub
In the call I construct the array "on the fly" without any assignment except directly to the method argument. Since there are no variable here I must set the paranthesis after the type. To allow this syntax Microsoft had the choice to either change how you traditionally declare arrays in Basic or allow for both syntaxes. They of course opted for the latter.
There is no difference.
It's mostly semantics. The first reads as create variable "v" of type string array and the 2nd reads as create array "v" of type string. Either way the result is the same array of strings.
There is no difference in the meaning of the two.
If you like to declare several variables in one dim statement, the second form provides more flexibility:
dim v(),v2 as string allows you to declare array types and non array types in the same statement.
The idea is passing the complete content of a listobject.databodyrange to an array to make operations in memory and not having to access the sheets cells values repeatedly which is very time consuming.
this is the code.
Dim theArray As Variant
theArray = mylistObject.DataBodyRange.value
MsgBox (theArray(1, 1)) '= column 1 row 1 = first element
It works, so far so good.
but!!! since theArray is dimensioned as Variant, their elements are NOT strings, So when passing every of the values of theArray into a function that requires a string an error appears.
what to do?
Note: I know I might change the data type of the function itself to variant, but this function is called from so many routines that i dont dare to touch it. I rather prefer try to look for the way to transform the content of that variant into a string
like theArray(i,j) to str(thearray(i,j)) (which does not work)
some help, some ideas?
EDIT 1:
this is the line of the error:
Dim theclaims As Variant
theclaims = rawClsTbl.DataBodyRange.value
For i = LBound(theclaims, 1) To UBound(theclaims, 1)
myText = deleteRefSigns(theclaims(i, 2))
etc
error: byref argument type missmatch
where:
Function deleteRefSigns(txT As String) As String
i will be trying the solutions proposed.
thx
Related questions:
I asked in overflow myself this question some time ago:
Passing Listobject Range to array and getting error "out of range"
and read also this one
Excel VBA Type Mismatch Error passing range to array
and several others.
The following should work:
Dim MyStr As String
MyStr = CStr(TheArray(1, 1))
Note: Always declare it as a forced array not just as Variant …
Dim TheArray() As Variant 'Variant array (can only be an array)
Dim TheArray As Variant 'Variant (can be a value or array)
… to ensure it contains an array. Otherwise it will contain only a value if you do
TheArray = Range("A1").Value
which might easily fail if your range is dynamic.
If you read a range into an array like
Dim TheArray() As Variant
TheArray = Range("A1:C20").Value
then there is no possibility to declare the array as String it is forced to be Variant by design.
It looks like you don't want deleteRefSigns to modify the argument passed by the calling procedure. If so, you can pass the argument by value...
Function deleteRefSigns(ByVal txT As String) As String
i have a User defined type in VBA (Excel) and want to have a fixed memory layout for the members so that I can use pointers in a later step for accessing those members.
The user defined type is looking like the code provided and I need to pass on the adresses of the members to a different programm.
I expected VBA to structere the members in the order that they are initialized but somehow it doesn't.
If you have any idea on how to solve that problem I would be really grateful!
Best Regards,
Lars
Public Type ExampleSet
Example_P_Anteil As Single
Example_I_Anteil As Single
Example_D_Anteil As Single
Example_v0 As Double
Example_Gang0 As Integer
Example_Beschleunigung As Double
Example_Startzeit As Double
Example_int1 As Integer
Example_int2 As Integer
Example_int3 As Integer
End Type
I challenge your conclusions.
Public Type TestUDT
SomeInteger As Integer ' Int16 (I2) at offset 0, padded with 2 bytes
SomeLong As Long ' Int32 (I4) at offset 4, no padding
AnotherLong As Long ' Int32 (I4) at offset 8, no padding
End Type
Public Sub test()
Dim udt As TestUDT
Debug.Print VarPtr(udt) 'expected: X
Debug.Print VarPtr(udt.SomeInteger) 'expected: X+0
Debug.Print VarPtr(udt.SomeLong) 'expected: X+4
Debug.Print VarPtr(udt.AnotherLong) 'expected: X+8
End Sub
Output is as expected:
723094616
723094616
723094620
723094624
User-Defined Types (UDT) are defined in the language specifications as:
A linear concatenation of the aggregated data values possibly with implementation defined padding between data values.
The padding might be what's throwing you off, but a UDT is still a linear concatenation of its members.
Use the LenB function to determine the length of a member (or of the UDT as a whole):
Dim foo As ExampleSet
Debug.Print VarPtr(foo), LenB(foo)
If the UDT contains strings, define them as fixed-length if the offsets of the members that follow it need to be fixed:
Public Type TestUDT
SomeString As String * 10
'...
End Type
That said, good luck accessing these pointers out of process.
I am constructing my first major program in VBA (With some help from SO). The beginning of it is below, and I am adding blocks of code in as structured a way as possible. Having read about the importance of using 'Option Explicit' though, I inserted it and started to declare all my variables. Running the program gave - 'Compile Error: Variable not Defined'.
I have tried deleting and re-entering declarations in case the problem was caused by me adding 'Option Explicit' after starting on the code, as well as removing the UDF and adding that back in, but without success.
Commenting out 'Option Explicit' does away with the error message and the subsequent blocks of code run exactly as designed.
EDIT:I should have said that the error always occurs with 'iLoopControl'.
Option Explicit
'UDF to roll a number of Dice of specified size and total them
Function Dice(NoOfDice, DiceSize)
Dice = 0
For iLoopControl = 1 To NoOfDice
Dice = Dice + WorksheetFunction.RandBetween(1, DiceSize)
Next
End Function
Sub MercOne()
Randomize
Dim Merc(86)
Dim Temp As Integer, TechLevel As Byte, ArmOfService As Byte
Dim Year As Byte, YearCount As Byte
Dim GenAssignment As Variant, UnitAssignment As Variant
Dim SpecAssignmentSwitchEnd As Byte, Roll As Byte
Dim Rank As Variant, NoOfDice As Variant, DiceSize As Byte
Dim GenAssignmentSwitchInt As Byte, GenAssignmentSwitchOff As Byte
Dim CharacterNumber As Long, iLoopControl As Long
I applaud you for using Option Explicit! Now you have to learn to use it well...
You have to declare a variable before using it. You use iLoopControl in your function Dice before declaring it. VBA will assume (absent Option Explicit) that you were just lazy, and create a Variant variable for you. But if you misspelled the variable name, it would create a new one - and debugging would be really difficult because you won't understand why values are wrong...
As an aside, it is a good idea to avoid Variant in general, except when you know the variable can contain different types, when you will use it in a For Each loop, or when it is an optional parameter in a function. It is slower than a "regularly typed" variable and takes more space. In larger programs that can make a difference. And if you do want a Variant, you don't need to say As Variant since that is implied.
One more point about your code: you declare iLoopControl in the scope of a Sub. That means it is unknown (and "undeclared") when you are not in that sub. Not even when you are inside a function called from that sub. The only variables that can be "seen" inside a Sub/Function are the ones that are declared at the module level (outside of sub/function), or within the sub itself.
Option Explicit means you must explicitly declare all variables and their types before using them. If you don't include the Option Explicit declaration, then VBA will just create Variant type variables whenever it encounters a new identifier.
So for example, you need to include Dim iLoopControl as Integer if you have declared Option Explicit:
Option Explicit
Dim iLoopControl As Integer
For iLoopControl = 1 To NoOfDice
Dice = Dice + WorksheetFunction.RandBetween(1, DiceSize)
Next
If you didn't include Option Explicit then you could just use iLoopControl without using the Dim iLoopControl as Integer to declare it.
You need to declare variables before using it
Where's End Sub for MercOne()?
I have a piece of Excel-VBA code of which the following behavior escapes my understanding
option explicit
....
private sub XYZ()
dim s as string
dim ser as series
dim diagram as chart
...
s = function_returning_string(....)
' Following line throws runtime exception 13
set ser = diagram.seriesCollection.item(s)
....
end sub
If I try to get a named item in the seriesCollection of the chart object as outlined above, it throws (the german) errors laufzeitfehler '13' typen unverträglich which would be Run-time error 13: Type mismatch in english.
Changing the offending line to
set ser = diagram.seriesCollection.item(CStr(s))
makes to error go away.
I have no idea why that is. CStr() is supposed to convert something (here: s) into a string, but s is already a string.
CStr converts the type of a variable into a String data type.
If you have a function s = function_returning_string(....) where s is already typed a string, doing for example this:
dim s as string
dim sString as string
sString = cstr(s)
would be meaningless.
In other cases, this may be useful.
Sometimes people use it for example to convert a string to a date, using cDate.
Or to take string, cut off some parts and use cDbl or cInt to convert towards a Double or Integer datatype.
Check your function "function_returning_string" if it is defined as :
Public Function function_returning_string() as string
In this case, the seriesCollection only allows String typed variables to be added in the collection.