FindWindow в C#(через pinvoke) находит нужный дескриптор окна, но не в нужных условиях. Как мне это исправить?
Я пытаюсь получить определенную ручку окна. Я искал решение в течение многих часов, и я понимаю, что мой вопрос звучит примерно так: FindWindow() не находит мое окно [C++], но это обсуждение не помогло.
Я пытался использовать оба FindWindow() и FindWindowEx(), как эти два:
IntPtr SysPropWndHandler = FindWindow("#32770", "Параметри продуктивності");
IntPtr SysPropWndHandler = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "#32770", "Параметри продуктивності");
Странно то, что когда я запускаю программу, она запускает новый процесс для определенной программы системных настроек из папки system32 и не может найти свой дескриптор в течение того же времени запуска (если это правильно, так сказать). Я попытался приостановить его, чтобы дать ему время создать окно и назначить дескриптор, но это не помогает. Но! Если эта системная программа запускается первой, а затем я запускаю свою программу, она сразу же находит ее ручку. Два пути для этого "внешнего запуска":
- Я запускаю системную программу вручную перед запуском моей программы
- Я запускаю свою программу, которая запускает эту системную программу, затем закрываю программу, тогда системная программа не закрывается. После этого я снова запускаю свою программу.
Но то, что я на самом деле пытаюсь заставить мою программу сделать, это:
- запустить системную программу (некоторые настройки производительности)
- скрыть окно
- изменить некоторые настройки через WinApi (вид эмуляции щелчка флажка)
- нажмите ОК
- закрой его
Поскольку мой код работает, по крайней мере, в некоторых условиях, похоже, что он не имеет ничего общего с кодированием, о котором шла речь в том же вопросе. Иначе это не сработает вообще.
Я пытался запустить его скрытно, но это не сработало. Я попробовал тот же код для блокнота для его отладки - он работает.
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:Параметры быстродействия
Попробуйте с этим кодом свой заголовок и скажите, работает ли он.
Также есть другой подход, как в этом ответе.