You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取同一PID的CDViewer.exe全部打开窗口标题?

解决同一PID下多窗口标题获取问题(以CDViewer.exe为例)

问题根源

你遇到的痛点非常典型:不管是C#里的Process.MainWindowTitle还是PowerShell中的mainWindowTitle属性,都只会返回进程中被标记为“主窗口”的第一个窗口标题。而像Citrix的CDViewer.exe这类进程,会在同一个PID下创建多个顶级窗口,但这些窗口不会被Process类识别为“主窗口”,所以只能抓到第一个。

要获取所有窗口标题,必须绕过Process类的限制,直接调用Windows原生API枚举系统中所有窗口,再筛选出属于目标PID的那些。


C#解决方案

我们需要借助user32.dll里的几个核心API来实现:

  • EnumWindows:枚举系统中所有顶级窗口
  • GetWindowThreadProcessId:获取窗口所属的进程ID
  • GetWindowText:读取窗口的标题文本
  • IsWindowVisible:过滤掉后台隐藏的窗口(可选)

下面是完整的可运行代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

class CitrixWindowLister
{
    // 导入Windows原生API
    [DllImport("user32.dll")]
    private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

    [DllImport("user32.dll")]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll")]
    private static extern bool IsWindowVisible(IntPtr hWnd);

    // 定义枚举窗口的委托类型
    private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    // 存储窗口信息的实体类
    public class WindowDetail
    {
        public IntPtr WindowHandle { get; set; }
        public string Title { get; set; }
        public int ProcessId { get; set; }
    }

    // 核心方法:获取指定PID的所有可见窗口
    public static List<WindowDetail> GetAllWindowsForProcess(int targetPid)
    {
        List<WindowDetail> matchedWindows = new List<WindowDetail>();

        EnumWindows((hWnd, lParam) =>
        {
            // 跳过不可见窗口
            if (!IsWindowVisible(hWnd))
                return true;

            // 检查窗口所属PID是否匹配
            GetWindowThreadProcessId(hWnd, out uint windowPid);
            if (windowPid != targetPid)
                return true;

            // 读取窗口标题
            System.Text.StringBuilder titleBuilder = new System.Text.StringBuilder(256);
            GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity);
            string windowTitle = titleBuilder.ToString().Trim();

            // 只保留有标题的窗口
            if (!string.IsNullOrEmpty(windowTitle))
            {
                matchedWindows.Add(new WindowDetail
                {
                    WindowHandle = hWnd,
                    Title = windowTitle,
                    ProcessId = (int)windowPid
                });
            }

            return true;
        }, IntPtr.Zero);

        return matchedWindows;
    }

    static void Main(string[] args)
    {
        string targetProcessName = "CDViewer";
        Process[] citrixProcesses = Process.GetProcessesByName(targetProcessName);

        foreach (Process proc in citrixProcesses)
        {
            Console.WriteLine($"=== PID: {proc.Id} | 进程名: {proc.ProcessName} ===");
            Console.WriteLine("已找到的窗口标题:");

            List<WindowDetail> windows = GetAllWindowsForProcess(proc.Id);
            foreach (var window in windows)
            {
                Console.WriteLine($"  - {window.Title}");
            }
            Console.WriteLine("----------------------------------------");
        }

        Console.ReadKey();
    }
}

代码说明

  1. 通过EnumWindows遍历所有顶级窗口,用IsWindowVisible跳过后台隐藏窗口(如果需要包含隐藏窗口,可移除这步判断)。
  2. 对每个窗口,用GetWindowThreadProcessId验证所属PID是否匹配目标进程。
  3. 用GetWindowText读取标题,过滤掉空标题的窗口,最后收集所有符合条件的窗口信息并输出。

PowerShell解决方案

PowerShell可以通过Add-Type嵌入C#代码来复用上面的逻辑,实现更简洁的窗口枚举:

function Get-ProcessAllWindows {
    param(
        [Parameter(Mandatory=$true)]
        [int]$ProcessId
    )

    # 嵌入C#代码实现API调用
    Add-Type @"
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;

    public class WindowEnumerator {
        [DllImport("user32.dll")]
        public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll")]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        public static List<string> GetWindowTitles(int pid) {
            List<string> titles = new List<string>();
            EnumWindows((hWnd, lParam) => {
                if (!IsWindowVisible(hWnd)) return true;
                GetWindowThreadProcessId(hWnd, out uint windowPid);
                if (windowPid != pid) return true;
                StringBuilder sb = new StringBuilder(256);
                GetWindowText(hWnd, sb, sb.Capacity);
                string title = sb.ToString().Trim();
                if (!string.IsNullOrEmpty(title)) titles.Add(title);
                return true;
            }, IntPtr.Zero);
            return titles;
        }
    }
"@

    # 调用嵌入的C#方法获取窗口标题
    [WindowEnumerator]::GetWindowTitles($ProcessId)
}

# 执行示例:获取CDViewer.exe的所有窗口
$citrixProcesses = Get-Process -Name CDViewer -ErrorAction SilentlyContinue
if ($citrixProcesses) {
    foreach ($proc in $citrixProcesses) {
        Write-Host "PID: $($proc.Id) | 进程名: $($proc.ProcessName)" -ForegroundColor Cyan
        Write-Host "窗口列表:"
        Get-ProcessAllWindows -ProcessId $proc.Id | ForEach-Object {
            Write-Host "  → $_"
        }
        Write-Host "------------------------"
    }
} else {
    Write-Host "未找到CDViewer.exe进程" -ForegroundColor Red
}

使用方法

直接运行这段脚本,它会自动定位所有CDViewer.exe进程,并输出每个PID对应的所有可见窗口标题。


关键注意事项

  • 权限问题:如果目标进程是高权限运行的,需要以管理员身份启动你的C#程序或PowerShell脚本,否则可能无法获取部分窗口信息。
  • 隐藏窗口:如果需要包括后台隐藏窗口,只需移除代码中IsWindowVisible的判断逻辑。
  • 空标题窗口:代码中默认过滤了空标题窗口,若需要保留,可去掉标题非空的判断。

内容的提问来源于stack exchange,提问作者Mirco Mares

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:32:53