如何获取不含后台应用的任务管理器应用列表?
问题描述
我尝试获取当前正在使用的应用列表,用以下C#代码从Windows设备中检索应用,但返回结果包含Media Player、ApplicationFrameHost、Settings这些在任务管理器「后台应用」标签里的程序,没法把它们和正常应用区分开。请问怎么像任务管理器那样分离应用列表和后台应用?
原C#代码
public enum WindowState { Normal, Minimized, Maximized } public class WindowWrapper : IWin32Window { private IntPtr _handle; private string _title; private WindowState _state; public IntPtr Handle => _handle; public string Title => _title; public WindowState State => _state; public WindowWrapper(IntPtr handle, string title, WindowState state) { _handle = handle; _title = title; _state = state; } } public class NativeMethods { public static readonly IntPtr GWL_STYLE = new IntPtr(-16); public static readonly UInt64 WS_VISIBLE = 0x10000000L; public static readonly UInt64 WS_BORDER = 0x00800000L; public static readonly UInt64 DESIRED_WS = WS_BORDER | WS_VISIBLE; public delegate bool EnumWindowsCallback(IntPtr hwnd, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] private struct WINDOWPLACEMENT { public int length; public int flags; public int showCmd; public POINT ptMinPosition; public POINT ptMaxPosition; public RECT rcNormalPosition; } [StructLayout(LayoutKind.Sequential)] private struct POINT { public int x; public int y; } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } public static List<WindowWrapper> GetAllWindows() { List<WindowWrapper> windows = new List<WindowWrapper>(); EnumDesktopWindows(IntPtr.Zero, (hwnd, lParam) => { if ((GetWindowLongA(hwnd, GWL_STYLE) & DESIRED_WS) == DESIRED_WS && !IsIconic(hwnd) && IsWindowVisible(hwnd) && IsWindow(hwnd) && hwnd != IntPtr.Zero) { StringBuilder buffer = new StringBuilder(255); GetWindowText(hwnd, buffer, buffer.Capacity); string title = buffer.ToString(); WINDOWPLACEMENT placement = new WINDOWPLACEMENT(); placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); GetWindowPlacement(hwnd, ref placement); WindowState state = WindowState.Normal; int processId; GetWindowThreadProcessId(hwnd, out processId); var process = Process.GetProcessById(processId); if (IsProcessInForeground(processId)) { state = WindowState.Normal; // Consider it as normal if it's in the foreground } else { switch (placement.showCmd) { case 1: state = WindowState.Normal; break; case 2: state = WindowState.Minimized; break; case 3: state = WindowState.Maximized; break; } } WindowWrapper wnd = new WindowWrapper(hwnd, title, state); if (!string.IsNullOrEmpty(wnd.Title) /*&& !string.IsNullOrEmpty(process.MainWindowTitle) */&& !process.HasExited && process.MainWindowHandle == hwnd) { windows.Add(wnd); } } return true; }, IntPtr.Zero); return windows; } private static bool IsProcessInForeground(int processId) { IntPtr foregroundWindow = GetForegroundWindow(); int foregroundProcessId; GetWindowThreadProcessId(foregroundWindow, out foregroundProcessId); return (foregroundProcessId == processId); } [DllImport("user32.dll", SetLastError = true)] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumWindowsCallback lpfn, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsIconic(IntPtr hWnd); [DllImport("user32.dll", SetLastError = true)] private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] private static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern UInt64 GetWindowLongA(IntPtr hWnd, IntPtr nIndex); }
解决方案
任务管理器区分「应用」和「后台应用」的核心是判断窗口是否为用户可见的主应用窗口,而非系统后台服务或辅助窗口。可以通过以下几个关键调整实现:
1. 添加扩展样式相关常量与API
在NativeMethods中新增用于判断主窗口的扩展样式常量,以及获取窗口拥有者的API:
public static readonly IntPtr GWL_EXSTYLE = new IntPtr(-20); public static readonly UInt64 WS_EX_APPWINDOW = 0x00040000L; public static readonly UInt32 GW_OWNER = 4; [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetWindow(IntPtr hWnd, UInt32 uCmd);
2. 修改窗口筛选条件
- 移除
!IsIconic(hwnd):最小化的应用仍属于正常应用,不应被排除 - 新增窗口拥有者判断:排除有父窗口的子窗口/对话框
- 新增
WS_EX_APPWINDOW样式检查:仅保留会显示在任务栏的主应用窗口
修改后的核心判断逻辑:
UInt64 windowStyle = GetWindowLongA(hwnd, GWL_STYLE); bool hasRequiredStyle = (windowStyle & DESIRED_WS) == DESIRED_WS; bool hasNoOwner = GetWindow(hwnd, GW_OWNER) == IntPtr.Zero; UInt64 exStyle = GetWindowLongA(hwnd, GWL_EXSTYLE); bool isMainAppWindow = (exStyle & WS_EX_APPWINDOW) == WS_EX_APPWINDOW; if (hasRequiredStyle && IsWindowVisible(hwnd) && IsWindow(hwnd) && hwnd != IntPtr.Zero && hasNoOwner && isMainAppWindow)
3. 优化进程与窗口关联逻辑
调整GetAllWindows方法,避免进程已退出的异常,同时简化窗口状态判断:
public static List<WindowWrapper> GetAllWindows() { List<WindowWrapper> windows = new List<WindowWrapper>(); EnumDesktopWindows(IntPtr.Zero, (hwnd, lParam) => { UInt64 windowStyle = GetWindowLongA(hwnd, GWL_STYLE); bool hasRequiredStyle = (windowStyle & DESIRED_WS) == DESIRED_WS; bool hasNoOwner = GetWindow(hwnd, GW_OWNER) == IntPtr.Zero; UInt64 exStyle = GetWindowLongA(hwnd, GWL_EXSTYLE); bool isMainAppWindow = (exStyle & WS_EX_APPWINDOW) == WS_EX_APPWINDOW; if (hasRequiredStyle && IsWindowVisible(hwnd) && IsWindow(hwnd) && hwnd != IntPtr.Zero && hasNoOwner && isMainAppWindow) { StringBuilder buffer = new StringBuilder(255); GetWindowText(hwnd, buffer, buffer.Capacity); string title = buffer.ToString(); if (string.IsNullOrEmpty(title)) return true; WINDOWPLACEMENT placement = new WINDOWPLACEMENT(); placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); GetWindowPlacement(hwnd, ref placement); WindowState state = WindowState.Normal; switch (placement.showCmd) { case 1: state = WindowState.Normal; break; case 2: state = WindowState.Minimized; break; case 3: state = WindowState.Maximized; break; } int processId; GetWindowThreadProcessId(hwnd, out processId); try { var process = Process.GetProcessById(processId); if (!process.HasExited && process.MainWindowHandle == hwnd) { WindowWrapper wnd = new WindowWrapper(hwnd, title, state); windows.Add(wnd); } } catch (ArgumentException) { // 进程已退出,直接忽略 } } return true; }, IntPtr.Zero); return windows; }
4. 可选:UWP应用处理
如果需要包含Settings、Media Player这类UWP应用,可通过检查进程的包信息(需引用Windows.ApplicationModel)来区分宿主进程与实际应用:
using Windows.ApplicationModel; // 在获取process后添加 bool isUwpApp = false; try { var packageId = PackageId.FromProcessId(processId); isUwpApp = true; } catch { // 非UWP应用会抛出异常,直接跳过 }
关键说明
WS_EX_APPWINDOW是任务管理器区分主应用窗口的核心标志,只有带该样式的窗口才会显示在任务栏- 排除有窗口拥有者的子窗口,避免把弹窗、对话框误判为独立应用
- 保留最小化窗口的判断逻辑,符合任务管理器的展示规则
内容的提问来源于stack exchange,提问作者Elanchezhiyan
相关产品推荐
相关产品推荐

