Announcement

Collapse
No announcement yet.

Passing Dynamic String Array built in PBDLL to VB6

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

  • Passing Dynamic String Array built in PBDLL to VB6

    I have read all the posts on the subject of passing VB arrays to PBDLL
    but I am trying to figure out whether it is possible to build a string
    array in PB from a very large text file (using the ideas rehearsed in
    such posts as 'VB smokes PB' etc etc. When the array is built I then need
    to pass the array back to VB.

    It is clear from several forum participants that PB should be able to load
    and parse the file into an array faster than VB. To enable the routine to run
    on machines with modest memory I think I will have to load blocks of the
    data file at a time and deal with parsing each line in that block into
    the array before loading another block of the file into memory for
    processing. The source data file is larger than 250Mb and it contains
    many duplicate lines of data. I am trying to parse out only the unique
    lines in the file, checking if the line already exists in the array,
    adding anything new and passing over anything which is a duplicate - i.e. it
    already has been added to the array.

    This should leave me with an array of about a thousand items which
    I would then like to pass back to VB for further processing and user
    interaction.

    I am convinced that PB will do this job much faster than VB...could
    anyone suggest the best approach or if indeed it is possible to do it
    this way round rather than create an array and pass it to PB from VB.
    I have considered this but I dont know what size the array would end up
    once the file is parsed.

    Any suggestions on this approach would be much appreciated.
    My thanks in advance to anyone who can help.


    ------------------
    Mike Letchford
    Mike Letchford

  • #2
    Mike -
    Guess, that the fastest and safety way will be to process a big file in PB and to pass results to VB as small external file.
    BTW, in PB it's important to buffer output (for example, to pre-allocate a string of 8-16 KB and to fill it using Mid or MoveMemory).


    ------------------
    E-MAIL: [email protected]

    Comment


    • #3
      Hello again Semen!

      Thanks for the suggestion - I had not thought of doing it that way.
      Funny how the simple things don't occur to you when you are trying to be clever!

      Could you please expand on your buffering output comment. I am not quite sure what you mean

      Many thanks for your help....


      ------------------
      Mike Letchford
      Mike Letchford

      Comment


      • #4
        Mike --
        compare two variants on your PC

        Code:
              #Compile Exe
              #Dim All
              #Register None
              
              Function PbMain
                 Local i As Long, n  As Long, l As Long, m As Long, lBuf As Long, t1 As Single, t2 As Single
                 n = 100000
                 ReDim a(n) As Local String
                 For i = 1 To n: a(i) = "Line" + Str$(i): Next
                     
                 ' Variant 1
                 t1 = Timer
                 Open "c:\1.Txt" For Output As #1 Len = 32768
                 For i = 1 To n
                    Print #1, a(i)
                 Next
                 Close #1
                 t2 = Timer
                 MsgBox Format$(t2 - t1, "0.000 sec")
                 
                 ' Variant 2
                 Local BufBig As String
                 lBuf = 1024 * 16 ' 16K
                 BufBig = Space$(lBuf)
                 
                 t1 = Timer
                 Open "c:\2.Txt" For Output As #1
                 l = 0
                 For i = 1 To n
                    m = Len(a(i)) + 2
                    If l > (lBuf - m) Then Print #1, Left$(BufBig, l);: l = 0
                    Mid$(BufBig, l + 1, m) = a(i) + $CRLF
                    l = l + m
                 Next
                 Print #1, Left$(BufBig, l);
                 Close #1
                 t2 = Timer
                 MsgBox Format$(t2 - t1, "0.000 sec")     
              
              End Function
        ------------------
        E-MAIL: [email protected]

        Comment


        • #5

          Assuming your duplicate criteria can be formed into a key, this becomes a sort problem. Commercial sort libraries like SyncSort can eliminate duplicates very efficiently and quickly. You should take a look (www.syncsort.com).

          But... if might be fun to write a PB procedure which does something like..

          SUB SortFile (InputFilename$, keyType, keyoffset, keylen, Uniquefile$,DupFile$)

          ...where Uniquefile$ is where the uniquely keyed items go, and dupFile$ is a file where all the items eliminated go.

          You might consider the same kind of approach in your problem: instead of trying to return the array directly to VB, write your PB/DLL function to read and produce disk files, then load the result files from within VB.

          MCM


          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Originally posted by Semen Matusovski:
            Mike --
            compare two variants on your PC

            Hello Semen --

            Wow!......what a difference

            Many thanks for the insight....I am working on your suggested approach

            .....Also thanks to Michael for suggesting splitting the file etc.




            ------------------
            Mike Letchford
            Mike Letchford

            Comment


            • #7
              RE:
              Code:
                lBuf = 1024 * 16 ' 16K
                BufBig = SPACE$(lBuf)
                t1 = TIMER
              The two executable statements should FOLLOW the TIMER statement, as they are necessary to use "Method One" and not necessary to use "Method Two" and so rightly should be charged against the time for method one.

              Also, if your customer support response time is consistently thirteen years and four new major versions of the compiler you might want to consider a career change.

              And as long as you are allowing for new versions of the compiler, it would be a lot easier to...
              Code:
                LOCAL v AS VARIANT 
                LET  v =   PbArray()
              .. and simply pass 'v' to the VB function.
              Last edited by Michael Mattias; 28 Aug 2014, 08:28 AM.
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                Your earlier suggestion was to use Syncsort or pass a file.

                Michael M,
                Have you ever passed a variant string or array to VB6?

                Here is an attempt that does not return anything in VB6.

                Code:
                 #DIM ALL
                #COMPILE DLL
                FUNCTION PBMAIN AS LONG          'variant.bas
                  LOCAL v AS VARIANT
                  pbVariant v
                  ? VARIANT$(v)
                END FUNCTION
                 '
                 SUB PbVariant(v AS VARIANT) EXPORT
                  'LOCAL i, n as lONG
                  'n = 100
                  'DIM a(n) AS STRING
                  'FOR i = 1 TO n: a(i) = "Line" + STR$(i):next
                  v = "Michael Mattias"
                  ? VARIANT$(v),,"PB"
                  'let v = a()
                END SUB
                 
                PRIVATE DECLARE SUB pbVariant LIB "variant.dll" ALIAS "PBVARIANT" (v AS VARIANT)
                 PRIVATE SUB Form_Load()
                  DIM v AS VARIANT
                  pbVariant (v)
                  MSGBOX v, , "VB6"
                END SUB
                The world is full of apathy, but who cares?

                Comment


                • #9
                  >Have you ever passed a variant string or array to VB6?

                  I've passed variant strings, numbers and arrays (created just as you see here) to ADO.

                  I think that's probably close to passing to a VB-written procedure.

                  I don't know why your call to pbVariant() from Form_Load is "not working" but if it's working from PBMAIN it's not a problem with any of the PB code.

                  Is there maybe some function available in VB which will tell you the type of the variant returned? Something like the PB VARIANTVT() function?

                  Or maybe because your VB code is passing '(v)' instead if simply 'v' (no parens). In PB using that syntax would pass the value of v (a copy) but I don't know how vb interprets those parens in the absence of the CALL keyword.

                  MCM
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    I appreciate bringing up using a variant which I have never used.

                    Not having much luck with VB6, but found this Gary Beene example
                    that looks like what I want. It is passing the data the wrong way,
                    but if I can reverse it to pass from PB to VB6 my 2D array into a grid
                    will work.

                    The world is full of apathy, but who cares?

                    Comment


                    • #11
                      I've also used the safearray functions, DECLAREs for which were provided with the compiler in a file called vb.inc thru at least PB/9x. I don't know if that file is still part of the "#INCLUDE" set for 10x.

                      These are not as "clean" at the source code end as is the syntax using the VARIANT variable type but everything works as advertised.
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        No dynamic example. This passes variant arrays.

                        Code:
                        'PowerBASIC code creating variant arrays to pass to VB6
                         #COMPILE DLL  'pb2vb.bas
                        #DIM ALL      'Send 1 and 2 dimensional variant array to VB6
                        '--------------------------------------------------------------------------------
                        SUB PB2VB1 (BYREF vScores AS VARIANT) EXPORT
                          LOCAL ROW,COL AS LONG
                          DIM sArray(-1 TO 1) AS STRING
                          FOR ROW = LBOUND(sArray) TO UBOUND(sArray)
                            sArray(ROW) = "R"+FORMAT$(ROW)
                          NEXT
                          vScores = sArray()
                        END SUB
                        '--------------------------------------------------------------------------------
                        SUB PB2VB2(BYREF vScores AS VARIANT) EXPORT
                          LOCAL ROW,COL AS LONG
                          DIM sArray(1 TO 3, -1 TO 1) AS STRING
                          FOR ROW = LBOUND(sArray) TO UBOUND(sArray)
                            FOR COL = LBOUND(sArray,2) TO UBOUND(sArray,2)
                              sArray(ROW,COL) = "R"+FORMAT$(ROW)+",C"+FORMAT$(COL)
                            NEXT
                          NEXT
                          vScores = sArray()
                        END SUB
                        'VB6 code reading variant arrays passed from PowerBASIC
                        Code:
                        Option Explicit  'VB6 code
                        Private Declare Sub pb2vb1 Lib "pb2vb.dll" Alias "PB2VB1" (ByRef vScores As Variant)
                        Private Declare Sub pb2vb2 Lib "pb2vb.dll" Alias "PB2VB2" (ByRef vScores As Variant)
                         Private Sub Command1_Click()
                          Dim v As Variant
                          pb2vb2 v  'create 2-dimensional array in PB and return it
                          ShowVariantArray v 'show values
                          
                          pb2vb1 v  'create 1-dimensional array in PB and return it
                          ShowVariantArray v 'show values
                          Unload Me: End
                        End Sub
                         Sub ShowVariantArray(v As Variant)
                          Dim row As Long, col As Long, i As Long, dimNum As Long
                          If VarType(v) = vbArray + vbString Then
                            On Error GoTo FinalDimension
                            For dimNum = 1 To 60000  'see how many dimensions
                               i = LBound(v, dimNum)
                            Next
                          End If
                        FinalDimension:
                          dimNum = dimNum - 1
                          MsgBox "Dimensions" + Str$(dimNum)
                          Select Case dimNum
                            Case 1  '1-dimensional array
                              For row = LBound(v) To UBound(v)
                                MsgBox v(row)
                              Next
                            Case 2  '2-dimensional array
                              For row = LBound(v) To UBound(v)
                                For col = LBound(v, 2) To UBound(v, 2)
                                  MsgBox v(row, col)
                                 Next
                              Next
                            Case Else
                                MsgBox "Only 1 and 2 dimensional arrays are supported", , Str$(dimNum)
                           End Select
                        End Sub
                        The world is full of apathy, but who cares?

                        Comment


                        • #13
                          The file is actually "VBAPI32.INC" and it IS supplied as one of the header files with the 10x compiler

                          Re..
                          Code:
                           If VarType(v) = vbArray + vbString Then
                              On Error GoTo FinalDimension
                              For dimNum = 1 To 60000  'see how many dimensions
                                 i = LBound(v, dimNum)
                              Next
                            End If
                          FinalDimension:
                            dimNum = dimNum - 1
                            MsgBox "Dimensions" + Str$(dimNum)
                          ...
                          That #INCLUDE file contains this function..
                          Code:
                           
                          DECLARE FUNCTION SafeArrayGetDim _
                              LIB "OLEAUT32.DLL" _ 
                              ALIAS "SafeArrayGetDim"  _
                              (BYVAL psa AS DWORD) AS DWORD
                          ... which is a little "Cleaner" way to get the number of dimensions in a passed array (safearray).

                          I don't know that VB has something like "ARRAYATTR (arrayvar(), number of dimensions constant)" as does PB, but I do know you can call WinAPI functions directly from VB.

                          Heck, you could write your own "ARRAYATTR for VB" function using the "underlying Windows functions" and that could make your VB programming with arrays a lot easier.

                          Suggest param in = VARIANT, since you've shown how to get arrays from PB to VB that way. (trust me it is a lot cleaner than creating safearrays using the low-level functions)

                          (I HAD to use the low level functions! PowerBASIC did not support VARIANTS at the time!)

                          The PB code in that file shows you how to get that "psa" (assumed to be a clever abbreviation for "pointer to a safearray" ) which is used in all those safearray functions. There has got to be a way to do the same thing in VB when it's passed as a VARIANT.

                          MCM
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            Sheesh, the VARIANT data type definition in the 10x header file is pure torture.

                            The one from the 9x Win32API.INC is a lot easier to use and understand...

                            Code:
                            UNION VARIANTDATA_SIMPLE
                              bVal AS BYTE            ' VT_UI1
                              iVal AS INTEGER         ' VT_I2
                              lVal AS LONG            ' VT_I4
                              fltVal AS SINGLE        ' VT_R4
                              dblVal AS DOUBLE        ' VT_R8
                              boolVal AS INTEGER      ' VT_BOOL
                              scode AS LONG           ' VT_ERROR
                              cyVal AS LONG           ' VT_CY
                              date AS DOUBLE          ' VT_DATE
                              bstrVal AS LONG         ' VT_BSTR
                              punkVal AS DWORD        ' VT_UNKNOWN
                              pdispVal AS DWORD       ' VT_DISPATCH
                              parray AS DWORD         ' VT_ARRAY|*
                              pbVal AS BYTE PTR       ' VT_BYREF|VT_UI1
                              piVal AS INTEGER PTR    ' VT_BYREF|VT_I2
                              plVal AS LONG PTR       ' VT_BYREF|VT_I4
                              pfltVal AS SINGLE PTR   ' VT_BYREF|VT_R4
                              pdblVal AS DOUBLE PTR   ' VT_BYREF|VT_R8
                              pboolVal AS INTEGER PTR ' VT_BYREF|VT_BOOL
                              pscode AS LONG PTR      ' VT_BYREF|VT_ERROR
                              pcyVal AS LONG PTR      ' VT_BYREF|VT_CY
                              pdate AS DOUBLE PTR     ' VT_BYREF|VT_DATE
                              pbstrVal AS LONG PTR    ' VT_BYREF|VT_BSTR
                              ppunkVal AS DWORD PTR   ' VT_BYREF|VT_UNKNOWN
                              ppdispVal AS DWORD PTR  ' VT_BYREF|VT_DISPATCH
                              psArray AS DWORD PTR    ' VT_ARRAY|*
                              pVariant AS DWORD PTR   ' VT_BYREF|VT_VARIANT
                              pByRef AS DWORD         ' Generic ByRef
                            END UNION
                            
                            
                            TYPE VARIANTAPI_SIMPLE
                              vt         AS WORD  'VARTYPE
                              wReserved1 AS WORD
                              wReserved2 AS WORD
                              wReserved3 AS WORD
                              vd         AS VARIANTDATA_SIMPLE 
                            END TYPE
                            
                            
                            FUNCTION GetSafeArrayPtr (V AS VARIANT) AS DWORD 
                            
                             LOCAL pV as VARIANTAPI_SIMPLE  PTR 
                            
                               IF VARIANTVT (V) = %VT_SAFEARRAY 
                            
                                     pv = VARPTR (V) 
                                     FUNCTION = @pv.vd.pArray 
                               ELSE
                                     FUNCTION = &hFFFFFFFF??? ' artificial meaning "passed variant 
                                                              ' variable not type VT_SAFEARRAY
                               END IF
                            END FUNCTION
                            
                               ... 
                              LOCAL v AS VARIANT 
                              LOCAL p AS DWORD      
                              LOCAL psa   AS DWORD  ' it's actually a SAFEARRAY PTR 
                            
                              LET V = some pb Array()  
                            
                              pSA = GetSafeArrayPtr (V)
                            Now you can call all those safearray functions in VBAPI32.INC easily!

                            MCM
                            Michael Mattias
                            Tal Systems (retired)
                            Port Washington WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment

                            Working...
                            X