Announcement

Collapse
No announcement yet.

Help in determining size of passed ASCIIZ parameter

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

  • Help in determining size of passed ASCIIZ parameter

    Hi All, given the code below, I have a generic routine to read parameters that are of varying lengths and all form part of a UDT. Because they are of varying length, the Get_Code function parameter is defined without a length and so the last character in this example corrupts my period.

    Is there any way without using an interim assign or extra parameter that the function can determine the size of the passed parameter? I suspect not but ask anyway...

    Code:
    #COMPILE EXE
    #DIM ALL
    
    TYPE TabDataType
      Office_Code AS ASCIIZ * 9
      Currency_Code AS ASCIIZ * 5
      Period AS LONG
    END TYPE
    
    
    FUNCTION Get_Code(zCode AS ASCIIZ) AS LONG
      zCode = "<ANY>"
    END FUNCTION
      
    FUNCTION PBMAIN () AS LONG
      STATIC TabData AS TabDataType
      STATIC AnyString AS STRING
      TabData.Period = 200910
      TabData.Office_Code = "1234567890"
      TabData.Currency_Code = "<ANY>"
      
      AnyString = "Pass 1:Currency = "+ TabData.Currency_Code+" Period="+FORMAT$(TabData.Period)
      Get_Code(TabData.Currency_Code)
      AnyString = AnyString+$CRLF+"Pass 2:Currency = "+ TabData.Currency_Code+" Period="+FORMAT$(TabData.Period)
      
      MSGBOX AnyString,,"Content of AnyString"
    
    END FUNCTION
    Thanks - Carlo

  • #2
    field big enough?

    Hi Carlo!

    The Office_Code field of TabDataType is Asciiz*9 which is only big enough to hold 8 characters. There are 10 in this string...

    TabData.Office_Code = "1234567890"

    And this >> "<ANY>" -- won't fit in an Asciiz*5 string either.

    ???

    Fred
    Last edited by Fred Harris; 27 Oct 2009, 04:31 PM.
    Fred
    "fharris"+Chr$(64)+"evenlink"+Chr$(46)+"com"

    Comment


    • #3
      Hi Fred

      That's not my issue (was typing junk). If you compile, you'll see the bug I created for myself in the value of the period.

      Cheers

      Comment


      • #4
        If you really want to measure how much data was passed in an ambiguous-length ("AS ASCIIZ") parameter, about all you can do is inspect the characters one by one until either the charcter is NUL or the address is invalid ( IsBadReadPtr).

        A function like lstrLen() will fail because it can hit the end of owned memory without having encountered a NUL.

        But if you want to handle variable-length strings, "AS STRING" might be a better choice than "AS ASCIIZ".


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

        Comment


        • #5
          Originally posted by Michael Mattias View Post
          about all you can do is inspect the characters one by one until either the charcter is NUL or the address is invalid ( IsBadReadPtr).
          ....after reading this: http://www.powerbasic.com/support/pb...t=IsBadReadPtr

          Dunno about variable length strings in UDTs, I think you have to use fixed-length ones.

          Carlo, are you creating this data, or are you importing it from elsewhere?

          ** edit - actually isbadreadptr will be no help at all, the data are probably inside valid memory, the problem is just field overflow within the UDT instance.

          Comment


          • #6
            I'm not understanding something here, Carlo. You are sending a 4 character string ("<ANY" & chr$(0)) and returning a 5 char string ("<ANY>" & chr$(0)). How is GetCode supposed to do anything else?
            '
            Code:
            #Compile Exe
            #Dim All
            Type TabDataType
              Office_Code As Asciiz * 9
              Currency_Code As Asciiz * 5
              Period As Long
            End Type
             
            Function Get_Code(zCode As Asciiz) As Long
              zCode = "<ANY>"
            End Function
             
            Function PBMain () As Long
              Static TabData As TabDataType
              Static AnyString As String
              TabData.Period = 200910
              TabData.Office_Code = "1234567890"
              TabData.Currency_Code = "<ANY>"
             
              AnyString = "Pass 1:Currency = "+ TabData.Currency_Code + " Period=" + Format$(TabData.Period) & _
                          Using$(" len tbcc = # ", Len(TabData.Currency_Code))
              Get_Code(TabData.Currency_Code)
              AnyString = AnyString+$CrLf+ _
                         "Pass 2:Currency = " + TabData.Currency_Code + " Period=" + Format$(TabData.Period) & _
                          Using$(" len tbcc = # ", Len(TabData.Currency_Code))
             
              MsgBox AnyString,,"Content of AnyString"
            End Function
            '
            ============================
            "Just living is not enough.
            One must have sunshine,
            freedom,
            and a little flower."
            Hans Christian Anderson
            ============================
            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


            • #7
              Hi Gösta

              Exactly... This code is to simulate a bug I had. I thought the complier would have known that TabData.Currency_Code could only contain 4 chrs plus a Null, but then if all the function is getting is a pointer to some place in memory rather than being told that it should be ASCIIZ * 5, I deduced the compiler cannot stop at 4 characters <ANY as it does when you do a simple Var = "<ANY>".

              The point is you run the program is that the ">" overwrites the Period so the period changes 200910 to 200704 which, as it happens, gave me a few more grey hairs

              Comment


              • #8
                There is no reliable way to detect the size of a buffer address on it's own.

                In the following example, two alt. methods are shown: pass the size to the function (function A) or let the compiler enforce the size (function B)

                Code:
                #Compile Exe
                #Dim All
                 
                Function Get_CodeA(zCode As Asciiz, ByVal dwSize As Dword) As Long
                  zCode = Left$("<ANY>", dwSize-1)
                End Function
                 
                Function Get_CodeB(zCode As Asciiz * 5) As Long
                  zCode = "<ANY>"
                End Function
                 
                Function PBMain () As Long
                 
                  Local z As Asciiz * 5
                 
                  Get_CodeA(z, SizeOf(z))
                  ? "A: " + z
                 
                  Get_CodeB(z)
                  ? "B: " + z
                 
                End Function
                kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                Comment


                • #9
                  Thanks Kev and all...

                  @Kev - That's the conclusion I came to, but it helps to be affirmed before starting a massive "Search and Update" exercise.

                  Comment


                  • #10
                    ** edit - actually isbadreadptr will be no help at all, the data are probably inside valid memory, the problem is just field overflow within the UDT instance
                    In this case, that's not a problem "if" you are managing the contents of the UDT members using only assignment statements ... at least if you are using any version of PB/Win greater than 6.0.

                    When you assign a string or string expresssion to an ASCIIZ target, the compiler will never write more than SIZEOF(target)-1 characters of the source string/string expression, always reserving the last byte of the target for the required $NUL.

                    If you are managing your UDT data via copymemory() or direct pointer moves which cannot be reduced to an assignment or as part of a UNION, all bets are off (as they should be!)

                    (Prior to 6.10, this was not true. Been there, done that, reported the bug.)

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

                    Comment


                    • #11
                      @MCM

                      Are you saying this code would have worked in Version 6? My initial thoughts where that the compiler should know that I am passing TabData.Currency_Code which is ASCIIZ*5 so why overwrite the next element in the UDT. Then I concluded it is only passing a pointer to TabData.Currency_Code, so how can the function know the length?

                      Comment


                      • #12
                        I don't see the problem if you are writing the data. If you want to know the length of the string then you can determine it by populating the UDT along the following lines:

                        Code:
                        #compile exe
                        #dim all
                        type test
                                a as asciz * 4
                                b as integer
                        end type
                        
                        function pbmain () as long
                            local ttest as test
                            
                             ttest.a = "ttttt"
                             ttest.b = 0
                            ? varptr (ttest.a), varptr (ttest.b)
                            ? "length of string should not exceed", _
                                varptr (ttest.b) - varptr (ttest.a) -1
                            waitkey$
                        end function

                        Comment


                        • #13
                          Code:
                          FUNCTION Get_Code(zCode AS ASCIIZ) AS LONG
                            zCode = "<ANY>"
                          END FUNCTION
                          When you do this...
                          Code:
                           Get_Code(TabData.Currency_Code)
                          .. which is an "ASCIIZ * 5" variable, you are passing 4 chars plus a nul

                          When you write to it..
                          Code:
                          zCode = "<ANY>"
                          .. you are writing SIX characters (five plus $nul) , meaning you are corrupting your data by overrunning the target field, meaning everything you do after that is pot-luck.

                          Or maybe you are writing FIVE characters, the compiler assuming 'zCode' is initialized to $NUL... but even then you are corrupting your data, since you still are overwriting the REQUIRED $NULL at the end of the string.

                          If you want to use ambiguous-length ASCIIZ or ASCIIZ PTR variables, you are 100% responsible for ensuring the target variable is of a sufficient size to hold whatever you are writing.

                          This is a PB-compliler-version-independent Error 1 (Programmer Error).

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

                          Comment


                          • #14
                            In the PBWin help file for ASCIIZ string, it doesn't say what happens when you define a parameter in a procedure as being ASCIIZ but with no maximum length specified. As ASCIIZ strings have a maximum length (as opposed to a fixed length), I take it PB decided to allow this sort of thing so that you could use the same function with ASCIIZ strings that have different maximum lengths but are leaving it up to the programmer to make sure they don't write past the end of the string passed in. I believe this would usually be done by defining the procedure like this:

                            Code:
                            FUNCTION Get_Code(zCode AS ASCIIZ, lSize AS LONG) AS LONG
                            and calling it like this:

                            Code:
                            Get_Code(TabData.Currency_Code, SIZEOF(TabData.Currency_Code))
                            And just to clarify something,

                            Originally posted by Carlo Pagani View Post
                            The point is you run the program is that the ">" overwrites the Period so the period changes 200910 to 200704 which, as it happens, gave me a few more grey hairs
                            The ">" is not messing up the Period member of the UDT, it is the null being put at the end of the string overwriting the least significant byte of Period. 200910 = &H000310CE and 200704 = &H00031000. Notice that the last byte, &HCE, has been replaced with null, &H00.
                            Jeff Blakeney

                            Comment


                            • #15
                              it doesn't say what happens when you define a parameter in a procedure as being ASCIIZ but with no maximum length specified
                              Lots of stuff unsaid in the help but that is not important right now..

                              What it is, is, "Address of a buffer" .. same as when you use " sz AS ASCIIZ * somevalue"

                              The difference is, when you state the length with " * somevalue", the compiler will limit how much data will be read or written; when you don't use "* somevalue" it won't... it just follows your instructions, reading until it hits a $NUL (or GPFs because it did not find one until it exceeded the owned memory), or writing WHATEVER YOU TELL IT and appending a $NUL... or GPFing because you tried to write into memory you don't own.

                              Btw in this example? Overwriting that UDT is not going to get you a GPF, because that is stored on the stack, and by definition you own the stack. Unfortunately, you are probably already using the area of the stack where your overrun is writing... for other variables. Oops. You have variables changing value mysteriously? Well, this is the easy way to do that.

                              As I said... when you use ambiguous-length ASCIIZ variables, you are in charge - and responsible. Great tool - once you learn how to use it.

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

                              Comment

                              Working...
                              X