Найти окно с конкретным текстом для процесса - PullRequest
9 голосов
/ 27 апреля 2010

Я пытаюсь выяснить, было ли окно с определенным процессом открыто процессом. Этот процесс порождает несколько окон, и мне нужно проверить их все.

У меня нет проблем с поиском процесса, с

foreach (Process p in Process.GetProcesses())
{
  if (p.MainModule.FileName.ToLower().EndsWith("foo.exe"))
     FindChildWindowWithText(p); //do work

проблема в том, что делать дальше. Я не могу использовать Process 'MainWindowText, потому что он меняется в зависимости от того, какое окно активировано.

Затем я попытался использовать функцию Windows EnumChildWindows и GetWindowText, но я не уверен, передаю ли я правильный дескриптор EnumChildWindows. EnumChildWindows работает должным образом при передаче MainWindowHandle, но, конечно, MainWindowHandle изменяется с активным окном. Поэтому я передал Process.Handle, но при переключении окон приложения я получаю разные дескрипторы и разные результаты. (Я понимаю, что EnumChildWindows возвращает дескрипторы не только windows , но и control на языке .net, это не проблема, если бы я мог также получить заголовок окна)

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

Спасибо

Ответы [ 3 ]

20 голосов
/ 02 мая 2010

Получив Процесс, вы можете перечислить все Windows в процессе и проверить, совпадает ли какая-либо из них с окном, которое вы ищете.

Вам понадобятся следующие объявления P / Invoke

[DllImport("user32", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool EnumThreadWindows(int threadId, EnumWindowsProc callback, IntPtr lParam);

[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

[DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
private extern static int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);

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

public static IntPtr FindWindowInProcess(Process process, Func<string, bool> compareTitle)
{
  IntPtr windowHandle = IntPtr.Zero;

  foreach (ProcessThread t in process.Threads)
  {
    windowHandle = FindWindowInThread(t.Id, compareTitle);
    if (windowHandle != IntPtr.Zero)
    {
      break;
    }
  }

  return windowHandle;
}

private static IntPtr FindWindowInThread(int threadId, Func<string, bool> compareTitle)
{
  IntPtr windowHandle = IntPtr.Zero;
  EnumThreadWindows(threadId, (hWnd, lParam) =>
  {
    StringBuilder text = new StringBuilder(200);
    GetWindowText(hWnd, text, 200);
    if (compareTitle(text.ToString()))
    {
      windowHandle = hWnd;
      return false;
    }
    return true;
  }, IntPtr.Zero);

  return windowHandle;
}

Затем вы можете вызвать функцию FindWindowInProcess, чтобы найти окно, заголовок которого заканчивается в качестве примера "ABC".

IntPtr hWnd = FindWindowInProcess(p, s => s.EndsWith("ABC"));
if (hWnd != IntPtr.Zero) 
{
  // The window was found....
}

Конечно, вы можете заменить s => s.EndsWith ("ABC") любым выражением, которое будет соответствовать вашим критериям поиска для окна, это может быть регулярное выражение и т. Д.

Вот также версия FindThreadWindow, которая также проверит первый уровень дочерних окон. Вы можете пойти дальше и сделать это рекурсивной функцией, если ваши окна находятся ниже в иерархии.

private static IntPtr FindWindowInThread(int threadId, Func<string, bool> compareTitle)
{
  IntPtr windowHandle = IntPtr.Zero;
  EnumThreadWindows(threadId, (hWnd, lParam) =>
  {
    StringBuilder text = new StringBuilder(200);
    GetWindowText(hWnd, text, 200);        
    if (compareTitle(text.ToString()))
    {
      windowHandle = hWnd;
      return false;
    }
    else
    {
      windowHandle = FindChildWindow(hWnd, compareTitle);
      if (windowHandle != IntPtr.Zero)
      {
        return false;
      }
    }
    return true;
  }, IntPtr.Zero);

  return windowHandle;
}

private static IntPtr FindChildWindow(IntPtr hWnd, Func<string, bool> compareTitle)
{
  IntPtr windowHandle = IntPtr.Zero;
  EnumChildWindows(hWnd, (hChildWnd, lParam) =>
  {
    StringBuilder text = new StringBuilder(200);
    GetWindowText(hChildWnd, text, 200);        
    if (compareTitle(text.ToString()))
    {
      windowHandle = hChildWnd;
      return false;
    }
    return true;
  }, IntPtr.Zero);

  return windowHandle;
}
3 голосов
/ 01 мая 2010

Вместо перечисления процессов и поиска окна, я бы перечислил окна (используя EnumWindows ) и нашел бы процесс (используя GetGuiThreadInfo ).

0 голосов
/ 01 мая 2010

Почти похожий (или точно такой же?) Вопрос с принятым ответом, на который вы можете сослаться: .NET (C #): получение дочерних окон, когда у вас есть только дескриптор процесса или PID?

...