Announcement

Collapse
No announcement yet.

Possible bug in PowerBASIC's implementation of Fixed-length strings

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

  • Possible bug in PowerBASIC's implementation of Fixed-length strings

    There may be a possible bug in PowerBASIC's implementation of Fixed-length strings.

    Below is the Excel VBA code which calls a PowerBASIC DLL function:

    Code:
     
    Declare Function ProcStrArry Lib "C:\PBWin90\MySamples\MySample1.dll" (ByRef x$) As String
    Sub TestPassStrArrs()
    Dim MyVarArr(0 To 3) As String
    ' The following strings have a Fixed length of 2:
    MyVarArr(0) = "xc"
    MyVarArr(1) = "SR"
    MyVarArr(2) = "DI"
    MyVarArr(3) = "CO"
    Dim AnswStr As String
    ' Call the PowerBASIC DLL's ProcStrArry function:
    AnswStr = ProcStrArry(MyVarArr(0))
    MsgBox "AnswStr = " & AnswStr
    End Sub
    Following is the code for the PowerBasic DLL called by the above Excel VBA code:

    Code:
     
    FUNCTION ProcStrArry ALIAS "ProcStrArry" (BYREF x AS STRING) EXPORT AS STRING
    DIM xPtr AS STRING PTR * 2
    ' Using xPtr = VARPTR(x$) gives random non-repeatable displays of Result0 - Result4
    ' Let's try:
    xPtr = STRPTR(x$) ' At least this results in a correct display of Result0.
    DIM Result0 AS STRING
    DIM Result1 AS STRING
    DIM Result2 AS STRING
    DIM Result3 AS STRING
    Result0 = @xPtr[0]
    MSGBOX "Current value of Result0 is: " & Result0
    Result1 = @xPtr[1]
    MSGBOX "Current value of Result1 is: " & STR$(Result1=Result1)
    Result2 = @xPtr[2]
    MSGBOX "Current value of Result2 is: " & Result2
    Result3 = @xPtr[3]
    MSGBOX "Current value of Result3 is: " & Result3
    FUNCTION = "DONE"
    END FUNCTION
    Executing the above Excel VBA code always yields a correct display by the PowerBasic DLL of Result0, namely "xc". This correct result is repeatable. The displays for Result1, Result2, and Result3, however, appear to be random text and are not repeatable.

    Hopefully the bug is in my code and not in PowerBASIC's implementation of
    Fixed-length strings. Your suggestions and corrections will be greatly appreciated. May you have a blessed evening.

  • #2
    Not sure what you messing with..
    BYREF in VB(A) is most likely a varptr to unicode string.
    PB is ansi code so you would need the Windows API.
    Better is to use a variant (evt. safearray).

    Maybe the VB data exchange examples are still in the examples folder?
    hellobasic

    Comment


    • #3
      looking at your code.. why not pass it byval so VB will make it an ansi string (a feature that is), it doesn't seem to be modified??

      added:
      Yukk, you try to reach the array from inside with pb code, no..!

      Like i mentioned, you can use a variant like:
      Local v as variant
      v = thearray()
      pass v.
      The internal conversion to/from safarray should be done this way.
      Not tested but should work.
      Last edited by Edwin Knoppert; 27 Mar 2009, 06:47 PM.
      hellobasic

      Comment


      • #4
        Hi Edwin:
        Thanks for your responses. There is one thing we both should keep in mind: Result0 is always displayed correctly. This tells me that the problem is not an incorrect string format (ansi, unicode, etc.) If incorrect string format or handling were the problem, Result0 could never be displayed correctly. Further, repeated executions of the Excel VBA code, always result in Result1 through Result3 containing random gibberish which is not repeatable over successive executions. In other words, every time I execute the VBA code, different results are obtained for Result1, Result2, and Result3. This tells me that Result1 = @xPtr[1], Result2 = @xPtr[2], and Result3 = @xPtr[3] are either being incorrectly implemented, or my code is not using @xPtr correctly. But since I am getting the correct result when the PowerBASIC DLL executes Result0 = @xPtr[0], and then correctly displays Result0, we know that the problem is probably not in the implementation or handling of Result0 = @xPtr[0], but rather in the implementation or handling of Result1 = @xPtr[1], Result2 = @xPtr[2], and Result3 = @xPtr[3]. May you have a blessed evening.

        Sincerely,

        Michael Fitzpatrick
        Last edited by Michael Fitzpatrick; 27 Mar 2009, 10:11 PM. Reason: typos

        Comment


        • #5
          xPtr[1] doesn't point to 2nd element of array whose 1st element is passed to DLL

          After extensive testing, I have come to the conclusion that in the SuperBASIC DLL, although @xPtr[0] is in fact the contents (= "xc") of the first string of the fixed-length array that xPtr = STRPTR(x) is pointing to, xPtr[1] does not point to the second string of that array and therefore @xPtr[1] is not the contents of the second string of that array. In fact, I wrote a loop to search from xPtr[-1000] through xPtr[6000] , and could not find "SR" (the contents of the second string of that array) in that range. It seems that this might be a bug in SuperBASIC's implementation of Pointers (@) when the first element of the fixed-length string array in question is passed from Excel to a SuperBASIC DLL and it is desired to examine or utilize the other elements of that fixed-length string array. So far, my testing shows that the implementation of Pointers (@) is correct for integers, double precision numbers, strings, and arrays of double precision numbers. I haven't tested other types.
          Last edited by Michael Fitzpatrick; 28 Mar 2009, 12:58 AM. Reason: typo

          Comment


          • #6
            PowerBASIC strings function just fine.

            If I were you, Michael, I'd report this terrible bug at once to SuperBasic Technical Support. However, if they aren't able to help, you might try this idea in PowerBASIC instead. {smile}

            If you try that approach, you'll need to correct your code a bit. You are passing a parameter as a pointer to the target data of a dynamic (OLE) string. OLE strings are not allocated contiguously by Windows. They can be virtually anywhere Windows thinks is best for you.

            Rather, you need to pass the VARPTR, get each handle, and follow it successively to each target data item. There are other methodologies, as well.

            PowerBASIC strings function just fine.

            Best regards,

            Bob Zale
            PowerBASIC Inc.

            Comment


            • #7
              Code:
              DIM xPtr AS STRING PTR * 2
              
               xPtr      = ????
              
               Result0 = @xPtr[0]
               Result1 = @xPtr[1]
               Result2 = @xPtr[2]
               Result3 = @xPtr[3]
              This code assumes xPtr is the address of an eight-character buffer ="xcSRDICO"

              So if you change the way you call the function, your code will work just fine.

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

              Comment


              • #8
                No references to xcSRDICO

                Hi Michael:

                Thank you very much for your response. I searched the entire PowerBasic web site, all discussion groups, and the User's manual for xcSRDICO and found nothing. Then I searched the WWW using Google for xcSRDICO and found nothing. Then I searched all of Amazon.com and was informed: "Your search "xcSRDICO" did not match any products. " I am not sure how to proceed at this point. What is the best way to learn about xcSRDICO and how to use it? Also what is the best way to learn how to set xPtr to the address of the eight-character buffer ="xcSRDICO"? Is "xcSRDICO" hexadecimal? Thanks! May you have a blessed day.

                Sincerely,

                Michael Fitzpatrick

                Comment


                • #9
                  "xcSRDICO" is your data that you used for your example.

                  Best regards,

                  Bob Zale
                  PowerBASIC Inc.

                  Comment


                  • #10
                    PowerBASIC strings function just fine

                    Hi Bob:

                    Thanks for your quick response. I am a newbie at Basic and am not sure how to implement your suggestion. Is there available any example code which shows how to pass a VARPTR, get a handle, and follow it to its target data item? Also what is the VARPTR being passed to? And how does one get the handle of a VARPTR? Thanks! May you have a blessed day.

                    Sincerely,

                    Michael Fitzpatrick

                    Comment


                    • #11
                      Here's one way of doing it. There are many...


                      Code:
                      Function PBMain()                                                             █
                        Local AnswStr As String                                                     ░
                        Dim MyVarArr(0 To 3) As String                                              ░
                        MyVarArr(0) = "xc"                                                          ░
                        MyVarArr(1) = "SR"                                                          ░
                        MyVarArr(2) = "DI"                                                          ░
                        MyVarArr(3) = "CO"                                                          ░
                        AnswStr = ProcStrArry(MyVarArr(0))                                          ░
                        MsgBox "AnswStr = " & AnswStr                                               ░
                      End Function                                                                  ░
                                                                                                    ░
                      FUNCTION ProcStrArry ALIAS "ProcStrArry" (BYREF x AS STRING) EXPORT AS STRING ░
                        Local xPtr as String Ptr                                                    ░
                        Local Result0, Result1, Result2, Result3 AS STRING                          ░
                        xPtr = VarPtr(x)                                                            ░
                                                                                                    ░
                        Result0 = @xPtr[0]                                                          ░
                        Result1 = @xPtr[1]                                                          ░
                        Result2 = @xPtr[2]                                                          ░
                        Result3 = @xPtr[3]                                                          ░
                                                                                                    ░
                        MSGBOX "Current value of Result0 is: " & Result0                            ░
                        MSGBOX "Current value of Result1 is: " & Result1                            ░
                        MSGBOX "Current value of Result2 is: " & Result2                            ░
                        MSGBOX "Current value of Result3 is: " & Result3                            ░
                        FUNCTION = "DONE"                                                           ░
                      END FUNCTION                                                                  ░


                      The misunderstanding was thinking that data for 4 dynamic strings would be stored contiguously in memory. Not true. A single dynamic string variable is a 32-bit handle which represents the location of the string data. Windows implements those handles as 32-bit pointers to string data in OLE string space. A 4 element array is four of those pointers which could point anywhere, because each could be any length, including nul, in which case the pointer would be zero. Effectively, you have string pointers with two levels of indirection.

                      Best regards,

                      Bob Zale
                      PowerBASIC Inc.

                      Comment


                      • #12
                        I am a newbie at Basic and am not sure how to implement your suggestion. Is there available any example code which shows how to pass a VARPTR, get a handle, and follow it to its target data item? Also what is the VARPTR being passed to? And how does one get the handle of a VARPTR?
                        A newbie? Really? No!

                        Developing mixed-language software requires a good fundamental understanding of addresses and pointer variables.

                        VARPTR is used to get an address. But if you do not understand that to be the purpose of the VARPTR function, you should not be using it. If you think there is such a thing as a "handle" for "a VARPTR" , you do not understand it. If you do not understand VARPTR, you should not be using pointer variables, either.

                        Bottom line? IMO you are in a hole and first thing you should do is stop digging.

                        Take a suggestion?

                        Instead of calling your function in your PB-DLL from VBA, write yourself a little test program and get your code working correctly when calliing from your own EXE. Then you will know any problems you have calling the function from VBA are caused by the addressing scheme used by VBA and not by your misunderstanding of addresses.

                        Take a second suggestion? Forget about using either VB or PB arrays in this program

                        Have your VBA code pass one (1) string only. If the lengths of the elements are variable, send it as a single delimited string and use PARSE to break it apart in your function.

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

                        Comment


                        • #13
                          VARPTR() returns the address of a variable. It's simply important to understand the nature and contents of the variable. In the case of a dynamic string, it's a handle which is implemented as a pointer to string data. Therefore, it's correct to say that VARPTR returns the address of a string handle/pointer. If this handle/pointer is zero, the string is nul/empty. If it is non-zero, it's a pointer to the actual string data. The actual string data is preceded by a DWORD which is the string length, in bytes.

                          In contrast, STRPTR() returns a pointer to the actual string data, bypassing the initial handle/pointer. Knowing just the STRPTR() of a dynamic string will not allow you to find the STRPTR() of other members of a dynamic string array.

                          Best regards,

                          Bob Zale
                          PowerBASIC Inc.

                          Comment


                          • #14
                            Passing string arrays from Excel to a PowerBASIC created DLL

                            Greetings! I thank everyone of you for your responses. My main purpose in creating the DLL was to take some slow-running interpreted code from an Excel VBA module and place it in a DLL which could run that code much faster, the code in a DLL being compiled rather than interpreted. Therefore my main objectives are speed, speed, and speed.
                            Now here is my bright idea for essentially transferring string information from Excel to a DLL which will process it. Since I have only 4 different types of strings: "xc", "COO", "SR" and "DIR", I will let these strings be represented by integers as follows:

                            Code:
                             
                            String             Use this Integer Representation
                            "xc"                              4
                            "COO"                             3
                            "SR"                              2
                            "DIR"                             1
                            For example, the string array whose elements are:
                            OfcArry(0) = "xc"
                            OfcArry(1) = "DIR"
                            OfcArry(2) = "SR"
                            . . .
                            OfcArry(49) = "COO"

                            can instead be represented by an array of integers whose elements are:
                            IOfcArry(0) = 4
                            IOfcArry(1) = 1
                            IOfcArry(2) = 2
                            . . .
                            IOfcArry(49) = 3

                            and I believe that transferring this array of 50 integers from Excel to the PowerBASIC DLL is probably much faster than any of the schemes suggested or hinted at in this thread. Of course, if anyone has a faster and/or simpler scheme than this one, I will be happy to adopt it. May you have a blessed day.

                            Sincerely,

                            Michael Fitzpatrick

                            Comment


                            • #15
                              If all of your string data is 4 bytes or less, don't use a numeric value which merely "represents" the string. Instead, use a Long Integer which is equal to the 4 bytes or less of string data. That totally eliminates the conversion step.

                              Comment


                              • #16
                                Could you not pass it as a delimiter'ed string? That is, join all the elements into one string with, say $TAB's between the elements and then just break it back apart when your DLL gets it?
                                Furcadia, an interesting online MMORPG in which you can create and program your own content.

                                Comment


                                • #17
                                  Your suggestion

                                  Thanks for your suggestion. Since my motive here is speed, I will have to compare the execution time of packing the data (in Excel VBA which is interpreted, not compiled) and then unpacking it in my DLL with the time to pass an array of integers (already formed in Excel) to a DLL which then utilizes that array. Thanks again for your suggestion. May you have a blessed day.

                                  Sincerely,

                                  Michael Fitzpatrick

                                  Comment


                                  • #18
                                    Re Bob Zale's 8:12 PM suggestion

                                    Hi Bob:
                                    Thanks for the suggestion. I tried to implement it in the following Excel VBA code which will call the DLL to display the various values of the LongVarArr Array. Unfortunately when I tried to compile this Excel VBA code, the compiler gave me the message: "Type mismatch". I clicked the debug button of the message, and "LongVarArr(0) = "xc"" was highlighted. Apparently, Excel does not want to convert a text string to its value as a Long. How should I proceed with this code?

                                    Code:
                                     
                                    Declare Function ProcTxtAsLngArry Lib "C:\PBWin90\MySamples\MySample1.dll" (ByRef x&) As String
                                    Sub TestPassTxtAsLngArrs()
                                    Dim LongVarArr(0 To 4) As Long
                                    LongVarArr(0) = "xc"
                                    LongVarArr(1) = "SR"
                                    LongVarArr(2) = "DIR"
                                    LongVarArr(3) = "COO"
                                    LongVarArr(4) = "xc"
                                    Dim AnswStr As String
                                    AnswStr = ProcTxtAsLngArry(LongVarArr(0))
                                    MsgBox "AnswStr = " & AnswStr
                                    End Sub
                                    Thanks again for your help.

                                    Sincerely,

                                    Michael Fitzpatrick

                                    Comment


                                    • #19
                                      Michael, you are getting a type mismatch error because:

                                      Originally posted by Michael Fitzpatrick View Post

                                      Code:
                                       
                                      Dim LongVarArr(0 To 4) As Long [COLOR=red]'Array defined as Long Ingeger (a number)[/COLOR]
                                      LongVarArr(0) = "xc"                [COLOR=red]'Assignig a string to an integer[/COLOR]
                                      Bob was talking about packing strings into integers first (a fairly sophisticated programming process) so a simpler numeric array can get passed rather than a string array.

                                      =================================
                                      When your only tool is a hammer,
                                      everything looks like a nail.
                                      (FREDH - Salon's Table Talk)
                                      =================================
                                      It's a pretty day. I hope you enjoy it.

                                      Gösta

                                      JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                                      LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                                      Comment


                                      • #20
                                        Bob was talking about packing strings into integers first (a fairly sophisticated programming process
                                        It is?

                                        For a two byte string, X% = CVI(string)
                                        For any string up to four bytes, X& = CVL(LEFT$(string + SPACE$(4)), 4)

                                        (SPACE$() can be $NUL or anything else, just make sure CVL() gets an argument of at least four characters).

                                        To convert back to strings in called function, MKI$() or MKL$() can be used.
                                        Michael Mattias
                                        Tal Systems (retired)
                                        Port Washington WI USA
                                        [email protected]
                                        http://www.talsystems.com

                                        Comment

                                        Working...
                                        X