Announcement

Collapse
No announcement yet.

VARPTR problem

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

  • Daniel Corbier
    replied
    Tom, I did some further testing, and I see that my statement
    about VB/PB string parameters was incorrect. I had done a lot of
    testing, but I must have compared the wrong results together.
    I was about to post an example, but then I found from testing,
    and from the documentation, that an un-initialized VB string is
    treated as an empty string from within VB. However, when you pass
    an un-initialized string to an outside function which takes fixed-
    length strings, then it's passed as a NULL, just like
    vbNullString. So PB was receiving the info just the way VB
    sent it, and what VB was sending was consistent with the way
    they documented it. So I learned that there's a subtle difference
    in VB between an un-initialized string and an empty string.

    I shouldn't have included VB in the discussion. My original
    concern in this thread was about why within PB an empty literal
    string was received as a NULL from another PB routine. I still
    would like to see PB pass a literal empty string, "", as a valid
    non-null pointer to a routine that expects an ASCIIZ argument
    (from within PB). This would be consistent with the way PB correctly
    handles incoming literal empty strings from elsewhere, as well as
    how other compilers I've tested handle such strings from within.

    Notice, that I'm particularly speaking about a literal, "", string
    argument as opposed to a dynamic string variable. (PB doesn't
    even let you pass a dynamic string variable directly when an ASCIIZ
    argument is expected. It requires you to place BYCOPY if the argument
    is a dynamic string. And in this case also, it wouldn't hurt to
    pass the BYCOPYed pointer to a non-zero address for empty string
    variables).


    Lance, concerning global error handling, I don't necessarily have
    a specific idea in mind. I'm not even sure if global error
    handler
    is the best term to describe what I want. There are
    several alternatives that would be easier than the current
    On Error GoTo. One straight-forward possibility is something
    like what VB does (and even PB/DOS if I remember correctly).

    When you click on "Run" from the IDE, it would attempt to run
    the program to completion, but it would pause if ERR is set, and
    place the cursor at the most recently executed line of code.
    I have alternative code inside #IF/#ELSE blocks which allows me
    to run a simple "host" program from PB/CC which uses the DLL code.
    So that part wouldn't be a problem.

    In the particular situation I was in, this would have allowed me
    to quickly rule out a lot of code that I had to sequentially step
    into, or surround with On Error GoTo (which in my case
    modified the flow of my program to cause another error on top of
    the one I was looking for. And the place where I concentrated on
    placing a lot of error handling code was nowhere near the source of
    the error, which was yet somewhere else from where it was crashing).

    I would also like support for TRY/CATCH as well, but for a
    different reason. The global error handler would be as a kind
    of scaffold, mostly of use in the development stage. When the
    project is nearly completed the programmer would remove it,
    since it would no longer be needed. But the TRY/CATCH method
    would be of use, optionally, in a finished program.

    Interestingly, I use Try/Except in the Delphi sample program that
    calls my PB DLL (you can do it with C++ also). PB does not catch
    things like numeric overflow, whereas the Delphi host program that
    calls my DLL catches them. So it would seem like something that
    shouldn't be too difficult to add. It's probably like an
    extension of On Error GoTo, except it can handle Overflow and
    others, and the Try/Catch syntax would be familiar to people who
    are using other compilers.


    ------------------
    Daniel Corbier
    UCalc Fast Math Parser
    http://www.ucalc.com

    Leave a comment:


  • Tom Hanlin
    replied
    Anyway, I spent today looking into this some more, and based on
    what I read in the MSDN, I see that PB is interpreting VB zero-
    length strings incorrectly. A zero-length VB string has a valid
    non-null address. When a VB program needs to pass an actual null
    string to an API as opposed to "", this is done by passing
    vbNullString. However, when "" is passed from VB to PB's ASCIIZ
    argument, the "" is incorrectly interpreted the same as
    vbNullString.
    No, it's not. When VB converts an empty string to ASCIIZ, it does so by creating
    a string consisting only of a NUL terminator and passing that. This is fine with
    PowerBASIC. Your test program was for PowerBASIC, not VB, and PowerBASIC converts
    dynamic strings to ASCIIZ differently. The preferred solution, in PB, is to pass
    an actual ASCIIZ when a function expects an ASCIIZ. Otherwise, you may end up with
    a null pointer, which is what an empty dynamic string looks like.


    ------------------
    Tom Hanlin
    PowerBASIC Staff

    Leave a comment:


  • Florent Heyworth
    replied
    #2 Global Error trap

    I'm not a fan of the global error trap approach. A better
    approach would be to settle for SEH (Structured Exception
    Handling) with TRY, EXCEPT, etc constructs.

    This would have enabled you to trap the gpf and take
    appropriate action.


    Cheers

    Florent


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

    Leave a comment:


  • Lance Edmonds
    replied
    1. vbNullString... I'll have to ask R&D about that one.

    2. Global error trap... Personally I don't see how that particular (although unusual) situation could justify the addition of a global error trapping. Since the effect was a crash or GPF, how would a global error trap actually tell you that you acidentally created a recursive call any more than a local error trap would? Analysing the call stack would be much more effective, and you can do this within the sub/function code quite simply.

    3. ERR in the debugger... Long since confirmed as a bug.

    4. The IDE GPF has also been reported on this BBS a few times.

    #3 and #4 are expected to be corrected in the next update...



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

    Leave a comment:


  • Daniel Corbier
    replied
    Charles, I gave your idea a quick try, and it seems like a
    possible alternative as well. You're right, I don't see why
    Semen's method worked that way, while the other method which is
    similar did not. Both should yield a similar result. But I'm
    thankful that R&D will look into it.

    Anyway, I spent today looking into this some more, and based on
    what I read in the MSDN, I see that PB is interpreting VB zero-
    length strings incorrectly. A zero-length VB string has a valid
    non-null address. When a VB program needs to pass an actual null
    string to an API as opposed to "", this is done by passing
    vbNullString. However, when "" is passed from VB to PB's ASCIIZ
    argument, the "" is incorrectly interpreted the same as
    vbNullString. (Looking back I see that Eric's original
    message started explaining how things should work. But then he
    tried it in PB and ...

    I tested zero-length strings with C++ and Delphi, and I saw
    that in those compilers the pointer to a zero-length string ("")
    is not 0. The string pointer has a valid address, and the byte
    at that location is chr$(0). Furthermore, when I pass a "" string
    from VB to a Delphi DLL function which takes a null-terminated
    string argument (PChar), the pointer has a valid non-zero address.
    So it seems like PB is actually doing an extra step to convert
    the valid zero-length string it receives into a zero-address pointer.

    So I'd like to request that PB not convert zero length ASCIIZ
    arguments to NULL (whether it's from a call within PB or from VB).
    My guess is that it was done this way to match VB 3.0, which did
    not yet have a direct way of distinguishing a zero-length string
    from a null-string. Other than matching VB 3.0, I can see no
    other practical reason for not separating the two.

    After testing things, it turns out that all of this may have had
    nothing to do with the original GPF. Apparently ERR 9 in this
    context only masked a different problem. ERR 9, which was
    triggered at the ASCIIZ line diverted the code to the "On Error"
    label, skipping necessary code, and crashing. "IF VARPTR" did not
    do any better, since it set ERR 9 just the same. Later, I was
    not satisfied, to see the error mysteriously disappear when I
    disabled "Debug Error", and removed the "IF VARPTR" line. So I
    checked source code from a few weeks back, and realized that I
    in the process of debugging, I had also fixed some code which
    was calling itself recursively and running out of stack. This
    only goes to show how a global error handler would be more
    accurate in tracking such tricky errors (instead of manually
    inserting error handlers in functions where you think the problems
    might be located).

    I have another problem. A global error-handling routine is ideal.
    However, I've noticed that ERR is listed in the debugger watch
    window. I'd settle with that for now, however, no matter what I
    do to induce an error, ERR always displays 0. Am I using it
    the wrong way, or is it a bug? Also, it seems like bad pointer
    references aren't caught by On Error Goto at all (it just crashes).

    Here's a problem of a different nature that I noticed just
    recently in the IDE. If I load a long program, and as soon
    as it's loaded, I scroll all the way to the bottom with the
    mouse, using the scroll-bar on the right, and then I hold
    my hand on PgUp, to scroll back up, it crashes the IDE.


    ------------------
    Daniel Corbier
    UCalc Fast Math Parser
    http://www.ucalc.com

    Leave a comment:


  • Lance Edmonds
    replied
    No, it is you who have misread my message. The CHR$(0) comment comes from Steve's message, and the "<=0" comments stem from your advice.

    I find your comments about the importance of having more than 2G of physical RAM interesting. In truth, physical RAM is not a factor in these discussions. Remember, some Win32 platforms can actually provide up to 3Gb of virtual memory for a given process.

    However, my point was that you have to be careful when making assumptions about signed/unsigned comparisons. In this context, any advice that suggests checking "<=0" to determine if a pointer is invalid is flawed advice. Sorry!

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

    Leave a comment:


  • Guest's Avatar
    Guest replied
    Lance, you should re-read my message. I suggested checking the ascii value of the asciiz string and did not mention checking for chr$(0). Also, checking to see if the pointer (casted to a long) is <=0 will work fine unless you have more than 2gb ram. Of course, <> 0 will work in all cases and I should have stated that instead of <= 0.

    DON'T assign a pointer to a long integer if you have > 2GB ram. If you look at Semen's message - he is suggesting checking the value of a pointer - not a long. At any rate a pointer with a value of 0 is a null pointer.

    If the ascii value of a string = -1 then it is an empty string. I had thought it would be 0 for an asciiz string without testing this. Asciiz and dynamic strings both report -1 for the ascii value if the string is empty.






    Leave a comment:


  • Charles Dietz
    replied
    Daniel,

    It seems to me that your problem occured because the parameter you passed
    did not agree with what was expected in the sub. You passed a null string
    and the sub expected a string ending with chr$(0). Therefore, you must
    call test("" + chr$(0)). And the SUB should test with LEN, not VARPTR.

    So, your code should be:

    Code:
    $COMPILE EXE
    $DEBUG ERROR ON
    SUB Test(AsciizArg AS ASCIIZ)
       IF LEN(AsciizArg) = 0 THEN Temp$ = "" ELSE Temp$ = AsciizArg
       MSGBOX STR$(ERR) 'Returns 0 now
    END SUB
    FUNCTION PBMAIN AS LONG
       CALL Test("" + CHR$(0))
    END FUNCTION
    I don't really know why Semen's solution seemed to work.


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

    Leave a comment:


  • Lance Edmonds
    replied
    IMHO, these are mainly programming issues, not compiler issues. FOr example, if you are writing a commercial DLL and you do not adequately test the pointer being received from someone else's program (which is completely beyond your control), you are asking for problems. If your code GPF's, then you did NOT test the pointer adequately. Afterall, it is not up to PowerBASIC to decide what is and what is not a valid pointer in this circumstance...

    However, I'll still pass your comments along to R&D... Thanks!


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

    Leave a comment:


  • Daniel Corbier
    replied
    Eric summed it up here... In Daniels original code, VARPTR returns zero, and for good measure, ERR is set to reflect that a null string was passed.

    Where is the problem?
    There are two problems. 1. GPFs. 2. Complications in error
    handling.

    When I was getting the GPF, I added debug code gradually until it
    lead me to that line.

    Even in the absence of a GPF error, when ERR is set unnecessarily,
    it disrupts the program flow. ON ERROR GOTO jumps to a label, but
    I can't jump back automatically.

    WISHLIST Item: Please add support for RESUME NEXT.

    If passing a null ASCIIZ string is illegal, then it should be
    clearly documented. If it's not illegal, then ERR should not be
    set. This only gets in the way.

    Also, bugs which lead to error 9 typically lead to GPFs. That's
    why I suggest that R&D check to see whether whatever's triggering
    ERR 9 in this context might not also be corrupting memory causing
    a GPF. The problem might originate from elsewhere, but I don't
    think it hurts to double check.

    ------------------
    Daniel Corbier
    UCalc Fast Math Parser
    http://www.ucalc.com

    Leave a comment:


  • Lance Edmonds
    replied
    Steve and Ron's advice is incorrect I'm afraid... the terminating byte in an ASCIIZ string is not "part" of the string as far as the string engine is concerned, so a test like:
    Code:
    If left$(asciizString,1) = chr$(0) Then...
    will never be satisfied!!!

    To reiterate, if the function is expecting an ASCIIZ, and you pass BYVAL 0& or just "", then VARPTR in the function will return 0... the invalid pointer is identified clearly.

    Finally, regarding the VARPTR result - Ron mentions testing for "<=0", however, if you assign the VARPTR to a signed (LONG) integer, the test for an invalid pointer will fail INCORRECTLY, since a negative value is still a valid 32-bit address (just interpreted as a signed value).


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

    Leave a comment:


  • Guest's Avatar
    Guest replied
    Daniel, I would go with what Semen suggested (pass the address of the asciiz string and check if it is <=0)
    Or similarly to what Steve suggested - just check the ascii value of the asciiz string if you pass the string.

    Leave a comment:


  • Fred Oxenby
    replied
    Originally posted by Daniel Corbier:
    Guys thanks for the responses.
    Fred, I'm not familiar with IsBadStringPtr(). I didn't see
    it in the help file. I searched and found it in the API, but
    with no explanation of how it works.
    Parameters
    lpsz [in] Pointer to a null-terminated string, either Unicode or ASCII.
    ucchMax [in] Specifies the maximum size, in TCHARs, of the string. The function checks for read access
    in all bytes up to the string's terminating null character or up to the number of bytes specified by this parameter,
    whichever is smaller. If this parameter is zero, the return value is zero.

    Return Values
    If the calling process has read access to all characters up to the string's terminating null character
    or up to the number of characters specified by ucchMax, the return value is zero.

    If the calling process does not have read access to all characters up to the string's terminating null character
    or up to the number of characters specified by ucchMax, the return value is nonzero.

    If the application is compiled as a debugging version, and the process does not have read access to the entire memory range specified,
    the function causes an assertion and breaks into the debugger. Leaving the debugger, the function continues as usual,
    and returns a nonzero value This behavior is by design, as a debugging aid.

    Remarks
    This function is typically used when working with pointers returned from third-party libraries, where you cannot determine
    the memory management behavior in the third-party DLL.

    Threads in a process are expected to cooperate in such a way that one will not free memory that the other needs.
    Use of this function does not negate the need to do this. If this is not done, the application may fail in an unpredictable manner.

    Dereferencing potentially invalid pointers can disable stack expansion in other threads. A thread exhausting its stack,
    when stack expansion has been disabled, results in the immediate termination of the parent process,
    with no pop-up error window or diagnostic information.

    If the calling process has read access to some, but not all, of the specified memory range, the return value is nonzero.

    In a preemptive multitasking environment, it is possible for some other thread to change the process's access to the memory being tested.
    Even when the function indicates that the process has read access to the specified memory, you should use structured exception handling
    when attempting to access the memory. Use of structured exception handling enables the system to notify the process
    if an access violation exception occurs, giving the process an opportunity to handle the exception.

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

    Leave a comment:


  • Lance Edmonds
    replied
    Since VARPTR is returning zero, the CLNG is simply converting a long integer result into a long integer result...

    Eric summed it up here... In Daniels original code, VARPTR returns zero, and for good measure, ERR is set to reflect that a null string was passed.

    Where is the problem?



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

    Leave a comment:


  • Edwin Knoppert
    replied
    I experianced the same when i used a PB exe using PB dll.
    VB will allways use a valid address even when empty.

    I test this using:

    If Clng( VarPtr( az ) ) Then

    End If

    Maybe Clng() makes a difference here.


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

    Leave a comment:


  • Steve Hutchesson
    replied
    Daniel,

    If you wish to test if an ASCIIZ string is a NULL string, just read
    the 1st byte of the string and see if its ascii zero.

    Without testing it, I would be inclined to try,
    Code:
        If left$(asciizString,1) = chr$(0) Then
          ' code here
        End If
    Regards,

    [email protected]

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

    Leave a comment:


  • Daniel Corbier
    replied
    Guys thanks for the responses.

    Eric, yes, error #9 is my concern. I was getting a mysterious
    crash. I gradually narrowed it down, keeping an eye on arrays.
    But the culprit was a line where an empty ASCIIZ argument was
    used. The thing with these types of errors is that they remain
    silent, and crash somewhere else in the program, making it
    very hard to track.

    Fred, I'm not familiar with IsBadStringPtr(). I didn't see
    it in the help file. I searched and found it in the API, but
    with no explanation of how it works.

    Semen, I tried your solution (passing the arg as an ASCIIZ PTR),
    and it seems to work so far. I tried it with PB, VB, and Delphi.
    Since it works with Delphi, I assume that it will work with
    C++ as well. But I'll have to try it more extensively later on
    to make sure.

    Now that the problem appears to be solved, I still wonder why
    PB returns an error for VARPTR of an empty ASCIIZ string. This
    doesn't appear normal. Maybe R&D should look into it, and fix it
    if it's a bug. To me, it seems like "If VARPTR(AsciizArg) = 0 ..."
    should work just as well as "If AsciizPtrArg = 0 ...". The
    documentation discusses the usage of ASCIIZ arguments. It
    mentions necessary precautions when modifying the argument, but
    it doesn't say (at least where I was looking) anything about
    potential problems with accessing an empty ASCIIZ string arg.

    The program in the Sample\VB32 directory (CAPFIRST.BAS) would
    also need to be updated so that it can safely handle an empty
    string, since others might pattern their DLLs after that
    example.

    ------------------
    Daniel Corbier
    UCalc Fast Math Parser
    http://www.ucalc.com

    Leave a comment:


  • Eric Pearson
    replied
    I was wrong, I mean I was right...

    I am in fact seeing VARPTR=0 when "" is passed, as well as when BYVAL 0 is passed.

    What is the problem that you are trying to overcome? Are you concerned about the ERR 9?

    -- Eric



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

    Leave a comment:


  • Eric Pearson
    replied
    Dan --

    Keep in mind that there is a difference between passing a nul string and a nul pointer. For example, if you pass an ASCIIZ string that has a length of zero, you are actually passing a valid pointer to a memory location, but when Windows looks there it will find CHR$(0), indicating end-of-string. But if instead of a string you pass BYVAL 0 (thereby overriding PB's automatic parameter checking) if your function checks VARPTR it will see zero. A nul pointer, not a pointer to a nul string.

    You probably want to check for VARPTR(zString)=0 first. Then, only if it is nonzero, check for LEN(zString) = 0.

    [Added later]

    Hmmm... I just tried my own advice about VARPTR and I didn't get the results I expected. But it does look like testing LEN=0 will work.

    -- 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 May 23, 2001).]

    Leave a comment:


  • Semen Matusovski
    replied
    Code:
    $Compile Exe
    $Debug Error On
    Sub Test(AsciizArg As Asciiz Ptr)
       If AsciizArg = 0 Then Temp$ = "" Else Temp$ = @AsciizArg
       MsgBox "Err = " + Str$(Err),, "Temp$ = " + $DQ + Temp$ + $DQ
    End Sub
    Function PbMain As Long
       Call Test("Test #1")
       Call Test("")
    End Function

    ------------------
    E-MAIL: [email protected]

    Leave a comment:

Working...
X