Announcement

Collapse
No announcement yet.

Passing arrays to function in a DLL

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

  • Passing arrays to function in a DLL

    I have started this thread, because I need to get a clear answer

    Originally posted by Michael Mattias:
    I don't know where you got this idea the array descriptors have
    changed in PB 7.0 and that they are incompatible with PB 5.0 and 6.0,
    but it doesn't seem to be holding true
    From the best possible official source within PowerBASIC Inc.
    The problem I had was trying to use the Win 7.0 ARRAYATTR function against arrays created by PB/DLL 6.0.
    After much testing and a series of emails with demo programs to the support department, which referred the question to the "R&D" department, I got this very clear reply:
    The secret is "They are not compatible". Therefore, it would not be in
    your best interests to try to pass entire arrays between versions.
    Instead, pass a pointer to the first element and use DIM...AT x
    Maybe there are some things which will work, but this was good enough for me to avoid trying further or otherwise pressing my luck.

    [later]
    I know REDIM of a passed array will not work; this was one of the specific verbs I asked about, as I often REDIM a passed array to the correct size in external function calls.
    [/later]
    ---------------------------------------------
    I am not a 3-rd party developer but my software depends on a runtime-library developed in PB
    The first version was compiled with PBDLL50. Latest version compiled with PBWin70
    Old programs developed in PBDLL50/60/61 use this runtime-dll
    ---------------------------------------------
    So, I am in deep trouble here. I am passing a PB Array to a PB function and I can not find any indication
    in the documentation saying PB Arrays model PBWIN70 are NOT COMPATIBLE with older PB Arrays
    ---------------------------------------------
    Will this work when called from pre-pbwin70 programs?
    Code:
    Function FSO_GetFiles(ByVal PathSpec$,ByVal Mask$,Filer()As String)Export As Long
    Local fd As WIN32_FIND_DATA
    Local fAttr As Dword
    Local cnt&,hFind???
    
      If Len(PathSpec$) = 0 Then Function = 0:Exit Function
      If FSO_FolderExists(PathSpec$) = %false Then Function = 0:Exit Function
    '..samla ihop filerna..........
      On Error Resume Next
      If Right$(PathSpec$,1)<>"\" Then PathSpec$=PathSpec$ & "\"
      PathSpec$ = PathSpec$ & Mask$
      ReDim Filer(1 To 1)As String
      cnt& = 0
      hFind???  = FindFirstFile(ByVal StrPtr(PathSpec$), fd)
      If hFind??? = %INVALID_HANDLE_VALUE Then Function = %false :Exit Function
      Do
        If (Bit(fd.dwFileAttributes,4)= 0) And (Bit(fd.dwFileAttributes,8)=0) Then
         Incr cnt&
         ReDim Preserve Filer(1 To cnt&)
         Filer(cnt&) = RTrim$(fd.cFileName,Any Chr$(0,32))
        End If
        If IsFalse FindNextFile(hFind???,fd) Then Call FindClose(hFind???):Exit Do
      Loop
      Array Sort Filer(),Collate UCase
      Function = cnt&
    End Function
    ------------------
    Fred
    mailto:[email protected][email protected]</A>
    http://www.oxenby.se



    [This message has been edited by Fred Oxenby (edited January 31, 2003).]
    Fred
    mailto:[email protected][email protected]</A>
    http://www.oxenby.se

  • #2
    The center of the issue is that the array descriptor table can (and does) vary between versions of PowerBASIC. That is, the array descriptor table is an internal table maintained by the RTL, and the descriptor's internal format is version-specific. PB/Win 7.0 added the ARRAYATTR() function to allow folks to examine the descriptor table without needing to know the actual table format.

    Now, if the DLL and the EXE (from which the DLL is called) were written with the same version of PowerBASIC, then you can certainly pass arrays to DLL's just as you have done above.

    However, to ensure compatibilty between DLL's and EXE's that were written with different versions of PowerBASIC, you should pass a pointer to the array, along with another parameter containing the element count. Then the DLL would create an Absolute Array at the range passed.

    For example, to pass a string array, the EXE code might look like this:
    Code:
    DIM a(1:10) AS STRING
    ...
    CALL dllFunc(VARPTR(a(1)),10)  ' VARPTR(a(1)) is a pointer to the array's string handle table.
    ...and the DLL code might look something like this:
    Code:
    FUNCTION dllFunc(BYVAL dwStrArr AS DWORD, BYVAL lElemCount AS LONG) AS LONG
      DIM sArray(1:lEmemCount) AS STRING AT dwStrArray
      ' process sArray elements here.
    For other types of array, passing VARPTR(arr(1)) passes the address of the data held in the 1st element in the array (since only dynamic strings use a string handle table). Therefore, the technique is bascially identical regardless of the data type. However, the DLL must know what type of array is being passed, so in your case you'll only ever be passing a dynamic string array, however, if the data type can vary, then you might need to pass yet another parameter to indicate the data type, and the DLL code would need to create a different absolute array to match the data type.

    The reason that you would pass a pointer to the array data rather than the array descriptor is explained above: the descriptor table format varies between versions so there is no merit to passing a pointer to a descriptor that the DLL will not be able to read.

    Now, going back to your code, you are redimensioning the array in the DLL, which is passed back to the EXE. Unfortunately, that method is incompatible with the absolute array technique I showed above, since the calling code's array descriptor is not being changed by the DLL. Importantly though, the DLL <U>can</U> change array elements that the calling code created to start with.

    Therefore the solution would be for the EXE to create an array with sufficient array elements before passing the array to the DLL. The DLL can then modify the preallocated array elements, and then pass back the updated array count. The calling code can then perform a REDIM PRESERVE to trim off the surplus array elements, leaving only those that it actually requires.

    In pseudocode, it might look something like this:
    Code:
    ' Exe code
    DIM sArr(0:100000) AS STRING ' preallocate the array and string handle table
    lCnt = dllFunc(VARPTR(sArr(0)), 100001)
    REDIM PRESERVE sArr(0:lCnt - 1) AS STRING
    ...
     
     
    'DLL code
    FUNCTION dllFunc(BYVAL dwStrArr AS DWORD, BYVAL lMaxElems AS LONG) AS LONG
      DIM sArray(0:lMaxElems - 1) AS STRING AT dwStrArr
      DIM lCntUsed AS LONG
      ... fill in the array here, keeping track of the number of elements used in lCntUsed
      FUNCTION = lCntUsed
    END FUNCTION
    Clear as mud?

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

    Comment


    • #3
      Silly question, but I'm bad at these fancy abbreviations - "RTL" means
      "Run Time Library", yes? Maybe is mentioned in PB help. Maybe I should "RTFM"..


      ------------------
      http://www.tolkenxp.com/pb
      Download: incLean, PBcodec, custom controls and code, etc.
      Borje Hagsten - [email protected]

      Comment


      • #4
        .. I can not find any indication in the documentation saying PB Arrays model PBWIN70 are NOT COMPATIBLE with older PB Arrays..
        You can stop looking 'cuz it ain't there. I've done enough looking for the both of us.

        Just tinkering, I know the "array data type" values returned by Win7 ARRAYATTR are different between 6 and 7; but I don't know if that's because the location of the datatype in the descriptor changed or because the actual numeric value changed. Which is why, I think, you cannot REDIM across versions. Never got around to testing LBOUND and UBOUND or 'address of element zero', all of which would permit reading and writing of arrays (with confidence, that is), even if not permitting REDIM.

        MCM

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

        Comment


        • #5
          Thanks. All I can say is, I am dead.
          What about 'Dynamic strings (A$)' Their descriptor are also PB specific.
          Will I ever know if I can pass a dynamic string byref to a DLL?


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

          Comment


          • #6
            There is no problem passing scalar variables. The issue involves passing complete arrays only, as described above.

            Oh, BTW, dynamic strings use handles, arrays use descriptors.

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

            Comment


            • #7
              Sorry Lance, I have some difficulties to see the comics in my present situation.
              But I am glad that 'Handels' are handles and will never change

              -----------
              from PBDLL611 Helpfile
              Code:
              In a Sub or Function, you can use REDIM to re-dimension an array that was passed as an argument.  
              That is, when the complete array was passed to the Sub or Function:
              
              CALL RemoveDuplicates ( CustomerNames$() )
              ...
              SUB RemoveDuplicates ( a$() )
                ...
                REDIM PRESERVE a$(1:NewCount&)
              END SUB
              PBDLL611 main target was creating DLL-s, was it not?

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



              [This message has been edited by Fred Oxenby (edited January 31, 2003).]
              Fred
              mailto:[email protected][email protected]</A>
              http://www.oxenby.se

              Comment


              • #8
                Fred;

                I have similiar concerns !

                I find it curious that my dll hasn't had a problem when called
                by PB 7.0 when passing string arrays.

                I decided to write a simple program that displays the first 256
                byte values of the array descriptor for a good size string array
                (at least 100 items).

                By using VARPTR with the array (ie. VARPTR(D$()) ) I can read the
                array descriptor.

                I compiled the program using both PB 6.0 and PB 6.1 and I find that
                6.1 is is slightly different than 6.0 (one byte is different in beginning)
                and since the EXE created by 6.1 is about 4 KB bigger than the one
                created with 6.0, (different runtime), it made me wonder if
                version 6.1 is compatible with 7.0, where as 6.0 is not.

                Is PB 6.1 compatible in passing arrays to a DLL to version 7.0 ?

                I don't have PB 7.0 to run this test, so I am posting the test
                program here so others can compile it with PB 5.0, 6.0, 6.1 and 7.0
                to see what part of the descriptor has changed.

                Could someone compile this program with both PB 6.0, 6.1 and 7.0 and post
                what differences there are in the array descriptor ?

                Code:
                #COMPILE EXE
                #REGISTER NONE
                #DIM ALL
                '
                #INCLUDE "win32api.inc"
                '
                DECLARE SUB ShowDialog_Form1(BYVAL hParent&)
                DECLARE CALLBACK FUNCTION Form1_DLGPROC
                DECLARE CALLBACK FUNCTION CBF_BUTTON1()
                GLOBAL hForm1&
                '
                FUNCTION PBMAIN
                    LOCAL Count&
                    RANDOMIZE 999
                    ShowDialog_Form1 0
                    DO
                        DIALOG DOEVENTS TO Count&
                    LOOP UNTIL Count&=0
                END FUNCTION
                '
                SUB ShowDialog_Form1(BYVAL hParent&)
                    LOCAL Style&, ExStyle&
                    Style& = %WS_POPUP OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU OR %DS_CENTER
                    ExStyle& = 0
                    DIALOG NEW hParent&, "Your Dialog", 0, 0,  213,  49, Style&, ExStyle& TO hForm1&
                    CONTROL ADD "Button", hForm1&,  100,  "Read Array Descriptor", 27, 17, 160, 15, _
                        %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP CALL CBF_BUTTON1
                    DIALOG SHOW MODELESS hForm1& , CALL Form1_DLGPROC
                END SUB
                '
                CALLBACK FUNCTION Form1_DLGPROC
                    SELECT CASE CBMSG
                        CASE ELSE
                    END SELECT
                END FUNCTION
                '
                SUB ReadDescriptor(BYVAL P&, BYVAL N1&, BYVAL N2&)
                LOCAL A$, N&, T$, T2$, L&
                L&=256
                A$=PEEK$(P&,L&)
                T$=""
                FOR N&=1 TO L&
                    T$=T$+RIGHT$("000"+LTRIM$(STR$(ASC(MID$(A$,N&,1)))),3)+"  "
                    IF (N& MOD 14)=0 THEN T$=T$+CHR$(13)+CHR$(10)
                NEXT N&
                T2$="Actual Bounds are:"+ STR$(N1&)+" to"+STR$(N2&)
                N2&=(N2&-N1&)+1
                N1&=1
                MSGBOX  T$+CHR$(13)+CHR$(10)+CHR$(13)+CHR$(10)+"Internal Bounds are:"+ _
                        STR$(N1&)+" to"+STR$(N2&)+CHR$(13)+CHR$(10)+T2$
                
                END SUB
                '
                CALLBACK FUNCTION CBF_BUTTON1
                LOCAL D$(), N&, N1&, N2&
                
                    IF CBCTLMSG=%BN_CLICKED THEN
                        N1&=RND(1,5)
                        N2&=RND(100,200)
                        DIM D$(N1& TO N2&, 1 TO 5, 1 TO 5, 1 TO 5)
                        FOR N&=N1& TO N2&
                            D$(N,1,1,1)=STRING$(N1&+N2&,CHR$(255))
                        NEXT N&
                        ReadDescriptor VARPTR(D$()), LBOUND(D$,1), UBOUND(D$,1)
                    END IF
                END FUNCTION

                ------------------
                Chris Boss
                Computer Workshop
                Developer of "EZGUI"
                http://cwsof.com
                http://twitter.com/EZGUIProGuy

                Comment


                • #9
                  Well, I have in spite of my worries, not had any call about
                  failing programs from customer.

                  I have created a PBWIN7-DLL with One exported sub
                  Code:
                  #Compile Dll "NONAME.DLL"
                  
                  Sub RedimArray(a$())Export
                    ReDim a$(1 To 100)
                    For i& = 1 To 100
                     a$(i&) = "String " & Format$(i&)
                    Next
                  End Sub
                  and a PBCC211-program
                  Code:
                  Declare Sub RedimArray Lib "NONAME.DLL"(a$())
                  
                  Function PbMain()As Long
                   Dim a$()
                   ReDim a$(1 To 1)
                   RedimArray a$()
                   For i& = 1 To UBound(A$)
                    Print a$(i&)
                   Next
                   WAITKEY$
                  End Function
                  And that works OK so perhaps I'm not that dead yet....


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

                  Comment


                  • #10
                    The PB 6.1 compiler was developed while PB was working on the 7.0
                    compiler and it obviously has a larger runtime, so the compiler
                    is different than the 6.0 version.

                    Could someone from PowerBasic comment on whether the 6.1 compiler
                    is compatible with the 7.0 compiler as far as passing arrays.

                    Is the incompatibility for all versions prior to 7.0 or just 6.0
                    and previous ones ?


                    ------------------
                    Chris Boss
                    Computer Workshop
                    Developer of "EZGUI"
                    http://cwsof.com
                    http://twitter.com/EZGUIProGuy

                    Comment


                    • #11
                      I had a few other PB users compile the test program above using
                      PB 7.0 and send it to me.

                      From what the test program displays, it looks like PB 6.1 uses the
                      same array descriptors as does PB 7.0 !

                      If so, this would explain why non of my customers have had a problem,
                      since I created a service pack for EZGUI 1.0 and 2.0 using PB 6.1
                      and the current beta (2.1) for 3.0 is also compiled in PB 6.1.

                      Now all I need is to hear if from the guys at PB !

                      To PowerBasic R&D :

                      Can you comfirm whether the PB 6.1 compiler uses the same array
                      descriptors as does PB 7.0, so we can know whether it is safe to
                      pass arrays between apps written in PB 7.0 to DLLs written in PB 6.1 ?





                      ------------------
                      Chris Boss
                      Computer Workshop
                      Developer of "EZGUI"
                      http://cwsof.com
                      http://twitter.com/EZGUIProGuy

                      Comment


                      • #12
                        Chris, you already have. Please re-review my 1st reply above... Thanks!

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

                        Comment


                        • #13
                          Originally posted by Borje Hagsten:
                          Silly question, but I'm bad at these fancy abbreviations - "RTL" means
                          "Run Time Library", yes? Maybe is mentioned in PB help. Maybe I should "RTFM"..


                          RTFM = "Read the fine manual" ?

                          ------------------
                          Barry

                          Comment


                          • #14
                            Ok, for those whom seek more info on passing arrays between EXE's and DLL's, here is the low-down directly from R&D:
                            When you pass a full array parameter, the compiler passes a pointer to the
                            array descriptor. For normal operations, like reading or writing one
                            element everything works perfectly ok today. That's because the compiler
                            does not need to access all of the descriptor information, where the array
                            data is accessed one array element at a time. The problem arises for
                            functionality where the array is referenced in its entirety like REDIM,
                            ERASE, JOIN, SORT, MAT.

                            So the rule is, if you are referencing it one item at a time, you should be
                            fine, for the most part. But you should still develop an alternate
                            strategy for the future.

                            Therefore, programmers should be urged...[list=1][*]You can only pass a full array when both EXE and DLL are created by
                            PowerBASIC. Therefore, first ensure they are created by the same version.
                            Otherwise, don't do it.
                            [*]Or, pass a pointer to the first element and use DIM...AT address to
                            manipulate it. Or use a pointer to manipulate it. Or, pack all the array
                            data into a string or a struc and pass that.
                            [*]Or, create functions in the module which creates the array to
                            manipulate it. That is, if a DLL creates an array, create a function in
                            the DLL to REDIM or ERASE it. The EXE would then call the function to
                            REDIM or ERASE it. Since it's created and altered in the same module,
                            everything is guaranteed to work.
                            [*]Or maybe even pass the array in a Variant, though they may not last
                            forever, either.[/list=a]
                            Items one and two are clearly the best long-term choices.

                            The real point is that you should avoid passing array descriptors between
                            modules unless you can assure like versions. We don't want to change
                            descriptors again now, or in the future, but it's going to happen. A simple fact
                            of life.

                            For PB/Win 7.0 and PB/CC 3.0 users, you can detect the current change by using ARRAYATTR().
                            That is, if you use ARRAYATTR(x%(),1) on an array, it should return %VARCLASS_INT. If it
                            does not equal that value, the array was created by an incompatible version
                            of PowerBASIC.
                            I hope this helps!


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

                            Comment


                            • #15
                              This is one of my gravest concerns about upgrading with PB.
                              These kinds of "hidden features" are just absolutly deadly
                              to me. All I need is one of these to trip me up and I can loose
                              a potential customer. I dont have the time nor the inclination
                              to learn about goshdarned decriptor tables. Thats what I pay PB
                              folks to handle for me.

                              As I have mentioned before, you guys
                              really need to be upfront about these kinds of changes to the
                              compiler. Nothing less than a very obvious document listing
                              every one of these differences is required. It is not OK for
                              this level of incompatabilty to surface this long after the
                              release of PB7.

                              Untill R&D, who must know about every one of these kinds of
                              changes, are more forthcoming, I for one will never upgrade
                              again! This is a strong statement, and I hope you guys get the
                              gravity of this. It is NOT ok!


                              ------------------
                              Kind Regards
                              Mike

                              Comment


                              • #16
                                Mike --

                                I understand your point, but has anybody reported a single actual problem that this has caused?

                                Avoiding updates is of course an option. That way you will be dealing with a known quantity, and nothing bad can happen. But nothing good can happen either.

                                -- Eric


                                ------------------
                                Perfect Sync Development Tools
                                Perfect Sync Web Site
                                Contact Us: mailto:[email protected][email protected]</A>

                                [This message has been edited by Eric Pearson (edited February 01, 2003).]
                                "Not my circus, not my monkeys."

                                Comment


                                • #17
                                  Well put Eric.

                                  In addition, PowerBASIC has never said that the [internal] array descriptor table would never change, and as discussed above, it is necessary when adding those new features y'all take great care in asking us to build into the language. This is one reason that PowerBASIC added in the ARRAYATTR() function in PB/Win 7.0 and PB/CC 3.0 -- so that arrays could be examined without having to deal with the internal descriptor structure.

                                  Unfortunately it's not really possible to have it both ways all the time, although it has to be said that R&D go to great lengths to keep these kind of disruptions to a minimum, and so far, it looks like they've done an excellent job of it.

                                  I hope you see the light and change your mind, Mike, but we're not going to force you to upgrade it you don't want to. IOW, your position is fine provided that you accept that you will be missing out on all the goodies and new functionality that the latest versions of PowerBASIC provide. You must also understand that since PB/Win 7.0 supercedes 6.1x, it is unlikely that any additional [minor] updates for PB/DLL 6.1x will be released in the future.


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

                                  Comment


                                  • #18
                                    I understand your point, but has anybody reported a single actual problem that this has caused?
                                    Here!

                                    Following PB's suggestions I upgrade my 6.0 code to 7.0. The application consists of 1 EXE, plus 2 support DLL's, plus five optional (i.e., extra priced) DLLs. (See it at The Provider Payment Partner System.

                                    I was going to do this module by module. Module One migration failed. End of Expedition.

                                    I only blew about a day and a half of my time.

                                    Now I will upgrade when I do a "major" upgrade with some new features I wanted to add anyway. Of course, I will re-engineer a few things vis-a-vis array passing.

                                    My complaint is not that this was done: it was that it was done without any notice to existing customers... customers who were agressively solicited to purchase the upgrades.

                                    FWIW... if the %VARCLASS_xxxx equates returned by ARRAYATTR(Array(),1) follow the help file documentation...

                                    Returns the data type, as defined in the following table. It is important to note that the numeric equates listed on the right of the table are built into PowerBASIC, but the numeric values they represent are subject to change. Therefore, application code should always use the numeric equates rather than the numeric value, to ensure compatibility with future versions of PowerBASIC.
                                    ..We will have the same problem going from 7.0 to 7.next or 8.first

                                    MCM



                                    [This message has been edited by Michael Mattias (edited February 01, 2003).]
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      To me, the point isn't that you have changed the internal array descriptor table, but the fact that we haven't been duly informed of it. The technical note of R&D should have been included in the New Features of the help file.

                                      Contrarily to the opinion that to pass an entire array isn't a common practice, I believe that is one of the most common practices when you're a beginner, because until you have experience it's difficult to understand the technique of pass a pointer and use REDIM AT.

                                      Please, in the future don't wait to inform us until somebody reports a problem. Thanks.


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

                                      Comment


                                      • #20
                                        My gripe is that you guys dont tell us! I need an acompanying
                                        document that cannot be missed, telling me in general and specific
                                        terms what has changed and what the possible rammifications
                                        are. Why is this so hard? PB is a great product but this mushroom
                                        principal is just not working for me. PB is not a lesser product
                                        because you boldy state "We changed the descriptor mapping
                                        interface bimodal bit setting function hiword in order to bring you
                                        increased functionality ... This may affect the passing of arrays with
                                        earlier versions of PB"

                                        Then I can think, hmmmm do I have any applications that are critical
                                        that might be affected by this. If I do I can wait untill I have time
                                        to check it out before i upgrade.


                                        ------------------
                                        Kind Regards
                                        Mike

                                        Comment

                                        Working...
                                        X