Announcement

Collapse
No announcement yet.

Null Pointer Problem

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

  • Michael Mattias
    replied
    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)

    Leave a comment:


  • Jeff Blakeney
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    >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.

    Leave a comment:


  • Bob Zale
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    >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

    Leave a comment:


  • Bob Zale
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    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?

    Leave a comment:


  • John Petty
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    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

    Leave a comment:


  • Cliff Nichols
    replied
    >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

    Leave a comment:


  • Michael Mattias
    replied
    >What ByRef??? I did not ByRef???? ORRRRRrrrrr Did I????

    You did:

    Code:
    OPTIONAL AxisNumber AS LONG
    When not specified, BYREF is assumed.

    Leave a comment:


  • Cliff Nichols
    replied
    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.

    Leave a comment:


  • Michael Mattias
    replied
    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

    Leave a comment:


  • Cliff Nichols
    started a topic Null Pointer Problem

    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?
Working...
X
😀
🥰
🤢
😎
😡
👍
👎