Announcement

Collapse
No announcement yet.

Use LOWRD and HIWRD carefully with API messages

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

  • Use LOWRD and HIWRD carefully with API messages

    When processing some windows messages (ie. WM_LBUTTONDOWN) which pass both the X and Y coordinates of the mouse cursor in the lParam value, you have to extract the X and Y coordinates from a single Long value.

    The MS docs say to use HIWRD and LOWRD to extract each value. The problem with this is that the HIWRD and LOWRD functions in PB do not return negative values. The value will always be a positive value, even if the bit that defines a negative value (in an Integer) is set.

    While in most instances this doesn't cause a problem, there are some instances that you will need to return a negative value.

    For example, when a window captures the mouse, it is possible to get a negative value back for a coordinate when processing a message like WM_LBUTTONDOWN. Using LOWRD and HIWRD to extract a negative value from lParam, will produce an error in your program, since you can't get a negative value back.

    Here are some "replacement" functions for LOWRD and HIWRD which will return a negative value when it exists:

    Code:
    FUNCTION NLOWRD(BYVAL L AS LONG) AS LONG
       LOCAL IP AS INTEGER PTR, I AS INTEGER
       IP=VARPTR(L)
       [email protected]
       FUNCTION=I
    END FUNCTION
    '
    ' -----------------------------------------
    '
    FUNCTION NHIWRD(BYVAL L AS LONG) AS LONG
       LOCAL IP AS INTEGER PTR, I AS INTEGER
       IP=VARPTR(L)+2
       [email protected]
       FUNCTION=I
    END FUNCTION
    If you know that you may get a negative value when extracting a Integer (Low word or high word) from a Long, then use the functions above instead of LOWRD and HIWRD.



    [This message has been edited by Chris Boss (edited May 19, 2000).]
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

  • #2
    Chris -
    try
    Code:
       Dim l As Long, i As Integer, j As Integer
       l = &HFFFEFF00  ' -2  -256 
       i = HiWrd(l): j = LoWrd(l)
       MsgBox Str$(i) + Str$(j)
    ------------------

    Comment


    • #3
      Semen;

      This is a quote from the PB docs:

      Remarks:

      LOBYT/LOWRD always return the least significant (first byte/word) unsigned result, even when used with signed values.
      I was using LOWRD and HIWRD in an expression that would set a value in a LONG (not Integer) and I do not get a negative value back. Your expression sets the value of an Integer using LOWRD and HIWRD, so it may be different.

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


      [This message has been edited by Chris Boss (edited May 19, 2000).]
      Chris Boss
      Computer Workshop
      Developer of "EZGUI"
      http://cwsof.com
      http://twitter.com/EZGUIProGuy

      Comment


      • #4
        Semen;

        I tested your code and if i and j are Integers it works fine.

        If you change the variables i and j to Longs, it does not work correctly

        This means that if the expression used sets the value of a Long then there is a problem when the number is negative.

        I always use Longs for Integer variables (in 32 bit code) (which were having values set by using LOWRD and HIWRD), so in my code I was not getting a negative value back.



        ------------------
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #5
          Semen;

          Also if you use LOWRD and HIWRD in a comparison , select case , or any other way which does not set a variable to a value (which is very likely), you will get an error.

          Try the code below:

          Code:
          #COMPILE EXE
          FUNCTION PBMAIN () AS LONG
             DIM l AS LONG
             l = &HFFFEFF00  ' -2  -256
             MSGBOX STR$(LOWRD(l)) + STR$(HIWRD(l))
          END FUNCTION

          ------------------
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            Old problem, easy solution... to translate between values, use a union (and maybe a UDT to extract low and hi word's, etc).

            Code:
            TYPE DblInt
              Wrd1 AS INTEGER
              Wrd2 AS INTEGER
            END TYPE
             
            TYPE DblWrd
              Wrd1 AS WORD
              Wrd2 AS WORD
            END TYPE
             
            UNION
              Param1 AS LONG
              Param2 AS DWORD
              Param3 AS DblInt
              Param4 AS DBlWord
            END UNION
            Now, by setting any particular member of the union, you'll be able to read the component parts or "convert" between signed and unsigned simply by referring to the desired member of the UNION.

            Simple!


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

            Comment


            • #7
              Chris has found an interesting problem here, as per the name of the
              functions, they only return unsigned results which is no use for
              negative numbers. Below is a small test piece where you have signed
              in, signed out.

              Code:
                  LOCAL z&    ' signed LONG
                
                  x% = -10    ' signed 16 bit integer
                  y% = -20    ' signed 16 bit integer
                
                ' -------------------------------------------
                ' Combine the two values into a LONG integer
                ' -------------------------------------------
                  ! mov ax, y%
                  ! rol eax, 16
                  ! mov ax, x%
                  ! mov z&, eax
                
                  x% = 0      ' clear 16 bit integers
                  y% = 0
                
                ' ---------------------------------------------
                ' Extract the two values from the LONG integer
                ' ---------------------------------------------
                  ! mov eax, z&
                  ! mov x%, ax
                  ! rol eax, 16
                  ! mov y%, ax
                
                  MessageBox hWin,ByCopy str$(x%)+" "+str$(y%),"Result", _
                             %MB_OK or %MB_ICONINFORMATION
              Regards,

              [email protected]

              ------------------
              hutch at movsd dot com
              The MASM Forum

              www.masm32.com

              Comment


              • #8
                Chris --
                That it will be problems w/o REGISTER NONE = with register variables is clear from the beginning, because for temporary calculations are used 32-bits registers.
                Exactly because of such reason I never use REGISTER DEFAULT.

                There are some popular functions, linked with keyboard, menu, which really return integer, but declared in Win32Api.Inc as Long.
                Occurs funny situations, if you try to compare with predefined constants.
                So, I use similar trick for these functions: i = ..., where i as Integer. Of course, #Register none.

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

                Comment


                • #9
                  Yes, the fix is simple.

                  The reason I posted this though, is that it is important that HIWRD and LOWRD not be used indescrimately when processing lParam (and wParam) values for Window messages.

                  Quite a few bugs can be introduced to our windows programs if we do !

                  It would be nice, if the next version of PB offered an alternative set of functions for HIWRD and LOWRD (say HIWRDI and LOWRDI) which return a true Integer value (signed) and then give due warning in the docs about when to use one or the other.

                  Just add this to the wish list !


                  ------------------
                  Chris Boss
                  Computer Workshop
                  Developer of "EZGUI"
                  http://cwsof.com
                  http://twitter.com/EZGUIProGuy

                  Comment


                  • #10
                    Added to the list. Thanks!

                    I don't know about the names though - maybe more like HIINT() and LOINT() would sound better?

                    Regardless, PowerBASIC can another built-in way to convert between signed and unsigned... take a look at BITS() in the help file.

                    Code:
                    ' Simple PB/CC code to demonstrate how to return signed integers from a composite unsigned DWORD
                    #COMPILE EXE
                    FUNCTION PBMAIN
                        DIM a AS DWORD
                        a = &H0FFFFFFFE
                        PRINT HIWRD(a), LOWRD(a)
                        PRINT BITS%(HIWRD(a)),BITS%(LOWRD(a))
                        WAITKEY$ 
                    END FUNCTION

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

                    Comment


                    • #11
                      Lance --
                      your code doesn't work, at least, on my PC (PB/CC & DLL).
                      CInt also.
                      CVI(MKI$(LoWrd( works, but it's not serious.

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

                      Comment


                      • #12
                        Semen, it will in the next compiler update... No, we don't have a date for shipping yet.


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

                        Comment


                        • #13
                          For example, when a window captures the mouse, it is possible to get a negative value back for a coordinate when
                          processing a message like WM_LBUTTONDOWN. Using LOWRD and HIWRD to extract a negative value from lParam,
                          will produce an error in your program, since you can't get a negative value back.
                          The trick here is to pay careful attention to what is actually packed in the LONGs.
                          You have to read the docs carefully and use type casting (pay attention to the hungarian
                          not always acurate but helps) . For example,
                          Code:
                          WM_LBUTTONDOWN - lParam is packed as two SHORTs(INTEGERs in PB) so,
                                           LOCAL x AS INTEGER
                                           LOCAL y AS INTEGER
                                           x = LOWRD(lParam)
                                           y = HIWRD(lParam)
                          WM_SETCURSOR   - lParam is packed as a SHORT(INTEGER in PB) and a WORD(WORD in PB) so,
                                           LOCAL nHittest  AS INTEGER
                                           LOCAL wMouseMsg AS WORD
                                           nHittest  = LOWRD(lParam)
                                           wMouseMsg = HIWRD(lParam)
                          LOWRD and HIWRD is not the problem.

                          ------------------
                          Dominic Mitchell
                          Phoenix Visual Designer
                          http://www.phnxthunder.com

                          Comment


                          • #14
                            This should work fine, the fastest way I can think of:

                            ! push lParam
                            ! pop int1
                            ! pop int2

                            Of course lParam is a DWORD and int1 and int2 are INTEGERs here.


                            Peter.


                            ------------------
                            [email protected]

                            Comment


                            • #15
                              As Chris was kind enough to find this problem, here are two very small
                              functions for those who don't like mixing assembler with their normal
                              code.

                              Code:
                                ' -------------------------------------------
                                ' This is the code to load the LONG integer.
                                ' -------------------------------------------
                                    LOCAL yy as LONG
                                
                                    ! mov ax, -100  ; high integer
                                    ! rol eax, 16
                                    ! mov ax, -50   ; low integer
                                    ! mov yy, eax
                                
                                ' -------------------------------------------
                                ' The message Box to test the return values.
                                ' -------------------------------------------
                                    MessageBox hWin,ByCopy str$(LoInt(yy))+str$(HiInt(yy)), _
                                               "Hi & Lo Integer from LONG", _
                                               %MB_OK or %MB_ICONINFORMATION
                                
                                ' -------------------
                                ' The two functions.
                                ' -------------------
                                '##########################################################################
                                
                                FUNCTION HiInt(ByVal lVal as LONG) as Integer
                                
                                    #REGISTER NONE
                                
                                    ! mov eax, lVal
                                    ! rol eax, 16
                                    ! mov FUNCTION, ax
                                
                                END FUNCTION
                                
                                '##########################################################################
                                
                                FUNCTION LoInt(ByVal lVal as LONG) as Integer
                                
                                    #REGISTER NONE
                                
                                    ! mov eax, lVal
                                    ! mov FUNCTION, ax
                                
                                END FUNCTION
                                
                                '##########################################################################
                              I just saw Peter's small code version using push/pop which I think is very neat.

                              Regards,

                              [email protected]

                              ------------------
                              hutch at movsd dot com
                              The MASM Forum

                              www.masm32.com

                              Comment

                              Working...
                              X