Как перечислять все окна, относящиеся к определенному процессу, используя .NET?

Как я могу найти все окна, созданные определенным процессом, используя С#?

UPDATE

Мне нужно перечислить все окна, относящиеся к определенному процессу, используя PID (идентификатор процесса) приложения.

Ответ 1

Используйте Win32 API EnumWindows (если вы хотите, чтобы дочерние окна EnumChildWindows)), или, альтернативно, вы можете использовать EnumThreadWindows.

[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);

Затем проверьте, к какому процессу принадлежит каждое окно, используя API Win32 GetWindowThreadProcessId

[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);

Ответ 2

delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
    IntPtr lParam);

static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processId)
{
    var handles = new List<IntPtr>();

    foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
        EnumThreadWindows(thread.Id, 
            (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);

    return handles;
}

и использование образца:

private const uint WM_GETTEXT = 0x000D;

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, 
    StringBuilder lParam);

[STAThread]
static void Main(string[] args)
{
    foreach (var handle in EnumerateProcessWindowHandles(
        Process.GetProcessesByName("explorer").First().Id))
    {
        StringBuilder message = new StringBuilder(1000);
        SendMessage(handle, WM_GETTEXT, message.Capacity, message);
        Console.WriteLine(message);
    }
}

Ответ 3

Древняя нить, но это заставило меня начать здесь небольшую полезную функцию, которая найдет дочернее окно, которое соответствует лямбда (Predicate). Легко изменить, чтобы вернуть список. В предикате обрабатываются несколько критериев.

    public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

    [DllImport("user32.Dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);

    /// <summary>
    /// Find a child window that matches a set of conditions specified as a Predicate that receives hWnd.  Returns IntPtr.Zero
    /// if the target window not found.  Typical search criteria would be some combination of window attributes such as
    /// ClassName, Title, etc., all of which can be obtained using API functions you will find on pinvoke.net
    /// </summary>
    /// <remarks>
    ///     <para>Example: Find a window with specific title (use Regex.IsMatch for more sophisticated search)</para>
    ///     <code lang="C#"><![CDATA[var foundHandle = Win32.FindWindow(IntPtr.Zero, ptr => Win32.GetWindowText(ptr) == "Dashboard");]]></code>
    /// </remarks>
    /// <param name="parentHandle">Handle to window at the start of the chain.  Passing IntPtr.Zero gives you the top level
    /// window for the current process.  To get windows for other processes, do something similar for the FindWindow
    /// API.</param>
    /// <param name="target">Predicate that takes an hWnd as an IntPtr parameter, and returns True if the window matches.  The
    /// first match is returned, and no further windows are scanned.</param>
    /// <returns> hWnd of the first found window, or IntPtr.Zero on failure </returns>
    public static IntPtr FindWindow(IntPtr parentHandle, Predicate<IntPtr> target) {
        var result = IntPtr.Zero;
        if (parentHandle == IntPtr.Zero)
            parentHandle = Process.GetCurrentProcess().MainWindowHandle;
        EnumChildWindows(parentHandle, (hwnd, param) => {
            if (target(hwnd)) {
                result = hwnd;
                return false;
            }
            return true;
        }, IntPtr.Zero);
        return result;
    }

Пример

var foundHandle = Win32.FindWindow(IntPtr.Zero, ptr => Win32.GetWindowText(ptr) == "Dashboard");

Ответ 4

Через некоторое время я нашел простой и более короткий способ:

Вам понадобится: " using System.Diagnostics; "

    [DllImport("kernel32.dll", SetLastError = true)] // Optional
    [return: MarshalAs(UnmanagedType.Bool)]          // Optional

    private void button1_Click(object sender, EventArgs e)
    {
        AllocConsole(); // Easy to read              // Optional

        Process[] processlist = Process.GetProcesses();

        foreach (Process process in processlist)
        {
            if (!string.IsNullOrEmpty(process.MainWindowTitle))
            {
                Console.WriteLine("Process: {0} ID: {1} Window title: {2}",
                process.ProcessName, process.Id, process.MainWindowTitle);
            }
        }
    }