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