CmdPal: Window Walker - detect UWP apps and prevent "Unresponsive" tag on them (#41938)

## Summary of the Pull Request

This PR introduces detection of UWP processes and skips evaluation of
the Process.Responding property for them.

The Process.Responding property is only reliable for Win32 apps. For UWP
processes, relying on this property can produce incorrect results. With
this change, UWP apps will no longer be flagged with an Unresponsive tag
due to misleading property values.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #38353
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2025-09-23 23:20:06 +02:00
committed by GitHub
parent f995e414b7
commit 0edf06bb5f
7 changed files with 194 additions and 9 deletions

View File

@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
internal static class ProcessPackagingInspector
{
#pragma warning disable SA1310 // Field names should not contain underscore
private const int ERROR_INSUFFICIENT_BUFFER = 122;
private const int APPMODEL_ERROR_NO_PACKAGE = 15700;
#pragma warning restore SA1310 // Field names should not contain underscore
/// <summary>
/// Inspect a process by PID and classify its packaging.
/// </summary>
public static ProcessPackagingInfo Inspect(int pid)
{
var hProcess = NativeMethods.OpenProcess(ProcessAccessFlags.QueryLimitedInformation, false, pid);
using var process = new SafeProcessHandle(hProcess, true);
if (process.IsInvalid)
{
return new ProcessPackagingInfo(
pid,
ProcessPackagingKind.Unknown,
HasPackageIdentity: false,
IsAppContainer: false,
PackageFullName: null,
LastError: Marshal.GetLastPInvokeError());
}
// 1) Check package identity
var hasPackage = TryGetPackageFullName(process, out var packageFullName, out _);
// 2) If packaged, check AppContainer -> strict UWP
var isAppContainer = false;
int? tokenErr = null;
if (hasPackage)
{
isAppContainer = TryIsAppContainer(process, out tokenErr);
}
var kind =
!hasPackage ? ProcessPackagingKind.UnpackagedWin32 :
isAppContainer ? ProcessPackagingKind.UwpApp :
ProcessPackagingKind.PackagedWin32;
return new ProcessPackagingInfo(
pid,
kind,
HasPackageIdentity: hasPackage,
IsAppContainer: isAppContainer,
PackageFullName: packageFullName,
LastError: null);
}
private static bool TryGetPackageFullName(SafeProcessHandle hProcess, out string? packageFullName, out int? lastError)
{
packageFullName = null;
lastError = null;
uint len = 0;
var rc = NativeMethods.GetPackageFullName(hProcess, ref len, null);
if (rc == APPMODEL_ERROR_NO_PACKAGE)
{
return false; // no package identity
}
if (rc != ERROR_INSUFFICIENT_BUFFER && rc != 0)
{
lastError = rc;
return false; // unexpected error
}
if (len == 0)
{
return false;
}
var sb = new StringBuilder((int)len);
rc = NativeMethods.GetPackageFullName(hProcess, ref len, sb);
if (rc == 0)
{
packageFullName = sb.ToString();
return true;
}
lastError = rc;
return false;
}
private static bool TryIsAppContainer(SafeProcessHandle hProcess, out int? lastError)
{
lastError = null;
if (!NativeMethods.OpenProcessToken(hProcess, TokenAccess.TOKEN_QUERY, out var token))
{
lastError = Marshal.GetLastPInvokeError();
return false; // can't decide; treat as not-UWP for classification
}
using (token)
{
if (!NativeMethods.GetTokenInformation(
token,
TOKEN_INFORMATION_CLASS.TokenIsAppContainer,
out var val,
sizeof(int),
out _))
{
lastError = Marshal.GetLastPInvokeError();
return false;
}
return val != 0; // true => AppContainer (UWP)
}
}
}