• VBScript OCX receive and supply a safeArray of type VT_UI1- problem

    From R.Wieser@21:1/5 to All on Tue May 21 20:44:34 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JJ@21:1/5 to R.Wieser on Wed May 22 06:56:18 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    On Tue, 21 May 2024 20:44:34 +0200, R.Wieser wrote:
    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

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Newyana2@21:1/5 to R.Wieser on Tue May 21 20:49:37 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    On 5/21/2024 2:44 PM, R.Wieser wrote:

    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.


    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Wed May 22 08:33:09 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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".

    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.

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Wed May 22 08:52:17 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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".

    I typically use a character-delimited string that can be converted to
    array easily by the caller:

    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. :-\

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Newyana2@21:1/5 to R.Wieser on Wed May 22 08:24:53 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    On 5/22/2024 2:52 AM, R.Wieser wrote:

    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.

    I typically use a character-delimited string that can be converted to
    array easily by the caller:

    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?

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to as well as thruout my on Wed May 22 17:52:14 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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 :-)

    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.

    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.

    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 ...

    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.

    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!> :-)

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Newyana2@21:1/5 to R.Wieser on Wed May 22 14:04:14 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    On 5/22/2024 11:52 AM, R.Wieser wrote:

    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.

    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

    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.

    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.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Wed May 22 22:15:12 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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.

    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.

    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.

    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.

    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

    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.

    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

    And multiplying the needed storage space by 16 ? Yeah, no.

    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.

    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.

    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.

    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.

    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"

    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.

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From JJ@21:1/5 to R.Wieser on Thu May 23 06:05:44 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    On Wed, 22 May 2024 17:52:14 +0200, R.Wieser wrote:

    (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).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Thu May 23 09:47:50 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Thu May 23 10:19:44 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From R.Wieser@21:1/5 to All on Thu May 23 21:13:29 2024
    XPost: alt.windows7.general, comp.os.ms-windows.programmer.win32

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)