Announcement

Collapse
No announcement yet.

Window Get ID problem

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

  • Michael Mattias
    replied
    Code:
    ' add a DECLARE of convenience, subject to limitations below: 
    DECLARE FUNCTION ChildWindowFromPointAPI LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" _
            (BYVAL hwndParent AS DWORD, BYVAL pt AS POINTAPI) AS DWORD
    ....
    
      LOCAL Pt AS POINTAPI, hCtrl AS LONG, ctrlId AS LONG 
    
      CASE  %WM_SETCURSOR (or whenever) 
         GetCursorPos     pt 
         ScreenToClient   CBHNDL, pt 
         hCtrl          = ChildWindowfromPointApi (CBHNDL, pt) ' See notes
         IF ISTRUE hCtrl THEN 
             IF hCtrl = CBHNDL THEN 
                ' cursor is within screen boundaries, but not over a control 
             ELSE
                  CtrlId = GetDlgCtrlId (hCtrl)     ' control ID 
             END IF 
         ELSE 
            cursor is not within the boundaries of screen 'CBHNDL'
            which can occur if the mouse has been captured and dragged off. 
         END IF
    Use of this DECLARE requires PBCC 4+ or PB/WIN 8+ (support for multiple procedures with shared ALIAS).

    If older compiler - or you just feel like it - you can use this declare (included in Win32API.INC) and this call:
    Code:
     
    DECLARE FUNCTION ChildWindowFromPoint LIB "USER32.DLL" ALIAS "ChildWindowFromPoint" _ 
            (BYVAL hwndParent AS DWORD, BYVAL x AS LONG, BYVAL y AS LONG) AS DWORD
    
         .......
    
        hCtrl = ChildWindowFromPoint (CBHNDL, pt.x, pt.y)
    Use ChildWindowFromPointEx() function to ignore disabled and/or invisible and/or transparent child controls.

    MCM

    Leave a comment:


  • Chris Boss
    replied
    Thanks Dominic,

    I forgot about the WM_NCHITTEST message!

    If WM_NCHITTEST returns back %HTTRANSPARENT then Windows will treat the window like it does not exist (transparent) and all mouse (and cursor) messages get passed through to the window (or dialog) behind it.

    The Static control is obviously returning %HTTRANSPARENT when the %SS_NOTIFY style is not used.

    One could override this, simply by returning something else in WM_NCHITTEST in the subclass routine.

    To demonstrate this, I redid the example I posted and I processed the %WM_NCHITTEST message. The code reverses the behavior of the Label control. The one without %SS_NOTIFY will generate the WM_SETCURSOR message and the one with %SS_NOTIFY will not.

    Code:
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "Win32api.inc"
    GLOBAL hDlg AS DWORD
    GLOBAL StaticWndProc AS DWORD
    GLOBAL hLabel1 AS LONG, hLabel2 AS LONG
    FUNCTION PBMAIN()
        DIALOG NEW PIXELS, 0, "My Dialog",,, 300,100, %WS_OVERLAPPEDWINDOW , TO hDlg
        DIALOG SET ICON hDlg, "face"
        CONTROL ADD TEXTBOX, hDlg, 551, "Button",  10, 10, 50, 50
        CONTROL ADD LABEL, hDlg, 200, "",  70, 10, 70, 60  ', %WS_CHILD or %WS_VISIBLE or %ss_notify
        CONTROL SET COLOR hDlg, 200, %BLACK, %WHITE
        CONTROL HANDLE hDlg, 200 TO hLabel1
        CONTROL ADD LABEL, hDlg, 300, "SS_NOTIFY",  160, 10, 70, 60 , %WS_CHILD OR %WS_VISIBLE OR %SS_NOTIFY
        CONTROL HANDLE hDlg, 300 TO hLabel2
        CONTROL SET COLOR hDlg, 300, %BLACK, %WHITE
        LOCAL hCtrl&
        CONTROL HANDLE hDlg, 200 TO hCtrl&
        StaticWndProc=SetWindowLong(hCtrl&, %GWL_WNDPROC, CODEPTR(SubClassProc))
        CONTROL HANDLE hDlg, 300 TO hCtrl&
        StaticWndProc=SetWindowLong(hCtrl&, %GWL_WNDPROC, CODEPTR(SubClassProc))
        DIALOG SHOW MODAL hdlg CALL DlgProc()
    END FUNCTION
    ' Dialog Callback Function ===============================================================
    CALLBACK FUNCTION DlgProc() AS LONG
        'Console message list
        SELECT CASE CB.MSG
            CASE %WM_SetCursor
                LOCAL iReturn AS LONG
                WINDOW GET ID CB.WPARAM TO iReturn
                IF CB.WPARAM=CB.HNDL THEN
                     DIALOG SET TEXT hDlg, "Mouse Over Dialog"
                ELSE
                     IF iReturn=551 THEN DIALOG SET TEXT hDlg, "Mouse Over TextBox"
                END IF
        END SELECT
    END FUNCTION
    FUNCTION SubClassProc(BYVAL hWnd&, BYVAL Msg&, BYVAL wParam&, BYVAL lParam&) AS LONG
         SELECT CASE Msg&
              CASE %WM_NCHITTEST
                   IF hWnd&=hlabel2 THEN
                        FUNCTION=%HTTRANSPARENT
                   ELSE
                        FUNCTION=%HTNOWHERE
                   END IF
                   EXIT FUNCTION
              CASE %WM_SETCURSOR
                   DIALOG SET TEXT hDlg, "Mouse Over Label"
              CASE ELSE
         END SELECT
         FUNCTION=CallWindowProc(StaticWndProc, hWnd&, Msg&, wParam&, lParam&)
    END FUNCTION

    Leave a comment:


  • Dominic Mitchell
    replied
    When a window class is Static like this, Windows treats the control as if it does not exist as
    far as the mouse is concerned. The mouse is viewed as moving over the parent dialog, rather than
    the control.
    Chris, your post really does not explain what is going on here.

    The behaviour of a control is largely determined by how its window procedure is coded and to a lesser
    extent its class styles. The return values of certain window management and creation messages also
    play a key role. This means that a control does not have to belong to the Static class to exhibit the
    behaviour Gary is seeing.

    The key to all of this is the WM_NCHITTEST and WM_SETCURSOR messages.

    The WM_SETCURSOR message is one of those messages in Windows(OS) that bubbles up the window hierarchy.

    The values returned by DefWindowProc or the values a programmer chooses to return when the
    WM_NCHITTEST message is received determines how a window behaves in response to mouse actions.
    A return value of HTTRANSPARENT makes the window invisible to mouse actions. HTCLIENT means the mouse
    is over the client area of the window and that it will process mouse actions.

    First, let us look at what happens when the mouse is moved over a label that has the SS_NOTIFY style.
    Note: in this and the second case, the mouse is not captured.

    Note: Window procedure in the diagrams refers to the actual window procedure that is registered for
    the class(Static aka label and dialog), not the obfuscation added by the dialog engine and DDT.

    To understand the diagrams, read them as the following example for the first one:

    The operating system(OS) sends the WM_NCHITTEST message to the window procedure for the label.
    The window procedure then returns a value of HTTRANSPARENT.

    Case 1: label(SS_NOTIFY)

    The operating system sends the WM_NCHITTEST message to the label.
    Code:
                  OS                 
                  |                  
                 \|/                 
            WM_NCHITTEST(message)  
                  |
            Label Window procedure
                  | 
            HTTRANSPARENT(return value)
    The OS then sends the message to the underlying window, the parent dialog, in this case.

    Code:
                  OS                   
                  |                    
                 \|/                   
            WM_NCHITTEST(message)    
                  |
            Dialog Window procedure
                  | 
            HTCLIENT(return code)
    The OS then sends WM_SETCURSOR message to the dialog.

    Code:
                    OS                      
                    |                       
                   \|/                      
              WM_SETCURSOR(message)       
                    |
             Dialog Window procedure
                    |
                FALSE(return value)  
                    | 
              DefWindowProc  <--- sets the cursor the registered 
                                  class cursor(IDC_ARROW)
    Case 2: label(~SS_NOTIFY)

    The following simplified diagram shows what happens when the mouse is moved over a
    label that does not have the SS_NOTIFY style.

    The operating system sends the WM_NCHITTEST message to the label.
    Code:
                  OS                 
                  |                  
                 \|/                 
            WM_NCHITTEST(message)  
                  |
            Label Window procedure
                  | 
            HTCLIENT(return code)
    The OS then sends WM_SETCURSOR message to the label.

    Code:
                        OS                      
                        |                       
                       \|/                      
                  WM_SETCURSOR(message)
                        |
                 Label Window procedure
                        |
                  FALSE(return value)  
                        | 
                   DefWindowProc  <--- before processing the message, 
                        |              DefWindowProc passes it to the
                        |              parent(the dialog) of the label.
                       \|/                                                                                                                                             
                 WM_SETCURSOR(message)
                        |
                Dialog Window procedure   
                        |
       _________________|_________________________
      |                                           |
      |                                           |
    FALSE(return value)                         TRUE(return value)
        |                                         |
    DefWindowProc <---+                     DefWindowProc <---+ 
                      |                                       |
    sets the cursor the registered         does no further processing. This means that
    class cursor for labels(IDC_ARROW)     a call to the SetCursor functions will set
                                           the shape of the cursor over the label.

    Please note, it is a coincidence that the registered class cursors for the dialog
    class(#32770) and the static class are both the same(IDC_ARROW).


    By the way, I started working on the post I promised on messages, but had to put it on hold
    because Phoenix3.0 is taking up all my time.
    Last edited by Dominic Mitchell; 6 Mar 2009, 07:06 PM.

    Leave a comment:


  • Michael Mattias
    replied
    You are missing my point, Gary.

    The point is, the documentation for WINDOW GET ID says it will return something. It is not qualified as to when you may call it or with what kind of control handle is required, in fact the doc says...
    Remarks
    The WINDOW statement may be used with any type of window in your program, including a Control or Dialog...
    If you are passing a valid window or control handle to WINDOW GET ID and you are NOT getting back the ID of that control, then either WINDOW GET ID is broken or the documentation is in error. Pick one.

    MCM

    Leave a comment:


  • Gary Beene
    replied
    Kev,

    Yes, it's a good idea. I was actually doing that in my "real" test code. I just cut the code down so I wouldn't feel like I was posting a book. I could see the results, I just couldn't find any documentation that led to me expect those results!

    Chris,
    Thanks for the information. What you told me is one of the reasons I curse windows almost daily - information that comes piece meal - usually from the hands of someone helping out, rather than from any documentation. It would seem to me that the action of a static control (label) with %ss_notify would have gotten more lines of documentation than what MSDN wrote:

    "Sends the parent window STN_CLICKED, STN_DBLCLK, STN_DISABLE, and STN_ENABLE notification messages when the user clicks or double-clicks the control."
    Michael,
    And yes, %ss_notify is just a value. It's what the controls do with the value that is the mystery to solve.

    Having gone back and read the static control and mouse sections as MSDN, I still didn't see anything that would have forewarned me of this specific problem.

    I know I can't expect documentation to tell me everything I need to know, but exact response of each and every common control seems like a reasonable expectation.

    Especially now that the world has been programming with messages for a very long time, I'm always surprised that "gotchas" (or got me's, to be exact) are all over the documentation. Every time I have to ask a question on the forum, that I think MS should have documented, it irks me. It takes my time and your time - which is probably replicated by thousands? of programmer every day!

    Leave a comment:


  • Kev Peel
    replied
    Gary,

    It would be better for your CPrint() function to also have the wParam and lParam values in the output, - then you may have spotted the problem immediately.
    I use similar code to CPrint() in my apps and tend to output function name and all parameters (plus any additional data), it's great for debugging with "real time" info.

    Leave a comment:


  • Chris Boss
    replied
    Mouse 101:

    The label control (when not using the SS_NOTIFY style) is considered a non-user input control. The label control is actually of the STATIC window class (which also includes the PB Graphic control, Image Control, Icon control).

    When a window class is Static like this, Windows treats the control as if it does not exist as far as the mouse is concerned. The mouse is viewed as moving over the parent dialog, rather than the control.

    This means the WM_SETCURSOR message is being generated for the Dialog and not the control.

    Basically Windows treats the Label control like it does not exists to the mouse, so WM_SETCURSOR and other mouse related messages won't be generated for the Label.

    This is why WINDOW GET ID returns zero (the dialog has no ID).

    Here is some code which demonstrates this. It even subclasses the label controls to see if they get WM_SETCURSOR (this message is a forwarded message, where the control forwards it to the parent to let it set the cursor first).

    Code:
    #COMPILE EXE
    #DIM ALL
    #INCLUDE "Win32api.inc"
    GLOBAL hDlg AS DWORD
    GLOBAL StaticWndProc AS DWORD
    FUNCTION PBMAIN()
        DIALOG NEW PIXELS, 0, "My Dialog",,, 300,100, %WS_OVERLAPPEDWINDOW , TO hDlg
        DIALOG SET ICON hDlg, "face"
        CONTROL ADD TEXTBOX, hDlg, 551, "Button",  10, 10, 50, 50
        CONTROL ADD LABEL, hDlg, 200, "",  70, 10, 70, 60  ', %WS_CHILD or %WS_VISIBLE or %ss_notify
        CONTROL SET COLOR hDlg, 200, %BLACK, %WHITE
        CONTROL ADD LABEL, hDlg, 300, "SS_NOTIFY",  160, 10, 70, 60 , %WS_CHILD OR %WS_VISIBLE OR %SS_NOTIFY
        CONTROL SET COLOR hDlg, 300, %BLACK, %WHITE
        LOCAL hCtrl&
        CONTROL HANDLE hDlg, 200 TO hCtrl&
        StaticWndProc=SetWindowLong(hCtrl&, %GWL_WNDPROC, CODEPTR(SubClassProc))
        CONTROL HANDLE hDlg, 300 TO hCtrl&
        StaticWndProc=SetWindowLong(hCtrl&, %GWL_WNDPROC, CODEPTR(SubClassProc))
        DIALOG SHOW MODAL hdlg CALL DlgProc()
    END FUNCTION
    ' Dialog Callback Function ===============================================================
    CALLBACK FUNCTION DlgProc() AS LONG
        'Console message list
        SELECT CASE CB.MSG
            CASE %WM_SetCursor
                LOCAL iReturn AS LONG
                WINDOW GET ID CB.WPARAM TO iReturn
                IF CB.WPARAM=CB.HNDL THEN
                     DIALOG SET TEXT hDlg, "Mouse Over Dialog"
                ELSE
                     IF iReturn=551 THEN DIALOG SET TEXT hDlg, "Mouse Over TextBox"
                END IF
        END SELECT
    END FUNCTION
    FUNCTION SubClassProc(BYVAL hWnd&, BYVAL Msg&, BYVAL wParam&, BYVAL lParam&) AS LONG
         SELECT CASE Msg&
              CASE %WM_SETCURSOR
                   DIALOG SET TEXT hDlg, "Mouse Over Label"
              CASE ELSE
         END SELECT
         FUNCTION=CallWindowProc(StaticWndProc, hWnd&, Msg&, wParam&, lParam&)
    END FUNCTION
    Last edited by Chris Boss; 6 Mar 2009, 02:41 PM.

    Leave a comment:


  • Gary Beene
    replied
    Michael,

    Yes, and on my website examples I use the named constants. It's always a better way to go for production or long-lived code.

    Today, I just had the -11 code handy and used it.


    On the question of Window Get ID vs %ss_notify:

    Since GetDlgCtrlID and Window Get ID both give the same result - won't work without %ss_notify - I had not focussed on Window Get ID as the culprit.

    In WINDOW, Help says "The WINDOW statement may be used with any type of window in your program, including a Control or Dialog", which didn't leave much room to wonder if it should work.

    Did you mean to hint that I missed something in the Help on WINDOW?

    It would seem that whatever is the problem with my code, it's affecting two windows functions, not a problem with just one.

    Leave a comment:


  • Michael Mattias
    replied
    What SS_NOTIFY does or does not do is immaterial to your question.

    It's what WINDOW GET ID does or does not do.

    However, if you are not over a control when WM_SETCURSOR notification message is sent, unless you added a Window ID yourself (in sample code you didn't) you should get back zero.

    (Add ID: SetWindowLong hWnd, %GWL_ID, idvalue)
    Last edited by Michael Mattias; 6 Mar 2009, 02:12 PM.

    Leave a comment:


  • Michael Mattias
    replied
    >GetStdHandle(-11&)

    GetStdHandle (%STD_OUTPUT_HANDLE) is a little easier to understand, isn't it?

    Well, perhaps not this afternoon, but six months from now you will look at this code and be wondering what the heck "-11&" means....

    Leave a comment:


  • Gary Beene
    started a topic Window Get ID problem

    Window Get ID problem

    In the app below, Window Get ID will not return the Control ID of a label unless I include the %ss_notify as a style for the label.

    But Help says that %ss_notify will allow a label to receive %STN_CLICKED and %STN_DBLCLK notification messages. It doesn't say anything about needing %ss_notify for Window Get ID to work.

    Does anyone know more about this? What else does %ss_notify enable, or disable (by its absence)?

    And BTW, GetDlgCtrlID has the same result as Window Get ID - both require %ss_notify for the return value to be the control ID.

    Code:
    #Compile Exe
    #Dim All
    #Include "Win32api.inc"
    
    Global hDlg As Dword
    
    Function PBMain()
        Dialog New Pixels, 0, "My Dialog",,, 100,100, %WS_OverlappedWindow , To hDlg
        Dialog Set Icon hDlg, "face"
        Control Add TextBox, hDlg, 551, "Button",  10, 10, 50, 50
        Control Add Label, hDlg, 200, "Button",  70, 10, 10, 60, ' %ss_notify
        Control Set Color hDlg, 200, %White, %White
        Dialog Show Modal hdlg Call DlgProc()
    End Function
    
    ' Dialog Callback Function ===============================================================
    
    CallBack Function DlgProc() As Long
        'Console message list
        Static iMsgCount&
        CPrint Str$(iMsgCount&)+ " " + WinMsg(Cb.Msg)
        Incr iMsgCount&
    
        'Respond to messagess
        Static iCount&
        Dim Style As Long, iReturn As Long, hTemp As Dword, temp$
        Select Case Cb.Msg
            Case %WM_SetCursor
                Incr iCount
                Window Get Id Cb.WParam To iReturn
                'print:   control id + handle of window under mouse + dialog handle
                CPrint "SetCursor: " & Str$(iReturn) & " " & Str$(Cb.WParam) &  " " & Str$(hDlg)
                Select Case iReturn
                    Case 200
                        CPrint Str$(iCount) + " H-setcursor"
                End Select
        End Select
    End Function
    
    Sub CPrint (SOut As String)    'Semen Matusovski's CPrint code:
        Static hConsole As Long, cWritten As Long
        If hConsole = 0 Then AllocConsole: hConsole = GetStdHandle(-11&)
        WriteConsole hConsole, ByCopy sOut + $CrLf, Len(sOut) + 2, cWritten, ByVal 0&
    End Sub
    Last edited by Gary Beene; 6 Mar 2009, 02:02 PM.
Working...
X