The MASM Forum

Projects => Rarely Used Projects => RosAsm => Topic started by: guga on April 22, 2025, 09:19:52 PM

Title: A more robust IsChild function
Post by: guga on April 22, 2025, 09:19:52 PM
Hi guys, i made a more robust InChild function. In fact this is the core that exists in User32.dll but tweaked to ensure a more wide scenario (multiple desktops, for example).

References:
ReactOS IsChild (https://doxygen.reactos.org/db/d26/win32ss_2user_2user32_2windows_2window_8c_source.html)
ReactOS GetThreadDesktopWnd (https://doxygen.reactos.org/d0/d92/win32ss_2user_2user32_2misc_2misc_8c_source.html)


;;
    IsChild
    Determines whether a specified window is a child or descendant of another window.

    Arguments:
        hWndParent (in): Handle to the potential parent window.
        hWnd (in): Handle to the window to test.

    Return Value:
        If hWnd is a child or descendant of hWndParent, returns TRUE (1).
        Otherwise, returns FALSE (0), including cases where:
            - hWnd or hWndParent is invalid (not a window).
            - The thread's desktop or default desktop window is inaccessible.
            - hWnd is not a child window (lacks WS_CHILD style or has WS_POPUP).
            - The parent chain ends without reaching hWndParent.

    Remarks:
        -   This function traverses the parent chain of hWnd using GetParent until it finds
            hWndParent or reaches a non-child window or NULL.
        -   A child window must have the WS_CHILD style and not have the WS_POPUP style.
        -   The function performs validation checks:
        -   Verifies hWnd and hWndParent are valid windows using IsWindow.
        -   Ensures the thread has a valid desktop using GetThreadDesktop.
        -   Ensures the default desktop window is accessible using GetDesktopWindow.
        -   These desktop checks are conservative, ensuring the window hierarchy is valid
            in the thread's context, though GetDesktopWindow is typically sufficient for
            user-mode applications.
        -   The function preserves ECX and EDX registers, as Windows API calls may modify
            these volatile registers.
        -   Used by GetWindowPosEx to determine if a window is a child for coordinate
            calculations.

 Usage Example:
   [WINPOS:
    WINPOS.cx: D$ 0
    WINPOS.cy: D$ 0
    WINPOS.width: D$ 0
    WINPOS.height: D$ 0]

   ; Check if a dialog control is a child of its parent dialog
   call 'USER32.GetDlgItem' D@hDlg, 156  ; Get control handle
   mov ebx eax                          ; Save control handle
   call 'USER32.GetAncestor' ebx, &GA_PARENT  ; Get parent dialog
   call IsChild eax, ebx
   If eax = &TRUE
       ; Control is a child, use client coordinates in GetWindowPosEx
       call GetWindowPosEx ebx, WINPOS
   End_If

    Author: Gustavo Trigueiros (aka: Beyond2000!/Guga)
    Build Date: May 2012 (v1.0), Updated April 2025 (v1.1)
    Notes:
        -   Adapted from system-level code, replacing GetThreadDesktopWnd with standard
            GetDesktopWindow for user-mode compatibility.
        -   Added GetThreadDesktop for additional thread desktop validation, inspired by
            original system-level implementation.
        -   Corrected style check to use bitwise OR (WS_POPUP | WS_CHILD) for reliable
            child window detection.
;;

Proc IsChild:
    Arguments @hWndParent, @hWnd
    Local @CurrentWnd, @Style
    Uses ecx, edx

    ; Validate input windows
    call 'USER32.IsWindow' D@hWndParent
    On eax = 0, ExitP
    call 'USER32.IsWindow' D@hWnd
    On eax = 0, ExitP

    ; Validate thread's desktop
    call 'kernel32.GetCurrentThreadId'
    call 'user32.GetThreadDesktop' eax
    On eax = 0, ExitP

    ; Validate default desktop window
    call 'USER32.GetDesktopWindow'
    On eax = 0, ExitP

    ; Initialize CurrentWnd = hWnd
    mov eax D@hWnd | mov D@CurrentWnd eax
    ; Traverse parent chain
    .While D@CurrentWnd <> 0
        ; Get window style
        call 'USER32.GetWindowLongA' D@CurrentWnd, &GWL_STYLE

        ; Check if window is a child: (style & (WS_POPUP | WS_CHILD)) == WS_CHILD
        and eax (&WS_POPUP+&WS_CHILD)   ; Bitwise OR for mask
        .If eax = &WS_CHILD
             ;  Get parent window
            Call 'USER32.GetParent' D@CurrentWnd
            mov D@CurrentWnd eax
            ; Check if parent is NULL
            On eax = 0, ExitP
            ; Check if parent matches hWndParent
            If eax = D@hWndParent
                mov eax &TRUE
                ExitP
            End_If
        .Else
            ; Not a child window, stop traversal
            xor eax eax
            ExitP
        .End_If

    .End_While

    ; Default return FALSE
    xor eax eax

EndP