News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

IsChildWindow (For simple identification. No transversals)

Started by guga, April 23, 2025, 11:15:49 PM

Previous topic - Next topic

guga

Hi guys, i made a simpler variation of mine IsChild function here. This is also based on the core that exists in User32.dll but tweaked to ensure a more wide scenario (multiple desktops, for example).


;;
  IsChildWindow
  Determines whether a window is a child window by checking for a valid parent
  and the WS_CHILD style.

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

  Return Value:
    Returns TRUE (1) if hWnd is a child window (has WS_CHILD style and a valid parent).
    Returns FALSE (0) if hWnd is not a child window (e.g., top-level, popup, invalid handle,
    or no parent).

  Remarks:
    - General-purpose function for checking child window status, similar to IsChildForWindowPos
      but with full validations for standalone use.
    - Uses GetAncestor to retrieve the immediate parent window.
    - Validates window handles, thread desktop, and default desktop for robustness.
    - Checks WS_CHILD style (and not WS_POPUP) via GetWindowLongA.
    - Unlike IsChild, does not check for a specific parent-child or descendant relationship,
      making it simpler and single-argument.
    - Preserves ECX and EDX registers as per calling convention.
    - Suitable for scenarios like GetWindowPosEx (to choose GetClientRect vs. GetWindowRect)
      or other checks for child window status.

  Author: Gustavo Trigueiros (aka: Beyond2000!)
  Build Date: April 2025 (v1.0)
;;

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

    ; Validate input windows
    ; Ensures hWnd is a valid window to prevent crashes or invalid operations
    call 'USER32.IsWindow' D@hWnd
    On eax = 0, ExitP

    ; Get parent or owner window
    ; GA_PARENT retrieves the immediate parent (e.g., dialog for a button)
    ; Returns NULL for top-level or popup windows, which aren't children
    Call 'USER32.GetAncestor' D@hWnd, &GA_PARENT
    On eax = 0, ExitP
    mov D@hWndParent eax

    ; Validate parent window handle
    ; Ensures the parent is a valid window, as GetAncestor may return a stale handle
    call 'USER32.IsWindow' D@hWndParent ; is the parent also a window ?
    On eax = 0, ExitP

    ; Validate thread's desktop
    ; Ensures the current thread has a valid desktop context
    call 'kernel32.GetCurrentThreadId'
    call 'user32.GetThreadDesktop' eax
    On eax = 0, ExitP

    ; Validate default desktop window
    ; Ensures the desktop window is accessible, for robustness in multi-desktop scenarios
    call 'USER32.GetDesktopWindow'
    On eax = 0, ExitP

    ; Check window style to determine if hWnd is a child
    ; GetWindowLongA retrieves GWL_STYLE, which includes WS_CHILD or WS_POPUP flags
    Call 'USER32.GetWindowLongA' D@hWnd, &GWL_STYLE
    ; Mask style to check for WS_CHILD (and not WS_POPUP)
    ; WS_CHILD indicates a child window; WS_POPUP indicates a non-child
    and eax (&WS_POPUP+&WS_CHILD)
    If eax = &WS_CHILD
        ; hWnd has WS_CHILD and a valid parent, so it's a child window
        mov eax &TRUE
    Else
        ; hWnd is not a child window (e.g., popup, top-level, or invalid style)
        xor eax eax
    End_If

EndP
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

zedd

What about for when a window on the desktop has a dialog box open which has several controls. How does it deal with "grandchildren" Windows? (Child of a child), or even (child of a child of a child)?  :biggrin:  Of course I don't use rosasm, but just wondering if you had considered those scenarios.
While GOOGLE is not always your friend, Google Search is very useful sometimes.  :cool:

guga

Hi Zedd

I did that yesterday. You can identify the child inside a child of a child and so on. Here is

https://masm32.com/board/index.php?topic=12742.0

What u have to do is basically search for it´s parents.

The 1st thing is work with 2 handles (as in the MS Ischild). One for the Parent windows (any parent) and other for the window itself from where u want to identify.

I started with 2 basic steps

I - Check for Validations

1 - Check if the parent handle is valid. If its not, then you exit the function.
    call 'USER32.IsWindow' D@hWndParent
    On eax = 0, ExitP
2 - Check if the handle of the window itself is working. If not, also exit the function.
    call 'USER32.IsWindow' D@hWnd
    On eax = 0, ExitP
3 - Validate the thread´s desktop to see if the window you want to check is, in fact, attached to anything in your desktop without any problems. This is how IsChild do internally, btw
    call 'kernel32.GetCurrentThreadId'
    call 'user32.GetThreadDesktop' eax
    On eax = 0, ExitP
4 - Finally try to validate the desktop window, to also see if everything is ok.
    ; Validate default desktop window
    call 'USER32.GetDesktopWindow'
    On eax = 0, ExitP

II - Start analyzing the window transversely, trying to hunt it´s parents.

1 - I 1st created a local variable containing the copy of the input hWnd, and named it as CurrentWnd, meaning this will be updated to be checked on each loop

Immediately inside the loop, the 1st thing to do is get the style of the window. Any child windows must contains, at least WS_CHILD flag. After you get the style you must AND the returned value from GetWindowLong (with GWL_STYLE) with the OR of WS_POPUP+WS_CHILD. This is the exact same behavior as the Api (ISChild) inside user32.dll
        ; 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 the resultant AND / OR operation is WS_CHILD only, then you are quite sure that your windows is a Child one. If the resultant comparison is not WS_CHILD, then, you can exit the loop (and the function with FALSE because the handle does not belongs to child.

2 - If the comparison is ok, you start getting the parent of the "CurrentWnd" Variable. (Remember, you are still inside the loop) If the returned value from GetParent is 0 your hWnd is also not a child and you can exit the function with FALSE.
If the resultant value (of CurrentWnd) is the same as the input hWndParent, then you found your child, and you can return TRUE, since the handle you are trying to analyze is definitely a child.
If the resultant value (of CurrentWnd) is not the same as the inputted Parent (hWndParent), then, you need to continue the loop

The loop routine is at:
    .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


If after all iterations, the loops ends without finding anything, then, this is definitely not a child.

The whole function is this (The information) is on the link i posted.
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
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

Btw, i had to create this thing for an update i made on GetWindowPos. I updated an old RosAsm function, and now it is correctly retrieving the values in logical pixels. I also emulated a routine for DPI awareness. I tested all these functions during last night. It seems they are working, specially the GetWindowPos because my old version wasn´t returning the proper values. https://masm32.com/board/index.php?topic=12744.msg138665#msg138665
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com