FindWindow в C#(через pinvoke) находит нужный дескриптор окна, но не в нужных условиях. Как мне это исправить?

Я пытаюсь получить определенную ручку окна. Я искал решение в течение многих часов, и я понимаю, что мой вопрос звучит примерно так: FindWindow() не находит мое окно [C++], но это обсуждение не помогло.

Я пытался использовать оба FindWindow() и FindWindowEx(), как эти два:

IntPtr SysPropWndHandler = FindWindow("#32770", "Параметри продуктивності");

IntPtr SysPropWndHandler = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "Параметри продуктивності");

Странно то, что когда я запускаю программу, она запускает новый процесс для определенной программы системных настроек из папки system32 и не может найти свой дескриптор в течение того же времени запуска (если это правильно, так сказать). Я попытался приостановить его, чтобы дать ему время создать окно и назначить дескриптор, но это не помогает. Но! Если эта системная программа запускается первой, а затем я запускаю свою программу, она сразу же находит ее ручку. Два пути для этого "внешнего запуска":

  1. Я запускаю системную программу вручную перед запуском моей программы
  2. Я запускаю свою программу, которая запускает эту системную программу, затем закрываю программу, тогда системная программа не закрывается. После этого я снова запускаю свою программу.

Но то, что я на самом деле пытаюсь заставить мою программу сделать, это:

  1. запустить системную программу (некоторые настройки производительности)
  2. скрыть окно
  3. изменить некоторые настройки через WinApi (вид эмуляции щелчка флажка)
  4. нажмите ОК
  5. закрой его

Поскольку мой код работает, по крайней мере, в некоторых условиях, похоже, что он не имеет ничего общего с кодированием, о котором шла речь в том же вопросе. Иначе это не сработает вообще.

Я пытался запустить его скрытно, но это не сработало. Я попробовал тот же код для блокнота для его отладки - он работает.

string prog_path = @"C:\Windows\System32\SystemPropertiesPerformance.exe";

Process process = new Process();
process.StartInfo.FileName = prog_path;
process.StartInfo.CreateNoWindow = true; // no need for that, but I tried with it and without it just in case it works
process.StartInfo.UseShellExecute = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.Start();

Согласно документации Microsoft вам необходимо установить UseShellExecute в true для того, чтобы использовать StartInfo.WindowStyle = ProcessWindowStyle.Hidden (что я и сделал), но программа по-прежнему может игнорировать это. Похоже, это именно то, что там происходит. Но я попытался получить дескриптор окна Exect через Spy++ и попытаться скрыть его - это работает, поэтому я могу манипулировать им оттуда и делать свое дело. Единственная проблема - найти ручку...

Как мне найти эту ручку в этом случае?

PS

  • Windows 10 x64 Pro украинский (для других языков этот заголовок окна в коде не будет работать)
  • .NET Framework 4.7.2
  • Код находится внутри библиотеки классов.NET Framework, которая запускается из консольного приложения C#.

2 ответа

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

public static void Main(string[] args)
{
    Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, (sender, e) =>
    {
        var element = sender as AutomationElement;
        if (element.Current.Name == "Параметры быстродействия")
        {
            Console.WriteLine("hwnd:" + element.Current.NativeWindowHandle);
        }
    });

    Process.Start("SystemPropertiesPerformance.exe");
    Console.ReadLine(); // wait ...
    Automation.RemoveAllEventHandlers(); // cleanup
}

Он отлично работает на моей машине Windows 10 x64. Если это не работает, убедитесь, что ваша программа и SystemPropertiesPerformance.exe работают на одном уровне контроля учетных записей.

Для меня это работает нормально (на Windows 7):

using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;

namespace findwindow
{
    class Program
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        public static void Main(string[] args)
        {
            Process.Start(new ProcessStartInfo(){FileName=@"C:\Windows\System32\SystemPropertiesPerformance.exe"});
            System.Threading.Thread.Sleep(100);
            IntPtr hwnd = FindWindow("#32770", "Параметры быстродействия");
            var sb = new StringBuilder(50);
            GetWindowText(hwnd, sb, 49);
            Console.WriteLine("hwnd:"+hwnd+", title:"+sb);
            Console.ReadKey(true);
        }
    }
}

Выходы:

hwnd:5636204, title:Параметры быстродействия

Попробуйте с этим кодом свой заголовок и скажите, работает ли он.

Также есть другой подход, как в этом ответе.

Другие вопросы по тегам