Announcement

Collapse
No announcement yet.

Dll Unloading, bad Pointers bad

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

  • Dll Unloading, bad Pointers bad

    I am having a bugger of a time tracking down why I do not get a "Crash" (in this case not BSOD, but "Windows has encountered a problem") that does not occur until closing, and only if I follow a certain sequence of steps. (Thankfully it is in one of my troubleshooting routines, so how often could it occur?, but on the other hand, How much faith can you have in a product if it causes an eventual crash when the tool was meant to SOLVE a problem?)

    Anyways, I have debugged for any errors that could be caught by the debugger. And I have made sure any threads are not left open via WaitObject, and I have set a flag in all my loops to escape if ending while in my loop. And I have added Error Handling routines to log an error if one occurs that I have not thought of.

    Now I am looking at bad pointers, or some other memory corrupted area and looking at the Windows API about "IsBadPtr", "IsBad______Ptr" functions, and I am a lil confused by the documentation, but I get the most part.

    Since I am pretty weak on pointers (getting better at it, but still weak), I am hoping someone can explain a lil better than the documentation can.
    1. If I use VARPTR or STRPTR, and the next line check if it is 0 or some value, how long is this value good for? (Assuming I make no changes on the variable that the VARPTR or STRPTR is pointing at), is it good for just in the function? or good for the scope that I declared the variable?, or technically though unlikely, but maybe possible, from the moment I ask till the moment that Windows happens to change it? (AKA, if I ask in 1 line and then the next line I do something, although its unlikely if even possible, that in that time the value could go from good to bad)
    2. Variables declared as actual pointers...."AS BYTE POINTER", "AS STRING POINTER", and etc....Are they valid for the scope I assign them? (AKA, if Global, or Local, or even the forgiving "Dim" (Which I will admit I have to learn to stop using Dim when its not an array, but thats another story)
    3. Temporarily until I totally understand pointers, I am hoping "IsBadCodePtr" is my save all to at least tell me is the pointer valid?, but I am confused about the remarks "If the calling process does not have read access to the specified memory, the return value is nonzero."...I do not get if the word "READ" means, as in "Read/Write" access....or if it means "Has access to" ???? Also, since it is "BadCode" does it mean the pointer to a function? or that a pointer is valid period? (for that matter, is there a function that just says the pointer is valid?...I will worry about the block it points to later, but for now I just want to understand better)


    Hard to follow I bet, but I am trying to understand the "basics" before I jump into the "Legal-ize" of what it all means.

    Up until now, I have been using the following basic concept
    Code:
        StringPointer = VARPTR(CharsToFind)  'Get the location of the pointer
        SELECT CASE StringPointer
            CASE 0
                FUNCTION = %False
                EXIT FUNCTION
            CASE ELSE
        END SELECT
    ....Do whatever comes next
    Conflict of terms I know...but in this case I just wanted to know if the pointer existed...is there a better way?
    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
    If I use VARPTR or STRPTR, and the next line check if it is 0 or some value, how long is this value good?
    VARPTR is good for the scope of the variable. STRPTR is good only for the scope of the variable AND since the last change to the string data.

    Variables declared as actual pointers...."AS BYTE POINTER", "AS STRING POINTER", and etc....Are they valid for the scope I assign them?
    The variables themselves are valid for their scope; however, since you are always assigning values to them, the TARGET of the poitner is only valid for the scope of the variable or function used to obtain the pointer.
    e.g.

    Code:
      GLOBAL  pSomething AS something PTR
    ....
      pSomething = VARPTR(someVar)
    The variable pSomething is valid always; however, the TARGET is only valid within the scope of the variable 'somevar'

    You can have 'valid for the life of my program' variables only by...
    1. Using a PB GLOBAL, STATIC or THREADED var.
    2. Allocating memory yourself.
    e.g
    Code:
       pSomething = HeapAlloc (GetProcessHeap, size, flags)
    Since the value returned by HeapAlloc is valid until HeapFree'd, this piece of memory is valid throughout the life of your program.

    Basically if the compiler allocates the memory, it deallocates (and invalidates) as soon as the variable to which the aloocation was made goes out of scope.

    Temporarily until I totally understand pointers...
    ... you were going to try to avoid them by coding in a more conventional fashion using 'regular' variables and more wisely using the scope features provided by the compiler?

    >but in this case I just wanted to know if the pointer existed

    'Existence' is meaningless.. if it didn't "exist" your compile would fail (you ARE using #DIM ALL, right?).

    Any pointer variable with a value < &h10000 (65536 d) is invalid. Pointer values greater than that MAY be valid. OR not. (note this is when cast as DWORD, eg, &h7F123456 could be valid, even though it is negative when cast as LONG).

    The IsBadxxxxPtr functions will tell you if an address ("pointer value") is valid in your procedure; however it can't tell you what's there until you read it.
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Thank you Michael, It clears up a few things.

      VARPTR is good for the scope of the variable. STRPTR is good only for the scope of the variable AND since the last change to the string data.
      Yep got that part, had to double check though, since VARPTR I look at as Pointers to Numbers, and STRPTR I look at as pointers to Strings, and if I change the value (or String) then the Pointer I obtained, may be pointed at the same "Target", but since I made a change, it is no longer the SAME target, but I need to repoint at the new target

      So as I get it...if I make a change, ANY change, then the scope is gone until I reassign the new value, so basically
      Code:
      FUNCTION 1
           Assign Global Variable
      END FUNCTION
      Code:
      FUNCTION 2
           Last Value = Last Value +1
      END FUNCTION
      Code:
      FUNCTION 1
           Do something with the Pointer and value
      END FUNCTION
      May just "Luck Out", but technically, I changed the target, so the value I use is now invalid, because the pointer is pointed at the wrong place (and "BEST case" use the target I last left there, worst case use whatever garbage may be there now

      ... you were going to try to avoid them by coding in a more conventional fashion using 'regular' variables and more wisely using the scope features provided by the compiler?
      I guess by that my typical pursuit is Unless I need it for some reason, I trust PB to get things right, rather than me attempting my attempt to concatenate a string like
      [quote]
      Lets say string = "Hello"
      I say string = "Hello" + CHR$(13)
      [/unquote]
      I trust PB to concatenate, and reassign for me rather than me using pointers to do all the work myself because that is their job, and they know better than I do in that area.

      'Existence' is meaningless.. if it didn't "exist" your compile would fail (you ARE using #DIM ALL, right?).
      Yep....learned from my mistakes LONNNNG AGO from the VB side that let you get away with things until you bent the rules too far and did NOT know why

      Any pointer variable with a value < &h10000 (65536 d) is invalid. Pointer values greater than that MAY be valid. OR not. (note this is when cast as DWORD, eg, &h7F123456 could be valid, even though it is negative when cast as LONG).
      Nice thing to know...but if memory serves...did I misunderstand or did you break a cardinal rule and use a Number rather than a equates? (I think you meant 65535 would be %WM_USER in Pb, but 65536 would be %WM_USER + 1) concept?

      The fun part is I am starting to get the point on pointers
      (Hopefully they are not all in my back )
      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
        The IsBadXX functions simply mean just that the pointer is invalid (hence it's "bad"), if you have IsBadCodePtr returning nonzero, it means that the code block it points to is unreadable, likewise IsBadReadPtr means a memory location cannot be read from. You can't write to a code block, they are always read only. In a typical application you have code storage (read only) and data storage (read/write).

        If you're having crashes occur randomly in a program then you really should go over the code line-by-line and try disabling some of it with #if 0 / #endif blocks and add some debug console output where you think the problem is likely to occur.

        Also, you seem to be muddying things with STRPTR vs. VARPTR. All that STRPTR does is return a pointer to a dynamic string's data address (pointer). You can't do this with VARPTR as it will return the string's handle which may be needed too (this is obviously for compatibility reasons - if I could "rewrite" the history of BASIC, I would have STRPTR returning the string's handle, and VARPTR returning the string data address )

        Here's a little example of STRPTR vs. VARPTR with arithmetic:

        Code:
        Function PBMain
          Dim s$
         
          s$ = "Hellooo"
         
          ? "Handle data:" + Str$(Peek(Dword, VarPtr(s$))) + $CrLf + _
            "StrPtr():" + Str$(StrPtr(s$)) + $CrLf + _
            "String data: " + Peek$(Asciiz, Peek(Dword, VarPtr(s$)), 1000)
         
        End Function
        Last edited by Kev Peel; 20 Nov 2007, 07:37 PM. Reason: added code
        kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

        Comment


        • #5
          did I misunderstand or did you break a cardinal rule and use a Number rather than a equates
          Nope.

          The values used by the operating system are not values you use when programming.

          That 64K number is not explictly documented, anyway.. it's inferred. See the IsIntResource Macro/function for why I have inferred this. There's some other situations eg in the ODBC API (on my mind these past couple days because of a project I'm working on) where a parameter value is documented as, "if >64K it's an address, if less then it is a numeric value of significance." That tells me user-obtained memory never has a (virtual) address less than 65536.

          In my way of thinking, WM_USER-based values should be used only to assign numeric values to private window notification messages. That is, of course, a pure 'style' thing but it works for me.

          Just as a little suggestion... if you are having problems vis-a-vis STRING pointers, you might want to think about changing to ASCIIZ datatypes. If a conversion is required for some operation, eg.....
          Code:
           LOCAL|STATIC|GLOBAL szText AS ASCIIZ * 1024 
           ......
           Z$ = MID$(szText,3,5)
          .. the PB compiler will handle all that for you.

          No, that does not result in "the fastest possible" code, but unless you are doing tens and hundreds of thousands of these things absolutely nobody will notice.
          Michael Mattias
          Tal Systems Inc. (retired)
          Racine WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Michael,

            That 64K number is not explictly documented, anyway.. it's inferred. See the IsIntResource Macro/function for why I have inferred this. There's some other situations eg in the ODBC API (on my mind these past couple days because of a project I'm working on) where a parameter value is documented as, "if >64K it's an address, if less then it is a numeric value of significance." That tells me user-obtained memory never has a (virtual) address less than 65536.
            I’m not sure if this relates to your discussion but as I understand it, earlier versions of Windows (16bit) would often distinguish between a handle and a string by determining if the high order bits were set or not. If they were set then the value was deemed to be a string pointer, if not then the value would be a handle. Hence the cutoff of less than 65536 as 65535 is the greatest value without effecting the high order bits.

            I can’t think of any API examples but my (fading) memory thinks that API’s to retrieve cursors or icons worked this way.


            Pat

            Comment


            • #7
              can’t think of any API examples but my (fading) memory thinks that API’s to retrieve cursors or icons worked this way
              That's a good example; so is Dialog box.

              Given a resource file like this:
              Code:
              // PAGE TWO : SELECT WHAT YOU WANT TO DO, ARCHIVE OR JUST EXTRACT
              100 DIALOG 6, 27, 280, 100
              STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
              CAPTION "PPPS Remittance Archive Wizard"
              FONT 8, "MS Sans Serif"
              BEGIN
                  CONTROL         "Archive - Delete from source database", ID_ARCHIVE,
                                  "Button", BS_AUTORADIOBUTTON, 20, 10, 136, 10
                  CONTROL         "Export - Do not delete from source database", ID_EXPORT,
                                  "Button", BS_AUTORADIOBUTTON, 20, 30, 154, 10
                  CONTROL         "Comment", ID_PAGECOMMENT, "Static", WS_GROUP, 22, 42, 216, 34
              END
              ..
              DUPFILES DIALOG 66, 50, 281, 65
              STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
              CAPTION "Confirm Destination Folder"
              FONT 8, "MS Sans Serif"
              BEGIN
                  CONTROL         "One or more destination files exist in selected folder ",
                                  -1, "Static", WS_GROUP, 39, 10, 174, 8
                  CONTROL         "Selected Folder name here", ID_DEST_FOLDERNAME,
                                  "Static", SS_CENTER | WS_GROUP, 38, 22, 174, 8
                  CONTROL         "&Overwrite", ID_DEST_OVERWRITE, "Button", WS_TABSTOP,
                                  38, 36, 50, 14
                  CONTROL         "&Append", ID_DEST_APPEND, "Button", WS_TABSTOP, 99, 36,
                                  50, 14
                  CONTROL         "&New Folder ", IDCANCEL, "Button", WS_TABSTOP, 158, 36,
                                  50, 14
              END
              Given the declare used in the default Win32API.INC...
              Code:
              DECLARE FUNCTION DialogBoxParam LIB "USER32.DLL" ALIAS "DialogBoxParamA" _ 
                (BYVAL hInstance AS DWORD, _ 
              lpTemplateName AS ASCIIZ, _ 
                BYVAL hwndParent AS DWORD, BYVAL lpDialogFunc AS LONG, _
               BYVAL dwInitParam AS DWORD) AS LONG
              ..the lpTemplate param must be "BYVAL 100&" to display the first dialog and "szTemplate" (an ASCIIZ variable = "DUPFILES") to display the second.

              All numeric identifiers for resource IDs or resource types must be less than 65536 (this IS documented), suggesting that "65536" is in fact a 'magic number' for string addresses, all of which are runtime user-allocated memory.

              The same thing applies to Bitmap, icon and other resources which may use either character or numeric identifiers

              ("#100" will also work for the first).

              And in a quasi-interesting note, since PB 8x allows duplicate ALIASes, you could add a DECLARE to your program...
              Code:
              DECLARE FUNCTION DialogBoxParam_ByNumber er LIB "USER32.DLL" ALIAS "DialogBoxParamA" _ 
                (BYVAL hInstance AS DWORD, _ 
              BYVAL DialogIdNo AS LONG , _ 
                BYVAL hwndParent AS DWORD, BYVAL lpDialogFunc AS LONG, _
               BYVAL dwInitParam AS DWORD) AS LONG
              .. and create dialogs without any inline BYVAL overrides and call with...
              Code:
              iRet = DialogBoxParam_ByNumber ( hInst, 100, ...)
              ... but I think I'm getting a tad esoteric.
              Michael Mattias
              Tal Systems Inc. (retired)
              Racine WI USA
              [email protected]
              http://www.talsystems.com

              Comment

              Working...
              X