2

All windows file explorer windows show up in the Task manager in type of sub-heading all under explorer.exe, however, I would like to close individual file explorer windows by the name on the content of the window. I am trying to close windows that show connectivity errors after connecting to a VPN such as the one below:

TaskMgr & Windows Explorer

When I use the following command I get info about it but PS gives no indication as to the name of the window (e.g. "Restoring Network Connections"). How do I get and use Window info?

  • Get-Process -name explorer

I also tried the following command, however, it only shows 1 of the window objects when I have over 10 open that match the criteria "*HFS*"

  • (New-Object -ComObject Shell.Application).Windows() | Where-Object{$_.LocationName -like "*HFS*" }

ComObject Shell

zett42
  • 25,437
  • 3
  • 35
  • 72
Lawrence
  • 43
  • 5
  • 1
    `Shell.Application.Windows` only gives you the actual explorer windows, but what you want to find are message boxes. You may call `FindWindow` using PInvoke or UI automation API to find these windows. – zett42 Aug 29 '22 at 21:53
  • I did some experiments - `FindWindow` et al don't work for Explorer because it uses DirectUI. .NET UI automation API looked promising but seems to be unreliable. I've read that it uses old UIA2 API which has some issues with newer applications. For using UIA3 we need a wrapper, like WinAppDriver (needs installation) or [FlaUI](https://github.com/FlaUI/FlaUI) (should work without installation, using `Add-Type` to import the assemblies). I can find the message box using Inspect.exe, so UI automation approach _should_ work. – zett42 Aug 30 '22 at 09:33
  • Hey, is my answer working for you? If yes, please click the accept ✓ button to the left of the answer to mark this question as solved. Thanks! – zett42 Sep 02 '22 at 10:12
  • 1
    Thank you very much for providing an answer! I tried to implement it but got lost as the answer is beyond my PS skill level. I am going to keep trying as I learn more. – Lawrence Feb 02 '23 at 15:08
  • 1
    That's unfortunate, but I don't think there is a simpler way. Feel free to post a new question with what you have tried so far and notify me so I can have a look. – zett42 Feb 02 '23 at 19:00

1 Answers1

3

I got this working using FlaUI, a .NET wrapper for UIA2 and UIA3 automation API. Although the .NET framework already includes a UI automation API, it uses UIA2 only, which in my experiments appeared to be unreliable for automating Explorer. Only UIA3 seems to work reliably.

First step is to get the FlaUI assemblies. I mostly followed the steps from this answer for using dotnet CLI to download the FlaUI assemblies from NuGet including all dependencies.

A prerequisite for using the dotnet CLI is an installation of a .NET SDK. I choose .NET 6.0 x64, but it should work with .NET 4.x SDK as well.

Once you have the .NET SDK installed, open a PowerShell console in the directory where your PowerShell script is located and then run the following commands:

Set-Location (New-Item -Type Directory assemblies)
# Create a dummy project ("-f netstandard2.0" to create a compatible .cs file)
dotnet new classlib -f netstandard2.0
# Target .NET 4.8 for compatibility with both PowerShell 5.1 and PowerShell (Core) 7+
(Get-Content assemblies.csproj).Replace('netstandard2.0', 'net48') | Set-Content assemblies.csproj
# Download the assemblies from NuGet
dotnet add package FlaUI.UIA3 -v 3.2.0
# Copy all assemblies including dependencies into Release directory
dotnet publish -c Release

You might want to check if there is a newer version of FlaUI on NuGet and replace the version number in the dotnet add package line above.

You should now have the following assemblies in the "\assemblies\bin\Release\net48\publish" folder:

FlaUI.Core.dll
FlaUI.UIA3.dll
Interop.UIAutomationClient.dll

These are the only files you need, you can move these wherever your script can reach them and delete the remaining files from the "assemblies" folder.

Actual code to automate Explorer:

# Load the FlaUI assembly
Add-Type -Path $PSScriptRoot\assemblies\bin\Release\net48\publish\FlaUI.UIA3.dll

# Create UIA3 automation instance
$automation = [FlaUI.UIA3.UIA3Automation]::new()

# Attach to running Explorer process
$app = [FlaUI.Core.Application]::Attach( (Get-Process explorer).Id )

# For each top-level window of Explorer
foreach( $wnd in $app.GetAllTopLevelWindows( $automation ) ) {

    # For each Explorer dialog window (message box)
    # You may also conditionalize on the window title: $_.Name -eq 'Restoring Network Connections'
    foreach( $dlg in $wnd.FindAllChildren().Where{ $_.ClassName -eq '#32770' } ) {
        "----- Found explorer dialog: -----"
        $dlg  # debug output

        # Find text control that contains the text '\\HFS'
        $child = $dlg.FindAllDescendants().Where{ $_.ControlType -eq 'Text' -and $_.Name -like '*\\HFS*' }
        if( $child ) {
            "----- Dialog has required child: -----"
            $child  # debug output

            "`n>>> Closing dialog now <<<"
            $dlg.Patterns.Window.Pattern.Close()
        }
    }
}

I have tested it with a "path not found" error message but it should work with your error message as well. Let me know how it works out for you.


Additional resources:

  • A very useful tool for exploring the UI automation hierarchy is Inspect.exe. If you see something in Inspect, you should be able to automate it.
  • Here are a few more examples of how to use FlaUI with PowerShell.
zett42
  • 25,437
  • 3
  • 35
  • 72
  • 2
    As an aside: I haven't worked with either utility, but he `inspect.exe` link states that it is a legacy tool and that the recommended successor is [Accessibility Insights for Windows](https://accessibilityinsights.io/docs/windows/overview/). – mklement0 Aug 30 '22 at 15:26
  • 1
    inspect.exe is still better when you need to work with UI automation for full-screen windows. – Carsten May 31 '23 at 14:54