powershelluser32

Why do I see a boolean value from EnumChildWindows when using this Powershell script?


A friend has a problem on his work PC. There are frequent network issues, after which he sees dozens of error popup windows from File Explorer. He wants a PowerShell script to close them all down without having to click each one individually. I solved the problem in Python but he must have a PowerShell script and I'm a PowerShell novice.

I found this example: https://stackoverflow.com/questions/64469727/powershell-and-winapi-enumwindows-function

and I cobbled together the following script, to help me understand what I need to do.


function Get-ChildWindowHandles {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [IntPtr]$ParentWindowHandle
    )

    $MemberDefinition = @"
// declare the EnumChildWindowsProc delegate type
public delegate bool EnumChildWindowsProc(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
public static extern bool EnumChildWindows(IntPtr hWnd, EnumChildWindowsProc enumProc, IntPtr lParam);
"@

    Add-Type -MemberDefinition $MemberDefinition -Name EnumChildWindowsUtil -Namespace Win32Functions

    # Create a list to act as a receptacle for all the window handles we're about to enumerate
    $ChildWindowHandles = [System.Collections.Generic.List[IntPtr]]::new()

    # Define the callback function
    $callback = {
        param([IntPtr]$handle, [IntPtr]$param) 

        # Copy the window handle to our list
        $ChildWindowHandles.Add($handle)

        # Continue (return $false from the callback to abort the enumeration)
        return $true
    }

    [Win32Functions.EnumChildWindowsUtil]::EnumChildWindows($ParentWindowHandle, $callback, [IntPtr]::Zero)

    return $ChildWindowHandles
}

$com_object = New-Object -com "Shell.Application"
$windows = $com_object.windows()
foreach ($w in $windows) {
    if ($w.Name -eq 'File Explorer') {
        $handles = Get-ChildWindowHandles -ParentWindowHandle $w.HWND
        foreach ($handle in $handles) {
            Write-Output "handle.GetType $($handle.GetType()) handle $($handle)"
        }
    }
}

It returns this:

handle.GetType bool handle True
handle.GetType System.IntPtr handle 591506
handle.GetType System.IntPtr handle 591192
handle.GetType System.IntPtr handle 722370
...

Why is the first element a boolean? I'd be very grateful if someone could explain what I'm doing wrong.

As an aside, why aren't File Explorer windows seen when I try Get-Process | Where-Object {$_.MainWindowTitle} #| Select-Object MainWindowTitle?

Thanks in advance for any help Chris


Solution

  • EnumChildWindows returns a BOOL according to https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumchildwindows:

    BOOL EnumChildWindows(
    ^^^^
      [in, optional] HWND        hWndParent,
      [in]           WNDENUMPROC lpEnumFunc,
      [in]           LPARAM      lParam
    );
    

    That return value isn't being captured in your function so it's being returned implicitly as part of the function's output stream.

    You can capture it with:

    $null = [Win32Functions.EnumChildWindowsUtil]::EnumChildWindows( ... )
    ^^^^^^^
    

    and it'll no longer appear in the function return values.

    Other options to do the same thing are:

    [void] [Win32Functions.EnumChildWindowsUtil]::EnumChildWindows( ... )
    ^^^^^^
    

    or

    [Win32Functions.EnumChildWindowsUtil]::EnumChildWindows( ... ) | Out-Null
                                                                   ^^^^^^^^^^
    

    but $null = is my own personal stylistic preference