Announcement

Collapse
No announcement yet.

Null Pointer Problem

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

  • Null Pointer Problem

    I have a problem that I have FINALLY tracked down to being a Null pointer problem. Below is the "Meat and Potatoes" of where the problem is occurring in my real program, but does not occur in the example below.

    Code:
    #COMPILE EXE
    #DIM ALL
    #DEBUG ERROR ON
    #TOOLS ON
    #OPTIMIZE SIZE
    #INCLUDE "Win32Api.inc"
    FUNCTION PBMAIN () AS LONG
         LOCAL DeviceCommandsToParse AS STRING
         DIM DeviceCommands() AS STRING
         DIM DeviceParams() AS STRING
         DIM DeviceComments() AS STRING
         DIM DeviceBytes() AS LONG
         LOCAL ComputerSent AS LONG
         LOCAL CommandTyped AS LONG
         LOCAL AxisNumber AS LONG
    '     AxisNumber = 0
         ParseDeviceCommands "Hello", DeviceCommands(), DeviceParams(), DeviceComments(), DeviceBytes(), ComputerSent, CommandTyped, AxisNumber
    END FUNCTION
    
         FUNCTION ParseDeviceCommands ALIAS "ParseDeviceCommands"(DeviceCommandsToParse AS STRING, DeviceCommands() AS STRING, DeviceParams() AS STRING, DeviceComments() AS STRING, DeviceBytes() AS LONG, _
                                                                 OPTIONAL ComputerSent AS LONG, OPTIONAL CommandTyped AS LONG, OPTIONAL AxisNumber AS LONG) EXPORT AS LONG
              SELECT CASE AxisNumber
                   CASE 0                                 'No Motor Axis slipped in
                        MSGBOX "Device Reply" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                   CASE 1                                'Slipped in X
                        MSGBOX "Motor 1 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                   CASE 2                                'Slipped in Y
                        MSGBOX "Motor 2 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                   CASE 3                                'Slipped in Z
                        MSGBOX "Motor 3 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                   CASE 4                                'Slipped in T
                        MSGBOX "Motor 4 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
              END SELECT
              FUNCTION = %FALSE                            'No Error
         END FUNCTION
    Now using the same code in my much bigger program I find that "AxisNumber" has a Null Pointer error associated with it. (HUH??? I declared it as long, not a pointer)

    To test it, I set a VarPtr(AxisNumber) comes back as 0
    So what could I be inadvertently be doing to set a null pointer? Or for that matter how would I PURPOSELY set a Null Pointer????? (No idea why I would, but I could easily do it with a string, but this is a Long)

    I am at a loss of ideas of what to check that I could be accidentally be passing a Null pointer?
    Engineer's Motto: If it aint broke take it apart and fix it

    "If at 1st you don't succeed... call it version 1.0"

    "Half of Programming is coding"....."The other 90% is DEBUGGING"

    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

  • #2
    So what could I be inadvertently be doing to set a null pointer? Or for that matter how would I PURPOSELY set a Null Pointer?
    Pass BYVAL %NULL or don't pass the optional param at all to the function in the "AxisNUmber" parameter and you get a null pointer.

    Or, use PB.Win 8.0.0, which converted optional byref params to value zero when the underlying data item was zero. (fixed in 8.0.1)

    On the "programmer's oversight" front: Since those params are OPTIONAL, you really should check for null pointer or use ISMISSING before using the AxisNUmber variable in any executable statement.

    On the "I told you so" front: Willy-nilly use of "BYVAL" at the point of call has side effects!

    Speaking of point-of-call overrides....... now I have to deal with the fact that point-of-call BYREF override is broken in 9.0.1, except support told me, no, it's working correctly. (No, it is NOT).

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

    Comment


    • #3
      Found It

      Not passing an optional parameter in essence is a null pointer

      Yep I just looked it up and sure enough according to the docs
      If the parameter is defined as a BYREF parameter, VARPTR (Varname) will equal zero; when this is true, any attempt to use Varname in your code will result in a General Protection Fault or memory corruption. You should use the ISMISSING() function first to determine whether it is safe to access the parameter.
      Now the part about
      If the parameter is defined as a BYREF parameter
      What ByRef??? I did not ByRef???? ORRRRRrrrrr Did I????


      Taking a moment to think, and the ole dim bulb started glimmers and thought I read somewhere that the default is byref and sure enough I found

      Passing parameters
      By default, PowerBASIC passes parameters by reference: a 32-bit to the data. You can also pass most parameters by value, by declaring with the optional keyword. When a parameter is passed by value, the actual value of the parameter is pushed on the stack.
      Problem solved, dummy me did not pass an optional parameter. :doh


      Modified example below:
      Code:
      #COMPILE EXE
      #DIM ALL
      #DEBUG ERROR ON
      #TOOLS ON
      #OPTIMIZE SIZE
      #INCLUDE "Win32Api.inc"
      FUNCTION PBMAIN () AS LONG
           LOCAL DeviceCommandsToParse AS STRING
           DIM DeviceCommands() AS STRING
           DIM DeviceParams() AS STRING
           DIM DeviceComments() AS STRING
           DIM DeviceBytes() AS LONG
           LOCAL ComputerSent AS LONG
           LOCAL CommandTyped AS LONG
           LOCAL AxisNumber AS LONG
           ParseDeviceCommands "Hello", DeviceCommands(), DeviceParams(), DeviceComments(), DeviceBytes(), ComputerSent, CommandTyped        'Dont pass AxisNumber and you will get a Null Pointer error
      END FUNCTION
      
           FUNCTION ParseDeviceCommands ALIAS "ParseDeviceCommands"(DeviceCommandsToParse AS STRING, DeviceCommands() AS STRING, DeviceParams() AS STRING, DeviceComments() AS STRING, DeviceBytes() AS LONG, _
                                                                   OPTIONAL ComputerSent AS LONG, OPTIONAL CommandTyped AS LONG, OPTIONAL AxisNumber AS LONG) EXPORT AS LONG
                SELECT CASE ISMISSING(AxisNumber)
                     CASE %FALSE
                          SELECT CASE AxisNumber
                               CASE 0                                 'No Motor Axis slipped in
                                    MSGBOX "Device Reply" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                               CASE 1                                'Slipped in X
                                    MSGBOX "Motor 1 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                               CASE 2                                'Slipped in Y
                                    MSGBOX "Motor 2 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                               CASE 3                                'Slipped in Z
                                    MSGBOX "Motor 3 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                               CASE 4                                'Slipped in T
                                    MSGBOX "Motor 4 Position" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                          END SELECT
                     CASE <> %FALSE
                          MSGBOX "AxisNumber Missing and I would have raised a Null Pointer error (If #DEBUG ERROR ON)" + $CR + "VarPtr = " + STR$(VARPTR(AxisNumber))
                END SELECT
                FUNCTION = %FALSE                            'No Error
           END FUNCTION
      Oh and it looks like MCM pointed out the same thing while I was posting.....
      Last edited by Cliff Nichols; 2 Oct 2009, 02:48 PM.
      Engineer's Motto: If it aint broke take it apart and fix it

      "If at 1st you don't succeed... call it version 1.0"

      "Half of Programming is coding"....."The other 90% is DEBUGGING"

      "Document my code????" .... "WHYYY??? do you think they call it CODE? "

      Comment


      • #4
        >What ByRef??? I did not ByRef???? ORRRRRrrrrr Did I????

        You did:

        Code:
        OPTIONAL AxisNumber AS LONG
        When not specified, BYREF is assumed.
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          >What ByRef??? I did not ByRef???? ORRRRRrrrrr Did I????

          You did:

          Code:
          OPTIONAL AxisNumber AS LONG
          When not specified, BYREF is assumed.
          I said that didn't I???


          point-of-call BYREF override
          now what the heck is that? or do you mean
          Code:
          #COMPILE EXE
          #DIM ALL
          #DEBUG ERROR ON
          #TOOLS ON
          #OPTIMIZE SIZE
          #INCLUDE "Win32Api.inc"
          FUNCTION PBMAIN () AS LONG
               LOCAL MyOverRide AS LONG
               MyOverRide = 12
               OverRideMyFunction BYREF MyOverRide               'Pass a reference
          END FUNCTION
          
               FUNCTION OverRideMyFunction(BYVAL X AS LONG) AS LONG        'But I wanted a Value, not a reference
                    MSGBOX STR$(X)
                    FUNCTION = %FALSE                            'No Error
               END FUNCTION
          Engineer's Motto: If it aint broke take it apart and fix it

          "If at 1st you don't succeed... call it version 1.0"

          "Half of Programming is coding"....."The other 90% is DEBUGGING"

          "Document my code????" .... "WHYYY??? do you think they call it CODE? "

          Comment


          • #6
            Well, yes you did say that and posting by more than one party at a time works both ways.

            And yes, I mean passing a reference.

            Oh, you wanted a code example?

            OK...here is the compilable (or non-compilable) example I submitted to support:
            Code:
            ' elapsed_time.bas
            ' BUG: Compiler not accepting BYREF override with parameter mismatch error. It Should; it should not even think about
            ' a parameter mismatch when point-of-call override (BYREF) is used. Just pass the address of the variable!
            ' PB.Win  9.0.1
            #COMPILE EXE
            #DIM ALL
            
            
            #INCLUDE "WIN32API.INC"
            
            %ONE_FILETIME_DAY    =   600000000 * 60 * 24
            %ONE_FT_DAY          =   864000000000&&   ' 864,000,000,000
            
            %ONE_FILETIME_HOUR   =   %ONE_FILETIME_DAY \ 24
            %ONE_FILeTIME_MINUTE =   %ONE_FILETIME_HOUR \ 60
            %ONE_FILETIME_SECOND =   %ONE_FILETIME_MINUTE \ 60
            
            
            FUNCTION PBMAIN () AS LONG
            
                LOCAL StStart AS SYSTEMTIME, STEnd AS SYSTEMTIME
            
                CALL TestVUint64()
                EXIT FUNCTION
            
            
                STStart.wYear   =   2009
                StStart.WMonth  =   9
                StStart.wDay    =  29
                STStart.wHour   =    16
                STStart.WMinute    = 45
                STStart.WSecond    =  0
            
                GetLocalTime  STEnd
            
                MSGBOX "Elapsed=" &  ElapsedTimeHHMMSS(StStart, StEnd)
            
            
            
            END FUNCTION
            
            
            
            
            FUNCTION ElapsedTimeHHMMSS (StStart AS SYSTEMTIME,StEnd AS SYSTEMTIME) AS STRING
            
            
               LOCAL QStart AS QUAD, QEnd AS QUAD, QDiff AS QUAD
               LOCAL lDiff  AS LONG
               LOCAL nHour, NMinute, nSec AS LONG
            
             ' COMPILER BUG HERE!!!!!!
            
              ' SystemTimeToFileTime   StStart, BYREF QStart  ' compiler is not accepting these it should!
              ' SystemTimeToFileTime   StEnd,   BYREF QEnd
            
               SystemTimeToFileTime   StStart, BYVAL VARPTR (QStart)  '
               SystemTimeToFileTime   StEnd,   BYVAL VARPTR (QEnd)
            
            
               QDiff  = QEnd - QStart
               lDiff =  QDiff \ %ONE_FILETIME_SECOND   ' difference in seconds, rounded
            
               nHour =  lDiff \ 3600
               lDiff = lDiff MOD 3600
            
               nMinute = lDiff \ 60
               lDiff   =  lDiff MOD 60
            
               nSec    =  lDiff
            
               FUNCTION = FORMAT$(nHour, "00") & ":" & FORMAT$(nMinute, "00") & ":" & FORMAT$(nSec, "00")
            END FUNCTION
            
            FUNCTION TestVUint64() AS LONG
              LOCAL pV  AS VARIANTAPI PTR, v AS VARIANT
            
              LOCAL Q AS QUAD, pQ AS QUAD PTR
            
              LET V = EMPTY   '  intialize
              PV    = VARPTR (V) ' pointer
            
              @pV.vt  = %VT_UI8    ' unint 74
              pQ      = VARPTR (@pV.VD)
              Q       = 12345&&
              @pq     = Q
            
            
              ' get variant#() value from intrinsic function
            
              Q  =  VARIANT#(V)
              MSGBOX FORMAT$(Q),, "With value +12345"
            ' hmm, try with bit 63 set...
              LET Q = -12345&&
              @pq   = Q
              Q     = VARIANT#(V)
              MSGBOX FORMAT$(Q),, "With value -12345"
            
            
            
            END FUNCTION
            It's type checking even though I assumed full responsibility for my actions when I typed "B-Y-R-E-F" at the point of call.

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

            Comment


            • #7
              I suspect the problem is that the function is defined in WIN32 as using two parameters that are both defined UDT's i.e. SystemTime and FileTime so the compiler is rejecting you use of a QUAD instead of a FileTime.
              Change that and it compiles fine.
              Actually the manual is quite clear on the subject "When a Function definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity."
              Thus your second method works with a QUAD.
              Last edited by John Petty; 3 Oct 2009, 08:13 AM.

              Comment


              • #8
                If BYVAL at point-of-call is an override, why isn't BYREF?

                If type-checking is skipped when BYVAL or BYCOPY is used, why not when BYREF is used? (you can use 'BYCOPY dynamic_string_var' for a procedure defined with an ASCIIZ in that parameter position)

                If BYREF at point of call does nothing, why does it exist?

                Why do BYREF, BYVAL and BYCOPY appear in the documentation as equals?

                If I can freely use 'BYVAL VARPTR(variable)', why can't I use 'BYREF variable' just as freely?
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  ByVal and ByRef in a function call serve two purposes:

                  1- They may be included in a function reference to aid in self-documentation.

                  2- ByVal may be used to override an appropriate ByRef parameter. ByRef may be used to override an appropriate ByVal parameter. Neither of them may be used to allow the use of a mismatched parameter type. That is, you may not use "ByVal x$" as an override for the parameter "ByVal param&", nor can you use "ByRef a&" as an override for "ByVal abc as UDT". To do so would invite Armageddon.

                  Bob Zale
                  PowerBASIC Inc.

                  Comment


                  • #10
                    >ByVal may be used to override an appropriate ByRef parameter.

                    Well, let's define "appropriate" and "mismatched," shall we?

                    "Appropriate," of course, is subjective: therefore there is no "one correct" answer.

                    I am using "BYVAL integer" as an override for "[BYREF] a_certain_udt". That sure sounds like a 'mismatch' to me. But apparently 'someone' considers it 'appropriate' because not only did the compiler accept my syntax, it worked as expected!

                    >ByRef may be used to override an appropriate ByVal parameter.

                    That limitation - that BYREF may be used [only?] to override a BYVAL parameter, appropriate or not - does not appear in the documentation for CALL.

                    And looking at my example... a POC-override attempting to pass the address of a quad integer when the function is defined to accept the address of a UDT - why such a restriction is in place is truly a mystery.

                    > nor can you use "ByRef a&" as an override for "ByVal abc as UDT". To do so
                    > would invite Armageddon.

                    Well, that surely avoids the 'subjectiveness' in the meaning of word 'appropriate:' it is one hundred percent patronizing.

                    How very 'Microsoft' of you: "We put the POWER into PowerBASIC... if we think you can handle it."

                    I would not be such a PITA re this if only the PB support dept had responded to my 'bug report' with something like... "The documentation for the use of BYVAL, BYREF and BYCOPY at the point of call is missing certain restrictions [itemize same here] and will be corrected in the next release of the help file," instead of what I got...
                    This is because the declare statement specifies a UDT and you are
                    passing a Quad in it's place. Either pass the UDT or pass a ByVal
                    pointer to the Quad. Another option would be to modify the declare
                    statement to use a Quad instead of a UDT.
                    Um, gee... had I been aware of the undocumented restrictions, maybe I could have figured that out myself instead of reading how I could solve my problem - That is, READING THE EXACT WORKAROUND I HAD INCLUDED IN THE CODE SUBMITTED WITH THAT REPORT.

                    Here I had plainly already solved my problem, yet I took the time to slice this specific code out of a much larger application to create a 'compilable [or not] example of the problem' and send it it. Maybe I should just solve my problems and keep those solutions to myself, not bothering to report what I find so you can avoid customer confusion in the future.

                    But if that's the way it's designed, then that's the way it's designed.

                    I shall resubmit as a new feature suggestion. That NFS shall more than likely include the phrase 'treat me like a grownup' somewhere.

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

                    Comment


                    • #11
                      Isn't it strange? I actually thought you might appreciate an accurate answer to your problem. I guess I was mistaken.

                      Best regards,

                      Bob Zale
                      PowerBASIC Inc.

                      Comment


                      • #12
                        >might appreciate an accurate answer to your problem. I guess I was mistaken

                        Au contraire, mon ami.

                        I truly appreciate knowing the score here.

                        Not that I LIKE the score, but knowing the score beats the snot out of not knowing.
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          In the documentation from PBWIN v9.01 for the CALL statement I find the following sections:

                          When you pass parameters from the calling code with an explicit BYVAL, you effectively switch off the compilers type-checking for that parameter. This can be useful in cases where the called code is expecting a BYREF parameter, and you wish to pass an address of another data type that would trigger a compile-time error without the BYVAL method. For example:

                          SUB TheSub(x AS ASCIIZ) ' Address of x expected
                          ...
                          DIM a$
                          a$ = "Dynamic string data"
                          CALL TheSub(BYVAL STRPTR(a$)) ' Pass data address
                          If a procedure expects a parameter by reference, it is possible to substitute a pointer by value, for the identical result. This is particularly useful with Fixed-length strings and Types:

                          DECLARE SUB a(z%)
                          DIM MyInt AS INTEGER, x AS INTEGER PTR
                          x = VARPTR(MyInt)
                          CALL a(MyInt)
                          ' or
                          CALL a(BYVAL x)
                          ' or
                          CALL a(BYVAL VARPTR(MyInt))

                          Of course, if the procedure is expecting a parameter by value, you may not pass the pointer, but rather the pointer target (i.e., CALL a(@x)).
                          When a procedure definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity. It is necessary to explicitly declare all pointer parameters as BYVAL (i.e., BYVAL X AS BYTE PTR). Failure to do so will generate a compile-time Error 549 ("BYVAL required with pointers").
                          Nowhere do I see any mention that you can over ride a BYREF with a BYREF. This seems to be an assumption on your part, Michael, as it seems that you didn't read it in the documentation. If it isn't in the documentation, then you can't assume it is going to work and you certainly shouldn't complain about it not working in that case either. All the BYVAL options that have been discussed in this thread are all listed in the documentation and quoted above which is why you can use them. Using BYREF QStart acts exactly as I would expect it to just as BYVAL VARPTR(QStart) works as I would expect it to according to the documentation.

                          If you want things to work differently, then a new feature suggestion would be the way to go and hope that others see the need for that functionality too.
                          Jeff Blakeney

                          Comment


                          • #14
                            Code:
                            MACRO RealByRef(anything) = BYVAL VARPTR (anything) 
                            ...
                               CALL foo  ( realbyref (x).....
                            (I actually use these when I remember I have them):
                            Code:
                            MACRO bva(anything)      = BYVAL VARPTR(anything)
                            MACRO bvaz(asciizString) = BYVAL IIF(lstrlen(asciizString), VARPTR(AsCIIZString), %NULL)
                            Michael Mattias
                            Tal Systems (retired)
                            Port Washington WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment

                            Working...
                            X