Announcement

Collapse
No announcement yet.

ReDim VB Structures in PB

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Andre-Tascha Lamme
    Guest replied
    Thanks for the sample code...got sidetracked yesterday by several issues
    so was unable to do much on this aspect of the project...Should get to it
    today and will post the results...

    à bientôt,

    André-Tascha Lammé



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Florent Heyworth
    replied
    Lance

    done it - I updated the code before I saw your post

    Cheers

    Florent


    [UPDATE]
    BTW, just for the record Tom Hanlin was right with his suggestion
    to use REDIM instead of DIM. I suspect that Andre-Tascha's problems
    if he used redim was connected to other factors.

    Disclaimer - the knowledge I have of the way VB treats its arrays
    comes from code I had to write to access VB arrays from C with
    VB version 6.0. Microsoft may have used other methods before this
    version and may use other ways in future versions (since this is
    all "behind the scenes" stuff.
    [/UPDATE]



    [This message has been edited by Florent Heyworth (edited August 02, 2001).]

    Leave a comment:


  • Lance Edmonds
    replied
    Ah! Editing your message behind my back, huh?

    THANKS!!!

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Lance Edmonds
    replied
    For clarity, can you post your VB definition of MYTYPE please? Thanks!


    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Florent Heyworth
    replied
    [UPDATE]: The SysFreeString call in PassArray() was not using
    the i index to iterate over the UDT array but a literal (an
    oversight on my part) - changed it to use the index

    Lance

    as far as I know (I don't use VB but have had to talk with it
    quite a few times) you can't DIM an array with variables instead
    of numeric literals although you can REDIM it with variables.

    This is really the difference in memory allocation between VB
    DIM and REDIM.

    Here's some test code which shows an example of using SafeArrayRedim
    and filling the type with data:

    Code:
    'VB CODE
    'Place this in a module in the declarations
    Type MYTYPE
        s1 As String
        s2 As Long
    End Type
    Declare Sub PassArray Lib "test.dll" (ByRef s() As MYTYPE)
    
    'place this in your startup code
    Private Sub Form_Load()
    Dim i As Long
    Dim x As Long, y As Long
    x = 10
    y = 10
    ReDim s(x To y) As MYTYPE
    
    s(x).s1 = "hello"
    s(x).s2 = 1290834
    PassArray s()
    
    For i = LBound(s()) To UBound(s())
        MsgBox s(i).s1
        MsgBox Format$(s(i).s2)
    Next
    
    End Sub
    Code:
    'PB TEST CODE 
    #COMPILE DLL "test.dll"
    
     
    DECLARE FUNCTION MultiByteToWideChar LIB "KERNEL32.DLL" ALIAS "MultiByteToWideChar" (BYVAL CodePage AS LONG, BYVAL dwFlags AS DWORD, lpMultiByteStr AS ASCIIZ, BYVAL cchMultiByte AS LONG, lpWideCharStr AS ASCIIZ, BYVAL cchWideChar AS LONG) AS LONG
    DECLARE FUNCTION WideCharToMultiByte LIB "KERNEL32.DLL" ALIAS "WideCharToMultiByte" (BYVAL CodePage AS LONG, BYVAL dwFlags AS DWORD, lpWideCharStr AS ASCIIZ, BYVAL cchWideChar AS LONG, lpMultiByteStr AS ASCIIZ, BYVAL cchMultiByte AS LONG, _
                     lpDefaultChar AS ASCIIZ, BYVAL lpUsedDefaultChar AS LONG) AS LONG
     
    #IF NOT %DEF(%NULL)
    %NULL = 0
    #ENDIF
     
    TYPE SAFEARRAYBOUND
      cElements AS DWORD
      lLbound   AS LONG
    END TYPE
     
    TYPE SAFEARRAY
      cDims      AS WORD
      fFeatures  AS WORD
      cbElements AS DWORD
      cLocks     AS DWORD
      pvData     AS DWORD
      rgsabound(0 TO 1) AS SAFEARRAYBOUND
    END TYPE
     
    '
    ' BSTR API
    '
    '
    'It's easier to declare the SysAlloc* family to use ASCIIZ as the OLECHAR parameter
    DECLARE FUNCTION SysAllocString LIB "oleaut32.DLL" ALIAS "SysAllocString" ( szString AS ASCIIZ) AS DWORD
    DECLARE SUB SysFreeString LIB "oleaut32.DLL" ALIAS "SysFreeString" (BYVAL hBSTR AS DWORD)
    DECLARE FUNCTION SysStringLen LIB "oleaut32.DLL" ALIAS "SysStringLen" (BYVAL hBSTR AS DWORD) AS DWORD
    
     
    '
    ' SAFEARRAY API
    '
    DECLARE FUNCTION SafeArrayCreate LIB "oleaut32.DLL" ALIAS "SafeArrayCreate"(BYVAL vt AS WORD,BYVAL cDims AS DWORD,BYREF rgsabounds AS SAFEARRAYBOUND) AS DWORD
    DECLARE FUNCTION SafeArrayDestroy LIB "oleaut32.DLL" ALIAS "SafeArrayDestroy"(BYVAL hsa AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayGetDim LIB "oleaut32.DLL" ALIAS "SafeArrayGetDim"(BYVAL hsa AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayGetLBound LIB "oleaut32.DLL" ALIAS "SafeArrayGetLBound"(BYVAL hsa AS DWORD,BYVAL nDim AS DWORD,BYREF plLbound AS LONG) AS DWORD
    DECLARE FUNCTION SafeArrayGetUBound LIB "oleaut32.DLL" ALIAS "SafeArrayGetUBound"(BYVAL hsa AS DWORD,BYVAL nDim AS DWORD,BYREF plUbound AS LONG) AS DWORD
    DECLARE FUNCTION SafeArrayGetElemsize LIB "oleaut32.DLL" ALIAS "SafeArrayGetElemsize"(BYVAL hsa AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayPutElement LIB "oleaut32.DLL" ALIAS "SafeArrayPutElement"(BYVAL hsa AS DWORD,BYREF rgIndices AS LONG,BYVAL pvData AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayGetElement LIB "oleaut32.DLL" ALIAS "SafeArrayGetElement"(BYVAL hsa AS DWORD,BYREF rgIndices AS LONG,BYVAL pvData AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayLock LIB "oleaut32.DLL" ALIAS "SafeArrayLock"(BYVAL hsa AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayUnlock LIB "oleaut32.DLL" ALIAS "SafeArrayUnlock"(BYVAL hsa AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayCopy LIB "oleaut32.DLL" ALIAS "SafeArrayCopy"(BYVAL hsa AS DWORD,BYREF phsaOut AS DWORD) AS DWORD
    DECLARE FUNCTION SafeArrayRedim LIB "oleaut32.DLL" ALIAS "SafeArrayRedim"(BYVAL hsa AS DWORD,BYREF psaboundNew AS SAFEARRAYBOUND) AS DWORD
     
    'Test type to reflect VB Type
    TYPE MYTYPE
        s1 AS ASCIIZ PTR
        s2 AS LONG
    END TYPE
     
    FUNCTION UnicodeToStr(BYVAL dw AS DWORD) AS STRING
      LOCAL Buffer AS STRING
      LOCAL length AS LONG
     
      length = SysStringLen(dw)
      Buffer = SPACE$(length)
      WideCharToMultiByte 0, _                     ' code page
                          %NULL, _                 ' performance and mapping flags
                          BYVAL dw, _              ' Unicode string to convert
                          -1, _              ' len of Unicode string
                          BYVAL STRPTR(Buffer), _  ' buffer for ANSI string
                          LEN(Buffer), _           ' len of ANSI buffer
                          BYVAL %NULL, _           ' unmappable chars buffer
                          BYVAL %NULL              ' unmappable chars flag
     
      FUNCTION = Buffer
     
    END FUNCTION
     
    FUNCTION StrToUnicode(BYVAL x AS STRING) AS STRING
      LOCAL Buffer AS STRING
     
      Buffer = SPACE$(LEN(x) * 2)
     
      MultiByteToWideChar 0, _                     ' code page
                          %NULL, _                 ' performance and mapping flags
                          BYVAL STRPTR(x), _       ' ANSI string to convert
                          -1, _                ' len of ANSI string
                          BYVAL STRPTR(Buffer), _  ' buffer for Unicode string
                          LEN(Buffer)              ' len of Unicode buffer
     
      FUNCTION = Buffer + CHR$(0,0)
      
    END FUNCTION
     
    SUB PassArray ALIAS "PassArray" ( dwsa AS DWORD ) EXPORT
        LOCAL i AS LONG
        LOCAL dwElem AS DWORD
        LOCAL lUBound AS LONG
        LOCAL tNewBound AS SAFEARRAYBOUND
        LOCAL psa AS SAFEARRAY PTR
         
        CALL SafeArrayGetLBound( dwsa, 1, tNewBound.lLBound )
     
        tNewBound.cElements = 20 'extend the array to contain 20 elements
         
        CALL SafeArrayRedim( dwsa, tNewBound )
        'access the array
        psa = dwsa
        dwElem = @psa.pvData
        CALL SafeArrayGetUBound( dwsa, 1, lUbound )
         
        DIM t(tNewBound.lLBound TO lUBound) AS MYTYPE AT dwElem
         
        'demonstrate looking at a string variable in the type
        IF t(tNewBound.lLBound).s1 THEN
            MSGBOX "Value of MYTYPE(" + FORMAT$(tNewBound.lLBound) + ") : " _
                    + UnicodeToStr( t(tNewBound.lLbound).s1 )
        END IF
        'demonstrate changing and initializing the new values
        FOR i = tNewBound.lLBound TO lUBound
            IF t(i).s1 THEN
                CALL SysFreeString( t(i).s1 )
            END IF
            t(i).s1 = SysAllocString( StrToUnicode("A day at the races - iter: " + FORMAT$(i) ) )
            t(i).s2 = i
        NEXT
    
     
    END SUB
    Cheers

    Florent



    [This message has been edited by Florent Heyworth (edited August 02, 2001).]

    Leave a comment:


  • Lance Edmonds
    replied
    Very interesting Florent! VB is indeed a strange creature.

    What about if the VB safearray is DIMmed with variables instead of numeric literals? I can't see how the same rules could apply then?

    Can you or anyone else double-check these facts please? Andre-Tascha?

    Once confirmed, I'd like to send a copy of this discussion over to the Documentation Dept for inclusion in the next doc update.

    Thanks folks!


    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Florent Heyworth
    replied
    Hi Andre-Tascha

    using SafeArrayLock and SafeArrayUnlock will not help you to
    redim the array you declared from VB. The workaround that you
    need is that you should use REDIM YourStruct(dimensions) AS YOURTYPE
    instead of the DIM statement when the VB array is declared in
    your code - the SafeArrayRedim function will then work.

    Using SafeArrayUnlock on a Dimmed VB array will work but the SafeArrayRedim
    will still fail since VB keeps the memory locked. I've had to deal with VB
    arrays from C before and this is what I could make out:

    a VB array which is Dimmed with dimensions is allocated at compile
    time whereas a Redimmed Array is dynamic and allocated at runtime.

    To declare a dynamic array you can do:

    Dim s() as MYTYPE
    Redim s(0 to 5)

    or the shorter

    Redim s(0 to 5) as MYTYPE

    This worked for me when dealing with SAFEARRAYS.

    Cheers

    Florent


    [This message has been edited by Florent Heyworth (edited August 02, 2001).]

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied
    Originally posted by Tom Hanlin:
    Hmm. Well, the key problem certainly seems to be that the array is locked
    when you try to resize it. I'm not familiar with how "safearrays" work,
    so I'm not clear whether this is a normal state for them or if VB's done
    something unusual. According to MSDN, though, "SafeArrayUnlock decrements
    the lock count of an array so it can be freed or resized." Seems worth a
    try. Here are the declarations for the lock and unlock functions:

    Code:
    DECLARE FUNCTION SafeArrayLock LIB "OLEAUT32.DLL" ALIAS "SafeArrayLock" _
       (BYVAL psa AS DWORD) AS LONG
    DECLARE FUNCTION SafeArrayUnlock LIB "OLEAUT32.DLL" ALIAS "SafeArrayUnlock" _
       (BYVAL psa AS DWORD) AS LONG
    They return %S_OK as a success code.

    Missed those in MSDN...will try them out, thanks. And Fred, the option
    of declaring an upper bound is not a really acceptable solution due to the
    overall requirements of the target folks



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Tom Hanlin
    replied
    Hmm. Well, the key problem certainly seems to be that the array is locked
    when you try to resize it. I'm not familiar with how "safearrays" work,
    so I'm not clear whether this is a normal state for them or if VB's done
    something unusual. According to MSDN, though, "SafeArrayUnlock decrements
    the lock count of an array so it can be freed or resized." Seems worth a
    try. Here are the declarations for the lock and unlock functions:

    Code:
    DECLARE FUNCTION SafeArrayLock LIB "OLEAUT32.DLL" ALIAS "SafeArrayLock" _
       (BYVAL psa AS DWORD) AS LONG
    DECLARE FUNCTION SafeArrayUnlock LIB "OLEAUT32.DLL" ALIAS "SafeArrayUnlock" _
       (BYVAL psa AS DWORD) AS LONG
    They return %S_OK as a success code.

    ------------------
    Tom Hanlin
    PowerBASIC Staff

    Leave a comment:


  • Fred Oxenby
    replied
    Why not dim array to maximum bounds you will ever have...
    And let PB work from there


    ------------------
    Fred
    mailto:[email protected][email protected]</A>
    http://www.oxenby.se

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied
    Originally posted by Tom Hanlin:
    I think the problem is that VB has created a fixed-size array.
    Try defining it like this:
    Code:
    ReDim SomeStruct(1 to 1) as SampleStruct

    No go...
    All calls to the WinAPI SafeArrayRedim return the fact that the array
    is locked...no matter how I initially dim the array in VB.

    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Tom Hanlin
    replied
    I think the problem is that VB has created a fixed-size array.
    Try defining it like this:
    Code:
    ReDim SomeStruct(1 to 1) as SampleStruct

    ------------------
    Tom Hanlin
    PowerBASIC Staff

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied
    Oh yeah...the retval from SafeArrayRedim equates to

    DISP_E_ARRAYISLOCKED

    Any ideas?



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied

    No it is not obvious since VbArrayRedim() returns non-zero for success...

    Actually that was the retval from the SafeArrayRedim API in OLEAUT32.DLL

    In PB, the code within the If/End If Block never executes

    VB call was as follows:

    Call VBPBArray(DarnStruct())
    as well as attempted:

    Call VBPBArray(DarnStruct(),1)
    How did you call the SUB from VB?

    (Adjusting the PB code of course)



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Lance Edmonds
    replied
    VbArrayRedim() returns non-zero for success... or did you test the actual value from SafeArrayRedim?

    How did you call the SUB from VB?

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied

    <Did you actually check the code to see if it did actually redimension the array, or did you dismiss it because the SUB parameters did not fit your goal? >
    Of course (code follows):

    SUB VBPBArray(pSA AS DWORD, BYVAL nSize AS LONG) EXPORT
    LOCAL l AS LONG
    LOCAL vb AS DWORD
    LOCAL NewSize AS LONG
    NewSize=5
    IF vbArrayRedim(psa, NewSize) THEN ' Success?
    MSGBOX"Hello"
    l = vbArrayLBound(psa, 1)
    vb = vbArrayFirstElem(psa)

    DIM A(l TO NewSize) AS SampleStruct AT vb
    a(1).Something = 1
    a(2).Something = 2
    a(3).Something = 3
    a(4).Something = 4
    a(5).Something = 5
    a(1).Cat="Kiki1"
    a(2).Cat="Kiki2"
    a(3).Cat="Kiki3"
    a(4).Cat="Kiki4"
    a(5).Cat="Kiki5"
    a(1).Dog="Pedro1"
    a(2).Dog="Pedro2"
    a(3).Dog="Pedro3"
    a(4).Dog="Pedro4"
    a(5).Dog="Pedro5"
    ' ... process resized array as necessary.
    END IF
    END SUB

    When checking the retval from the SafeArrayRedim, it returns -2147352563.
    The array is obviously not redimensioned.

    On the VB side the structure still has only the single element I
    passed through...



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Lance Edmonds
    replied
    Well, the code was only "designed" to show what I believe is the technique involved in redimensioning a VB safearray from PowerBASIC.

    Did you actually check the code to see if it did actually redimension the array, or did you dismiss it because the SUB parameters did not fit your goal?

    Basically, it should be very straight forward to rework the code so that "nSize" is derived from within the PB code.

    ie, pass a 1 subscript VB array to your PB code (DIM VBArray(0) AS SampleStruct). Your PB code would receive the safearray pointer as I showed above, and then redimension the array as necessary.

    All the functions to manipulate the safearray can be found in VBAPI32.INC.

    Also, you might want to try searching the BBS for "VBAPI" to locate similar discussions.

    I hope this helps!



    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Andre-Tascha Lamme
    Guest replied
    [B]I think Andre-Tascha actually wants to REDIM the VB array from within PowerBASIC.

    Correct, sorry I mistyped in my original post...the problem is that
    I do not know what the new size of the array is before heading into
    PB. Lance's sample code has it dimensioning the array from the nSize
    parameter. I *would like* to be able to send an empty array of
    structures through or alternatively an array with a single element.
    PB would then garner the number of structure elements and resize the
    structure array...


    André-Tascha



    ------------------
    http://www.nash-lamme.com

    Leave a comment:


  • Lance Edmonds
    replied
    I think Andre-Tascha actually wants to REDIM the VB array from within PowerBASIC.

    Although I'm not a VB programmer to give you an definitive answer, the solution would appear to be to pass the array (as a safearray) from VB to the (PB) DLL, and to use the VBAPI32.INC function vbArrayRedim() to resize the array.

    Something like this (typed on-line and not tested):
    Code:
    ' VB code
    DECLARE SUB VBPBArray LIB "MYDLL.DLL" ALIAS "VBPBArray" (VBarray() AS SampleStruct, BYVAL nSize AS LONG)
     
    'PB code
    #COMPILE DLL "MYDLL.DLL"
    #INCLUDE "VBAPI32.INC"
    ' include the SampleStruct TYPE/END TYPE block
    SUB VBPBArray ALIAS "VBPBArray" (pSA AS DWORD, BYVAL nSize AS LONG) EXPORT
      LOCAL l  AS LONG
      LOCAL vb AS DWORD
      IF vbArrayRedim(psa, nSize) THEN ' Success?
        l  = vbArrayLBound(psa, 1)
        vb = vbArrayFirstElem(psa)
        DIM A(l to nSize) AS SampleStruct AT vb
        ... process resized array as necessary.
      END IF
    END SUB
    I hope this helps! If any of the above code is wrong, please let me know!

    ------------------
    Lance
    PowerBASIC Support
    mailto:[email protected][email protected]</A>

    Leave a comment:


  • Wayne Diamond
    replied
    Dim SomeStruct() As SampleStruct
    Redim Preserve SomeStruct(100) As SampleStruct

    ?


    ------------------

    Leave a comment:

Working...
X
😀
🥰
🤢
😎
😡
👍
👎