Announcement

Collapse
No announcement yet.

API Declaration Question

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

  • API Declaration Question

    Along the lines of my earlier question, the MSDN for WindowFromPoint and ChildWindowFromPoint say the argument is a Point.

    But in the Win32API.inc, both declarations separate the x and y, as in this extraction:

    Code:
    DECLARE FUNCTION ChildWindowFromPoint LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" (BYVAL hwndParent AS DWORD, BYVAL x AS LONG, BYVAL y AS LONG) AS DWORD
    How can PowerBASIC make such a change in the argument? I'm sure it's not PB Inc, just my understanding of how it works.

    (Jose, I'm sure you'll have the answer)

  • #2
    The correct declare would be:

    Code:
    DECLARE FUNCTION ChildWindowFromPoint LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" (BYVAL hwndParent AS DWORD, BYVAL pt AS POINT) AS DWORD
    But when the function was translated, the PowerBASIC compilers did not allow to pass structures by value, so the only workaround was to pass the members of the structure, x and y, separately.
    Forum: http://www.jose.it-berater.org/smfforum/index.php

    Comment


    • #3
      Thanks Jose,

      So, the Declaration in Win32API.inc is intercepted by the PowerBASIC compiler and translates (merges x and y) the call to the true Windows API ?

      That's almost disturbing. Does it happen a lot? I always have trouble knowing what is a Windows requirement, and what is a PowerBASIC constraint. I guess if I were an SDK guy, I'd more likely know the differences.

      I like to know why things work the way they do and MSDN documentation is the first place I turn. The sometimes different MSDN documentation vs PowerBASIC implementation - including knowing when it occurs - can catch me by surprise.

      Comment


      • #4
        Gary,

        (NOTE: this post is in error for this particular API declare, so see my next post where I clarify. This post is valid though if a paramater is a structure passed by a pointer)

        In my experience I prefer to use:

        BYVAL lpSomeVar AS LONG

        for any API declaration that requires a pointer to a structure, rather than use BYREF and the structure itself.

        This way you can pass a pointer to any memory address you want and it does not have to be a in a predefined structure.

        I then use VARPTR to get the address of the data and pass that.

        For example the POINT structure is simply two longs one after the other. One could pass two longs in an array, two longs (first half) in a RECT structure or even just eight bytes (two longs) from within a string (use STRPTR).

        The value of simply passing a pointer BYVAL allows you to use memory templates for a variety of structures. It is very helpful when an API has a different structure for say Win95/98/ME than XP/VISTA. Some API's have changes there structure in different versions of WIndows (Windows knows which is which by the first member which is the size of the structure).

        Now if the API is declared using a BYREF structure, you can override this in PB by using:

        SomeApi BYVAL VARPTR(SomeVar)

        instead of

        SomeAPI SomeStruc ' which is byref

        It should be noted that some API's allow you to pass a null pointer instead of the structure. If you do, then you can't do it BYREF, but you can override using:

        SomeApi BYVAL %NULL
        Last edited by Chris Boss; 14 Jul 2009, 11:18 PM.
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #5
          So, the Declaration in Win32API.inc is intercepted by the PowerBASIC compiler and translates (merges x and y) the call to the true Windows API ?
          No.

          The definition of the POINT/POINTAPI structure is as follows:

          Code:
          TYPE POINTAPI
             x AS LONG
             y AS LONG
          END TYPE
          The functions WindowFromPoint and ChildWindowFromPoint don't expect a pointer to the structure in the stack, but the structure itself (it is passed by value, not by reference).

          When you pass an structure by value, all the members of it are pushed into the stack, one after another, not merged. In the case of the POINT structure, two long values, x and y, are pushed. If you declare the parameters of the function as BYVAL x AS LONG and BYVAL y AS LONG, instead of BYVAL pt AS POINT, two long values, x and y, are also pushed in the stack. The result is exactly the same. The convenience of passing an structure by value instead of the individual members is to use less parameters.
          Forum: http://www.jose.it-berater.org/smfforum/index.php

          Comment


          • #6
            That's almost disturbing. Does it happen a lot? I always have trouble knowing
            what is a Windows requirement, and what is a PowerBASIC constraint.
            Your source for ALL windows API calls is whatever you use for Windows API reference. WIN32API.INC is NOT (repeat NOT) a Windows API refernce.

            WINAPI32.INC is "a" translation to PB-compatible syntax of the documentation provided by Microsoft. Note I said "a" translation - not "the" translation. There is no such thing as one and only one correct PowerBASIC DECLARE statement for ANY external function regardless of source.
            I guess if I were an SDK guy, I'd more likely know the differences
            Maybe; but regardless: "translate Microsoft documentation for WinAPI functions to PowerBASIC-compatible syntax" should be on your list of "Ten Top Things Every PB Progreammer Should Be Able to Do."

            The thing Jose pointed out about pushing things onto the stack is really not all that important as long as you know you CAN pass a UDT by value using the current version of the compiler. His workaround for what was not possible in prior versions is not something anyone is going to expect you to know. (There's actually LOTS of stuff we had to do "the long way" in prior versions because of compiler limitations)

            FWIW, speaking of the CURRENT compiler... because you can now share ALIASes (you could not prior to 8x) you can actually DECLARE two or three versions of that function now:

            Code:
            ' the origianl, more or less
            DECLARE FUNCTION ChildWindowFromPoint _
                LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" _ 
                (BYVAL hwndParent AS DWORD, BYVAL pt AS POINT) _ 
              AS DWORD
            
            ' original with name telling you to pass POINT structure:
            DECLARE FUNCTION ChildWindowFromPointUSINGPOINT _
                LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" _ 
                (BYVAL hwndParent AS DWORD, BYVAL pt AS POINT) _ 
              AS DWORD
            
            ' original with differentname telling you to pass X And Y separately:
            DECLARE FUNCTION ChildWindowFromPointUSINGXY _
                LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" _ 
                (BYVAL hwndParent AS DWORD, BYVAL X AS LONG, BYVAL Y AS LONG) _ 
              AS DWORD
            Call any one of ;em from your program and get the same results.

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

            Comment


            • #7
              The value of simply passing a pointer BYVAL allows you to use memory templates for a variety of structures. It is very helpful when an API has a different structure for say Win95/98/ME than XP/VISTA.
              If it is an external function, you can also use AS ANY.
              Forum: http://www.jose.it-berater.org/smfforum/index.php

              Comment


              • #8
                Sorry, I didn't look at the SDK docs for childwindowfrompoint before commenting above.

                Jose is right.

                In this particular case the API docs say the values are not a pointer to a structure. There are simply two longs passed byval so they are passed on the stack both of them. Thats correct.

                My comment above is valid only when the API docs indication a paramater is a pointer to a structure (byref in PB terms to a structure).

                In Powerbasic, BYREF simply means a long (or dword) is all that is put on the stack, which points to a data structure. The data structure defined in the declare statement is simply for the benefit of the compiler to force a specific data type be used. The ANY specifier overrides this though.

                Windows (the API's) don't though know what structure you are passing (when done BYREF or actually a pointer to the data). They only assume the structure is correct.

                I should have looked at the api docs first before posting in this case.

                This brings a better lesson (even for me) to the fore.

                Always double check the API docs (SDK for Windows) to see what the official API definitions are. Learn to recognize the data types (also whether it should be byval or byref) of the declarations.

                Technically, all the paramaters of an API are BYVAL (value put on the stack). Thats the rule.

                How does one know whether a structure is all put on the stack or whether it is simply a pointer ?

                Simple.

                The API docs use a common syntax for describing the paramaters.

                ChildWindowFormPoint

                describes the last paramater as:

                POINT pt

                Type is POINT, but notice the variable label (pt). This is crucial because an important part of that label (name) is missing.

                For example look at the declaration for InvalidateRect. The second paramater is:

                CONST RECT* lpRect

                It is a structure, but the clue I always look for is whether the label (name) has lp in front of it. The prefix lp is always used to indicate a pointer to a structure.

                Then read the paramater description after that and it will also note that the paramater is a pointer to a RECT structure.

                This means the paramater is actually a BYVAL long (or dword) which is a pointer to the data structure (in this case my previous comment would hold true).

                I apologize for not double checking the API docs first before commenting on this specific API function.

                Jose is absolutely correct.
                In this case the POINT structure is passed BYVAL, which means the enire structure is put on the stack.

                In the past to do this in PB, required breaking the members up for the structure and passing each one one BYVAL so instead of a POINT structure the declaration passes two longs , each byval.
                PB 6.0 required this. Not sure about PB 7.0.

                PB 8.0 and 9.0 both can now pass a structure BYVAL, so it is possible to define an API declare with something like:

                BYVAL PT AS POINT

                whereas in the past it was not possible.

                The API includes likely have been progressively updated with each new compiler version, but declares like ChildWindowFormPoint ddin't get changed. The declare is the old style from previous versions.
                Last edited by Chris Boss; 14 Jul 2009, 11:20 PM.
                Chris Boss
                Computer Workshop
                Developer of "EZGUI"
                http://cwsof.com
                http://twitter.com/EZGUIProGuy

                Comment


                • #9
                  It is a structure, but the clue I always look for is whether the label (name) has lp in front of it. The prefix lp is always used to indicate a pointer to a structure.
                  I'm not a "C" guy, but when the textual documentation is unclear I look for the asterisk in front of the param to see if I should try by reference or by value first:
                  Code:
                     * pt    =   pt by reference 
                       pt       = pt by value
                  MCM
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Originally posted by Chris Boss View Post
                    It is a structure, but the clue I always look for is whether the label (name) has lp in front of it. The prefix lp is always used to indicate a pointer to a structure.
                    In Hungaian Notation, "lp" stands for "Long Pointer (to)" (DWORD) as opposed to "p" = "Pointer (to)" (WORD). It's there to distinguish the variable type (size) of the pointer.

                    It doesn't necessarily hint at the data type it points to. For example lpsz is a common prefix not pointing to a structure, but is a long pointer to a zero-terminated string (ASCIIZ).

                    Comment

                    Working...
                    X