Announcement

Collapse
No announcement yet.

ReDim VB Structures in PB

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

  • ReDim VB Structures in PB

    I have done quite a bit of searching for this, but have yet
    to find any solution in the forums. All attempts thus far (including
    attempting to utilize the SafeArray redimensioning) have caused
    some pretty spectacular yet unsatisfying results (i.e. GPF).

    I have a simple struct defined in both VB and PB (and yes,
    the alignment is in order). On the VB side the array is dimensioned
    as follows: Dim SomeStruct() as SampleStruct.

    Any attempts at redimensioning blow badly. Am I stuck with

    Dim SomeStruct(1 to 100) as SampleStruct

    on the VB side and "handle" any empty elements or needed extra
    elements, or is there a manner in which I can ReDim the array of
    VB structs within VB?

    Profuse thanks,

    André-Tascha





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

  • #2
    Dim SomeStruct() As SampleStruct
    Redim Preserve SomeStruct(100) As SampleStruct

    ?


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

    Comment


    • #3
      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>
      Lance
      mailto:[email protected]

      Comment


      • #4
        [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

        Comment


        • #5
          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>
          Lance
          mailto:[email protected]

          Comment


          • #6

            <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

            Comment


            • #7
              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>
              Lance
              mailto:[email protected]

              Comment


              • #8

                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

                Comment


                • #9
                  Oh yeah...the retval from SafeArrayRedim equates to

                  DISP_E_ARRAYISLOCKED

                  Any ideas?



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

                  Comment


                  • #10
                    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

                    Comment


                    • #11
                      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

                      Comment


                      • #12
                        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

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

                        Comment


                        • #13
                          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

                          Comment


                          • #14
                            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

                            Comment


                            • #15
                              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).]

                              Comment


                              • #16
                                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>
                                Lance
                                mailto:lanceed[email protected]

                                Comment


                                • #17
                                  [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).]

                                  Comment


                                  • #18
                                    For clarity, can you post your VB definition of MYTYPE please? Thanks!


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

                                    Comment


                                    • #19
                                      Ah! Editing your message behind my back, huh?

                                      THANKS!!!

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

                                      Comment


                                      • #20
                                        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).]

                                        Comment

                                        Working...
                                        X