Discussion:
VBScript OCX receive and supply a safeArray of type VT_UI1- problem
(too old to reply)
R.Wieser
2024-05-21 18:44:34 UTC
Permalink
Hello all,

I've just build an OCX for usage within vbscript, and am running into a
problem :

I'm trying to have a method #1 return a SafeArray of type VT_UI1, and later
provide it to a method #2.

The problem is that when I do this :

Object.Method2(Object.Method1())

everything works.

But when I use an intermediate variable like this

Data = Object.Method1()
Object.Method2(Data)

VBScript throws a "Type mismatch" error - and I have no idea why or how to
solve it. :-(

Declarations of both methods in the IDL file :

HRESULT Method1([out, retval] SAFEARRAY(unsigned char)* Result);
HRESULT Method2([in] SAFEARRAY(unsigned char) ByteData);

By the way:

wscript.echo vartype(Object.Method1())
Data = Object.Method1()
wscript.echo vartype(Data)

both of the above return &H2011 (array of VT_UI1)

Help ?

Regards,
Rudy Wieser
JJ
2024-05-21 23:56:18 UTC
Permalink
Post by R.Wieser
Hello all,
I've just build an OCX for usage within vbscript, and am running into a
I'm trying to have a method #1 return a SafeArray of type VT_UI1, and later
provide it to a method #2.
Object.Method2(Object.Method1())
everything works.
But when I use an intermediate variable like this
Data = Object.Method1()
Object.Method2(Data)
VBScript throws a "Type mismatch" error - and I have no idea why or how to
solve it. :-(
HRESULT Method1([out, retval] SAFEARRAY(unsigned char)* Result);
HRESULT Method2([in] SAFEARRAY(unsigned char) ByteData);
wscript.echo vartype(Object.Method1())
Data = Object.Method1()
wscript.echo vartype(Data)
both of the above return &H2011 (array of VT_UI1)
Help ?
Regards,
Rudy Wieser
It's mainly because for array, VBScript only have native support for array
of variant. It has no native support for array of non-variant. Meaning that,
it has no built-in conversion between array of variant and array of
non-variant.

In case of array of integers generated by VBScript itself, they're stored as
array of variants where those variants contain an int. VBScript by itself,
can't directly create an array of non-variant. Only array of variant.

To workaround the problem, store the array of non-variant result from
`Method1()` into VBScript generated array first, then store that VBScript
generated array into a variable. To use the result, pass the VBScript
generated array's element (i.e. the array of non-variant) to `Method2()`.
e.g.

Data = array(Object.Method1())
Object.Method2(Data(0))

Related to VBScript limitation...
The `byval` and `byref` keywords are not available for explicitly passing a
value to a method as a copy or a reference. They're only for
subroutine/function declaration. So VBScript by default will pass value to
methods as a copy rather than a reference. Only if the method argument type
was specifically declared as a reference (i.e. only accept a reference),
VBScript will pass a value as a reference.
R.Wieser
2024-05-22 06:33:09 UTC
Permalink
JJ,
Post by JJ
It's mainly because for array, VBScript only have native support
for array of variant.
I know.

What I /exepected/ was that it would just store the returned SafeArray into
a variant. It would not be able to do anything with it, but that is where
Method #2 would come in.

Maybe I asked the wrong question, and it should have been

"How do I get VBScript to store non-native data, like a SafeArray of VT_UI1,
into a Variant".
Post by JJ
To workaround the problem, store the array of non-variant result
from `Method1()` into VBScript generated array first,
:-) That is what tried to evade. The returned data from method #1 could be
quite large, and having its size multiplied by 16 just because single bytes
need to be stored in Variants is quite wastefull.
Post by JJ
Only if the method argument type was specifically declared as
a reference (i.e. only accept a reference),VBScript will pass
a value as a reference.
While writing this reply I realized that providing a Variant as an [in,out]
type would probably allow me to put a SafeArray into it, and as such
bypassingt the auto-conversion. I'll have to try and see.

Regards,
Rudy Wieser
Newyana2
2024-05-22 00:49:37 UTC
Permalink
Post by R.Wieser
I've just build an OCX for usage within vbscript, and am running into a
I'm trying to have a method #1 return a SafeArray of type VT_UI1, and later
provide it to a method #2.
What JJ said. VBS doesn't do datatypes. You have
two choices: Convert each array element to variant
when assigning it, or don't use an array. I typically
use a character-delimited string that can be converted
to array easily by the caller:

Dim c1
c1 = Chr(1)

s = "one" & c1 & "2" & c1 & "three"
a = Split(s, c1)

For i = 0 to 2
MsgBox a(i)
next

The caller can convert the string to a variant array in one
simple line. You don't have to convert the string to variant
until the final act of your function: Get3Numbers = CVar(s)

If you want to return an array then you have to convert each
array member individually.
R.Wieser
2024-05-22 06:52:17 UTC
Permalink
Newyana2,
Post by Newyana2
What JJ said. VBS doesn't do datatypes.
I beg to differer. Its Variants are wrappers that can contain a multitude
of different data types. Hence the existence of "vartype".
Post by Newyana2
I typically use a character-delimited string that can be converted to
I could. But as the MS documentation to SysAllocString contains a warning
about autoconversion on it I though it would be better not to use it. It
would no doubt bite me in the behind when I could least use it. :-\
Post by Newyana2
If you want to return an array then you have to convert each array member
individually.
While replying to JJ I realized that if the problem is the auto-conversion
of the SafeArray when its stored into a variant by VBScript I should try and
see what happens when I also provide a variant to method #1 so the SafeArray
can be stored into it - possibly bypassing the auto-conversion.

Regards,
Rudy Wieser
Newyana2
2024-05-22 12:24:53 UTC
Permalink
Post by R.Wieser
I beg to differer. Its Variants are wrappers that can contain a multitude
of different data types. Hence the existence of "vartype".
Variants have subtypes. But VBS only handles. An array is just a
collection of data packets in a row with an array descriptor prepended.
Once you access a(x) you're accessing a data type. If that type is
not a variant then VBS can't use it.

The variant is a 16-byte package that includes an identifier for
the data. That and object are the only types VBS can deal with.
Typed languages have to define the type of data: Dim A1() as long.
A1 is a variant, but the data are longs. Each array member will be
4 bytes. If you declare Dim A2() then each member is a variant. The
array itself is a variant either way, but not necessarily the data. You
can add only integers to A2 but each member is still a vaiant of
*subtype* long.
Post by R.Wieser
Post by Newyana2
I typically use a character-delimited string that can be converted to
I could. But as the MS documentation to SysAllocString contains a warning
about autoconversion on it I though it would be better not to use it. It
would no doubt bite me in the behind when I could least use it. :-\
I assumed you were working in VB6, using VB6 strings. If it's C++
then I guess you might have to specificall convert to ANSI and
then to variant. I'm not certain. VB deals in unicode internally but
by default shows an ANSI string in variables. We're normally dealing
with ANSI unless we explicitly convert it. I think VBS is the same.
Whatever the case, I assume you've been able to return strings
from your OCX. How could you return arrays but not strings?
Post by R.Wieser
Post by Newyana2
If you want to return an array then you have to convert each array member
individually.
While replying to JJ I realized that if the problem is the auto-conversion
of the SafeArray when its stored into a variant by VBScript I should try and
see what happens when I also provide a variant to method #1 so the SafeArray
can be stored into it - possibly bypassing the auto-conversion.
A safearray is a variant. The data may be other datatypes. An array
is just the descriptor followed by the data. As an example, Matthew
Curland presented a clever way to parse strings very quickly by pointing
an array descriptor at the string. I've used his method to run tokenizing
routines. Telling VB that the unicode string data is a short integer array,
I can then reference each character as an integer in the array, without
any string handling. It's just memory addresses, after all.

For i = 0 to UBound(ArrayThatsAString)
Select Case i
Case 32 'space character
Do This
Case 34 'quote mark
Do That

You can't do anything like that in VBS because each array member is
always a variant and there's no direct access to memory addresses,
no matter what data type you intend to use. You could return your array
to VB as A1() as String, but not to VBS. If you're not declaring a data
type then it's variant.
R.Wieser
2024-05-22 15:52:14 UTC
Permalink
Newyana2,
Post by Newyana2
Variants have subtypes. But VBS only handles. An array is just a
collection of data packets in a row with an array descriptor prepended.
Once you access a(x) you're accessing a data type. If that type is not a
variant then VBS can't use it.
I beg to differ again (exemptions-to-the-rule are the bane of most people
:-) ).

My previously mentioned "method #1" returns a SafeArray of type VT_UI1,
which in VBScript gets stored into a variant (which than shows its "vartype"
as 0x2011 and its "typename" as "Byte()"(!) ).

I can "wscript.echo" and use use "len()" and "mid()" on it - though it does
all of that badly, treating the VT_UI1 SafeArray as a BSTR. :-(

But yes, if I return a SafeArray of, for example, type VT_UI2 I cannot do
anything with it - other than provide it to another OCX-based method.

... which was/is exactly what I was trying to do :-)
Post by Newyana2
The variant is a 16-byte package
:-) you're preaching to the choir. I've been working with the internal
makeup of Variants and looking into SafeArrays for quite a while now.
Post by Newyana2
If you declare Dim A2()
Which I have not done and, in the current case, have no intention to. Any
explanation in that direction is wasted energy. Sorry.
Post by Newyana2
I assumed you were working in VB6, using VB6 strings.
Whut ? I specifically mentioned VBScript in the subjectline and the first
post, as well as thruout my replies ...
Post by Newyana2
A safearray is a variant. The data may be other datatypes.
No, it isn't. What you are referring to is a Variant which contains (a
pointer to) a SafeArray.
Post by Newyana2
I can then reference each character as an integer in the array, without
any string handling. It's just memory addresses, after all.
For i = 0 to UBound(ArrayThatsAString)
Select Case i
Are you trying to pull my leg ? The only thing there you are using
"ArrayThatsAString" for is to get its length.

Also, in VBScript that "ubound()" on a string doesn't work. It *does* work
on my VT_UI1 SafeArray-in-a-variant though. <huh!> :-)
Post by Newyana2
You can't do anything like that in VBS because each array member is always
a variant and there's no direct access to memory addresses,
In VBS you cannot access memory directly, ever.



But to return to my question:

it turns out that having the OCX-based method #1 return a SafeArray (as
shown by the IDL in my first post) works without a problem. The actual
problem is that VBScript doesn't seem to understand how to do it the other
way around.

Even though the IDL definition describes an SAFEARRAY(unsigned char) input
to the OCX-based method #2 and VBSCript has a Variant of type "VT_ARRAY or
VT_UI1" it can't seem to connect those dots.

This morning I've been trying several IDL definitions. By value, by ref, as
[in] and as [in,out], but none wanted to work. :-(

... and you know what the *real* weird thing is ?

That

Object.Method2( Object.Method1() )

(not using an intermediate variable I mean) *does* work. How ? Why ? No
idea.

But ... I have a solution : Method #1 can keep returning a SafeArray, but I
have to tell Method #2 to expect a Variant, and than just grab the pointer
to the SafeArray from it. Yes, an ugly work-around. But for the moment it
has to do.

Regards,
Rudy Wieser
Newyana2
2024-05-22 18:04:14 UTC
Permalink
Post by R.Wieser
Post by Newyana2
If you declare Dim A2()
Which I have not done and, in the current case, have no intention to.
You have no choice in VBScript. I wasn't suggesting sample code.
I was pointing out that if you're not specifying a datatype then it's
variant. A1() as byte is a byte array, but that's only possible
in VB. In VBS you can only declare A1(). Or you declare A1 and
use that to receive an array variable.

It's true that you can do some funky things around the edges.
Maybe you're managing to read a byte array as string. Whatever.
That's not valid code in VBS.
Post by R.Wieser
Post by Newyana2
I assumed you were working in VB6, using VB6 strings.
Whut ? I specifically mentioned VBScript in the subjectline and the first
post, as well as thruout my replies ...
I meant the language you're writing the OCX in. You're
not writing the OCX is VBS, right? (Please tell me that you
didn't take that on as an interesting challenge. :)

If you write the OCX in VB then the VB function can return
a string:

Function GetBlah() as Variant
s = "blahblahblah"
GetBlah = "CVar(s)
End Function

VBS will have no problem with that. So you can send a
cahracter-delimited string and let VBS turn that into an
array. Otherwise you'll ned to explicitly convert each array
member to variant:

Function GetBytes() As Variant
Dim A1(2) as variant
A1(0) = cvar(120)
A1(1) = CVar(12)
A1(2) = cvar(33)
GetBytes = A1
End Function
Post by R.Wieser
Post by Newyana2
A safearray is a variant. The data may be other datatypes.
No, it isn't. What you are referring to is a Variant which contains (a
pointer to) a SafeArray.
An array is a variant. Yes, it's a pointer-type variable. The
variable itself, though, is a variant.
Post by R.Wieser
Post by Newyana2
I can then reference each character as an integer in the array, without
any string handling. It's just memory addresses, after all.
For i = 0 to UBound(ArrayThatsAString)
Select Case i
Are you trying to pull my leg ? The only thing there you are using
"ArrayThatsAString" for is to get its length.
Sorry. Maybe this went over your head. My example
was showing how data in an array is structured. It was
a sample of VB code, not VBS.
Post by R.Wieser
In VBS you cannot access memory directly, ever.
Yes. As I said in the line you snipped but addressed:

"You can't do anything like that in VBS because each array member is
always a variant and there's no direct access to memory addresses"

I'm going to give you the benefit of the doubt and assume that you
read my post hastily and didn't follow the logic, but it sounds like you're
just looking to argue again.
R.Wieser
2024-05-22 20:15:12 UTC
Permalink
Newyana2,
Post by Newyana2
Post by R.Wieser
Post by Newyana2
If you declare Dim A2()
Which I have not done and, in the current case, have no intention to.
You have no choice in VBScript.
Guess again.
Post by Newyana2
Or you declare A1 and use that to receive an array variable.
Bingo !

And that is what I tried to tell you the whole time : a OCX-base method
which returns a SafeArray (of type VT_UI1), which than get stored (by
VBScript) into a Variant.

So, I *do* have a choice.
Post by Newyana2
Post by R.Wieser
Post by Newyana2
I assumed you were working in VB6, using VB6 strings.
Whut ? I specifically mentioned VBScript in the subjectline and the first
post, as well as thruout my replies ...
I meant the language you're writing the OCX in.
I realized too late that that might have been what you ment.

But no, I'm using Assembly. And yes, that means I get to see a *lot* of the
inner workings of programs.
Post by Newyana2
You're not writing the OCX is VBS, right? (Please tell me that you didn't
take that on as an interesting challenge. :)
wellll.... I would probably try (just to see if I could), if I would not
know that due to the lack of build-in features VBScript makes that
impossible.
Post by Newyana2
If you write the OCX in VB then the VB function can return
Function GetBlah() as Variant
s = "blahblahblah"
GetBlah = "CVar(s)
End Function
I have absolutily zero idea what "cvar()" does. But if it returns a BSTR
than I've already mentioned the problem with that - as well as ofcourse that
it would double the needed space to store the origional array of bytes. A
multiplication which I try to keep away from.
Post by Newyana2
Otherwise you'll ned to explicitly convert each array
Function GetBytes() As Variant
Dim A1(2) as variant
A1(0) = cvar(120)
A1(1) = CVar(12)
A1(2) = cvar(33)
GetBytes = A1
End Function
And multiplying the needed storage space by 16 ? Yeah, no.
Post by Newyana2
Post by R.Wieser
Post by Newyana2
A safearray is a variant. The data may be other datatypes.
No, it isn't. What you are referring to is a Variant which contains (a
pointer to) a SafeArray.
An array is a variant. Yes, it's a pointer-type variable.
Nope. How many times do I have to mention that I'm returning a SafeArray
of type VT_UI1 ? That is *NOT* a Variant, not from the outside, not from
the inside.
Post by Newyana2
Post by R.Wieser
Post by Newyana2
For i = 0 to UBound(ArrayThatsAString)
Select Case i
Are you trying to pull my leg ? The only thing there you are using
"ArrayThatsAString" for is to get its length.
Sorry. Maybe this went over your head.
No, it didn't.
Post by Newyana2
My example was showing how data in an array is structured.
No, you didn't. You just counted up from Zero upto a value equal to the
length of the string inside teh 'ArrayThatsAString' variable, wrote comments
as if the count would represent a character - and than "Do This" and "Do
That" when it reached a certain count.

You have *NOWHERE* accessed that "array that is a string" - other than to
get its length.
Post by Newyana2
It was a sample of VB code, not VBS.
:-) I do not need to know the fine points of either language to see that you
have create a simple loop, which doesn't even do anything when counting from
0 upto 31.
Post by Newyana2
Post by R.Wieser
In VBS you cannot access memory directly, ever.
"You can't do anything like that in VBS because each array member is
always a variant and there's no direct access to memory addresses"
You should really drop your hangup on arrays and variants you know. It gets
tedious.

Also, what have variants to do with accessing memory ? They can be used
store the result of such a memory access, but thats all.

And that is what I tried to make clear : Your arrays and Variants mentioning
there is nothing more than a red herring.
Post by Newyana2
I'm going to give you the benefit of the doubt and assume that you
read my post hastily and didn't follow the logic, but it sounds like
you're just looking to argue again.
Seeing how you have ignored every explanation to how I stored a VT_UI1
SafeArray into VBScript and kept hammering that 'all arrays are variants'
and that 'a SafeArray is a variant too' I don't think you are the one who
should be talking about "benefit of the doubt".

To be crude about it, I'm currently doubting either your knowledge or your
intentions.

Regards,
Rudy Wieser
R.Wieser
2024-05-23 08:19:44 UTC
Permalink
Newyana2,
I'm going to give you the benefit of the doubt and assume that you read
my post hastily and didn't follow the logic, but it sounds like you're
just looking to argue again.
In that regard, why didn't you just call me a liar when I posted that
"Object.Method2(Object.Method1())" using a SafeArray of type VT_UI1 as
argument worked and be done with it ?

Instead you kept on arguing that VBScript only knows arrays of Variants and
would not stop - regardless of me explaining to you that that isn't true.
You just ignored them. :-(

And you wouldn't stop trying to push the same work-arounds either - even
after I had explained to you why using an array of Variants or putting them
in a string are both problematic.

And to top that of you have posted *Zero* about the actual problem I
described.

Heck, you didn't even acknowledge *anything* in that regard. Not even my
(multiple!) "Ain't its strange that it works {this} way, but not {that} way"
remarks - with the {this} showing that it (returning and providing a
non-Variant array) actually worked.

It looks to me you decided for yourself that it could not work, and thus
silently rejected anything to the contrary.

Newyana, you may 'have your doubts' about me arguing (read: explaining why
some "solutions" won't work and some other stuff does), but I would suggest
you take a hard look in the mirror. Your reflection might have a few things
to tell you ...

Regards,
Rudy Wieser
JJ
2024-05-22 23:05:44 UTC
Permalink
Post by R.Wieser
(not using an intermediate variable I mean) *does* work. How ? Why ? No
idea.
VBScript is a stripped down version of Visual Basic (VB). Some VB features
are removed in VBScript. Unlike VBA which still retain almost all VB
features. VBA doesn't have any problem with your case of code (tested in
Excel VBA with ADODB.Stream's array of byte - same problematic variant
type).
R.Wieser
2024-05-23 07:47:50 UTC
Permalink
JJ,
Post by JJ
VBA doesn't have any problem with your case of code (tested in
Excel VBA with ADODB.Stream's array of byte - same problematic
variant type).
Thanks for checking.

And yes, that (VT_UI1 / Byte() ) is the type of the SafeArray I'm returning.

As mentioned, it has got /some/ support, but VBScript mostly regards it as a
string. Which, in my case, it definitily isn't.


And thanks for reminding me of ADODB. I had downloaded two example scripts,
but hadn't yet taken a look at them. Doing a quick(ish) test and looking
at the "vartype()" result of what "stream.read" returns it does match what
my method #1 returns: &H2011. IOW, nothing outof the ordinary (of sorts)
for VBScript.

After that I checked the TypeLib to that ADOB.stream object and saw that
neither the "stream.read" nor the "stream.write" methods used a SafeArray as
its out- or input (none of the methods do), but just used Variants to wrap
and transport them in.

IOW, althoug I called it "an ugly work-around", it seems to be the way its
supposed to be done. Oh well.

Also, the ADODB example mentioned build-in(!) functions like
lenb(),midb(),ascb() and chrb(), which I was not aware of. Meaning I do not
have to write them myself (my "method #2" was an early attempt to them).
Phew. :-)

And a funny thing : although you can do an "ubound()" on the result of a
"stream.read", you can't actually access that result as if its an array ...

Regards,
Rudy Wieser
R.Wieser
2024-05-23 19:13:29 UTC
Permalink
Post by R.Wieser
Also, the ADODB example mentioned build-in(!) functions like
lenb(),midb(),ascb() and chrb(), which I was not aware of. Meaning I do
not have to write them myself (my "method #2" was an early attempt to
them). Phew. :-)
It seems I was a bit to fast there : the above work for BSTR and a VT_UI1
array, but error-out when I try to use them on even just a VT_I1 array.

It looks like I have to write my own versions of them after all. And I was
/so/ glad not to have to do that ... :-)

Regards,
Rudy Wieser

Loading...