Получение заголовка видимого окна потока - PullRequest
1 голос
/ 16 февраля 2012

Мне было интересно, как бы я поступил так, поскольку я пробовал множество различных функций user32 и проводил всесторонние исследования в Интернете, но, к сожалению, пока не смог найти решение.

Существует приложение, которое имеет 5 потоков.Доступ к этим потокам может быть легко осуществлен с помощью метода GetProcessById класса .NET, при условии PID процесса.Однако не похоже, что есть функция, которую я могу использовать для предоставления идентификатора потока и перечисления его окон (родительского или дочернего).В одной из этих тем всего 10 окон, 9 скрытых и одно видимое.Название этой видимой темы - то, что я пытаюсь получить программно.

Мой последний подход состоял в том, чтобы захватить дескриптор процесса, поместить его через EnumChildWindows и попытаться таким образом добавить каждый дескриптор окна в коллекцию, но моя коллекция всегда пуста.

Вот скриншот того, что я вижу в инструменте ProcessThreadsView:

enter image description here

Что-то мне не хватает?Я написал автору инструмента по электронной почте, чтобы увидеть, как он это делает, но я решил попросить вас, ребята, посмотреть, есть ли установленный подход.

Обновление: Я пытался использоватьGetGUIThreadInfo, вот как я это называю:

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
    public uint cbSize;
    public uint flags;
    public IntPtr hwndActive;
    public IntPtr hwndFocus;
    public IntPtr hwndCapture;
    public IntPtr hwndMenuOwner;
    public IntPtr hwndMoveSize;
    public IntPtr hwndCaret;
    public Rect rcCaret;
}

static IEnumerable<IntPtr> EnumerateThreadWindowHandlesByProcessId(int processId)
{
    List<IntPtr> threadWindowHandles = new List<IntPtr>();

    foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
    {
        GUITHREADINFO threadInfo = new GUITHREADINFO();
        threadInfo.cbSize = (uint)Marshal.SizeOf(threadInfo);
        bool returnValue = GetGUIThreadInfo((uint)thread.Id, out threadInfo);
        threadWindowHandles.Add(threadInfo.hwndActive);
    }

    return threadWindowHandles;
}

Обновление 2:

Используя EnumThreadWindows, вот что я получил:

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

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

private static bool ThreadWindows(IntPtr handle, IntPtr param)
{
    //get window from handle later, testing for now
    logger.Info("foo bar");

    return true;
}

[STAThread]
public void Execute()
{
    Process[] processes = Process.GetProcessesByName("MyProcessName");

    Process processOfInterest = processes[0];

    foreach (ProcessThread thread in processOfInterest.Threads)
    {
        EnumThreadWindows(thread.Id, new EnumThreadDelegate(ThreadWindows), IntPtr.Zero);
    }
}

Ответы [ 3 ]

4 голосов
/ 16 февраля 2012

Я полагаю, что вы ищете GetGUIThreadInfo.

Обновление : в вашем вызове p / invoke есть несколько ошибок.

Прямоугольное поле неверно (не то, что это действительно имеет значение здесь).Используйте это:

[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

вместо System.Drawing.Rectangle.

Объявления типов для других полей также отключены.Это должно выглядеть следующим образом:

[StructLayout(LayoutKind.Sequential)]
public struct GUITHREADINFO
{
    public uint cbSize;
    public uint flags;
    public IntPtr hwndActive;
    public IntPtr hwndFocus;
    public IntPtr hwndCapture;
    public IntPtr hwndMenuOwner;
    public IntPtr hwndMoveSize;
    public IntPtr hwndCaret;
    public Rect rcCaret;
}

Было бы целесообразно проверить возвращаемые значения и использовать SetLastError = true в атрибуте DllImport, чтобы вы могли понять, почему произошел сбой вызова функции.Кроме того, вы должны передать структуру по ref, поскольку вы передаете размер структуры.

[DllImport("user32.dll", SetLastError=true)]
static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);

Используйте Marshal.GetLastWin32Error() для получения кода ошибки.Но проверяйте это только в том случае, если GetGUIThreadInfo возвращает false.

С учетом вышеуказанных изменений вызов GetGUIThreadInfo будет работать при наличии действительного идентификатора потока.Также обратите внимание, что другой процесс должен иметь фокус ввода, чтобы GetGUIThreadInfo мог вернуть что-либо полезное.

Если указанный поток не существует или имеет очередь ввода, функция будетошибка.

4 голосов
/ 16 февраля 2012

(Прошу прощения, если мой предыдущий ответ получился грубым; я сказал «кажущийся очевидным», потому что это казалось очевидным для меня, поэтому я предположил, что это было также очевидно для ОП и, следовательно, неправильно, но он не дал никаких подробностей относительно того, что был / не был судим.)

EnumThreadWindows - определенно та функция, которую вы хотите, и она должна работать. Я смог скопировать информацию из ProcessThreadsView, используя только EnumThreadWindows и IsWindowVisible для каждого приложения, работающего в моей системе. (Также имейте в виду, что «видимый» - это не то же самое, что «активный»).

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool EnumThreadWindowsProc(IntPtr handle, int param);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumThreadWindows(uint threadId, 
    EnumThreadWindowsProc callback, int param);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr handle);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int GetWindowText(
    IntPtr handle, 
    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder caption,
    int count);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int GetWindowTextLength(IntPtr handle);

static void Main(string[] args)
{
    var callback = new EnumThreadWindowsProc(Program.ThreadWindows);

    foreach (var proc in Process.GetProcesses())
    {
        foreach (ProcessThread thread in proc.Threads)
        {
            Program.EnumThreadWindows((uint)thread.Id, callback, 0);
        }
    }

    Console.ReadLine();
}

private static bool ThreadWindows(IntPtr handle, int param)
{           
    if (Program.IsWindowVisible(handle))
    {
        var length = Program.GetWindowTextLength(handle);
        var caption = new StringBuilder(length + 1);
        Program.GetWindowText(handle, caption, caption.Capacity);

        Console.WriteLine("Got a visible window: {0}", caption);
    }

    return true;
}

Что именно вы получаете, когда пробуете EnumThreadWindows? Ваш обратный вызов когда-либо срабатывает?

1 голос
/ 17 февраля 2012

Ваш код в Обновлении 2 в порядке (при условии, что eraAccessProcess на самом деле означает processOfInterest).Этот код работает.Единственные возможные причины сбоев, которые я вижу:

  1. processOfInterest - это не тот процесс, который вас действительно интересует.
  2. Этот процесс действительно не имеет связанных потоковс окнами.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...