【问题标题】:FindWindow in C#(via pinvoke) finds desired window handle, but not in the desired conditions. How do I fix it?C#(通过 pinvoke)中的 FindWindow 找到所需的窗口句柄,但不是在所需的条件下。我如何解决它?
【发布时间】:2019-05-27 18:08:23
【问题描述】:

我正在尝试获取某些窗口句柄。我花了好几个小时寻找解决方案,我知道我的问题听起来与这个问题相似: FindWindow() doesn't find my window [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(我这样做了),但程序仍然可以选择忽略它。看起来这正是那里正在发生的事情。 但我试图通过 Spy++ 获取 exect 窗口句柄并尝试隐藏它 - 它有效,所以我可以从那里操作它并做我的事情。唯一的问题是找到它的句柄......

在这种情况下如何找到该句柄?

附言

  • Windows 10 x64 Pro 乌克兰语(对于代码中的窗口标题不起作用的其他语言)
  • .NET Framework 4.7.2
  • 代码位于 .NET Framework 类库中,从 C# 控制台应用程序启动。

【问题讨论】:

  • process.Start(); 之后尝试process.WaitForInputIdle();。然后拨打IntPtr handle = FindWindow(null, "[The Windows Title in your language]");。我不认为你不能隐藏那个窗口。进程记得Dispose()
  • 另外,我认为(这里不确定)您应该在运行之前检查该进程是否已经处于活动状态。如果它已经在运行并且你再次运行它,你的应用程序可能会崩溃,除非你try/catchprocess.Start();。您可以使用Process.GetProcessesByName("SystemPropertiesPerformance").First(); 来查看它是否处于活动状态。
  • 你的程序启动后窗口是否隐藏?
  • First() 我的意思当然是FirstOrDefault() :)
  • 吉米,非常感谢。 )我没有检查其他答案,但process.WaitForInputIdle();process.Start(); 之后的简单行对我有用。我认为您应该将其添加为答案而不是评论。

标签: c# pinvoke window-handles


【解决方案1】:

另一种方法是使用 Windows 中内置的UI Automation 技术。例如,这个示例控制台应用程序应该可以工作。而且因为它是基于事件的,所以它不需要使用可以依赖于上下文的计时器:

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 以相同的UAC level 运行。

【讨论】:

  • 可能会起作用,但这个解决方案的缺点是如果用户想要启动同一个窗口 - 它也会被隐藏。最有可能的用户无论如何都不会启动它,但仍然可以被视为一个错误,所以我倾向于避免这种情况。是的,我也认为计时器不是最好的主意。 Jimmi 在 cmets 中提出问题的建议对我来说效果最好,但无论如何感谢您的回答。如果下次我需要你的方法——我现在知道在哪里可以找到它。 ;)
【解决方案2】:

对我来说,这个工作正常(在 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:Параметры быстродействия

用那个代码试试你的标题,然后说它是否有效。

还有一种不同的方法,例如this 答案。

【讨论】:

  • 添加一些睡眠可能是个好主意,但这在很大程度上取决于 PC 的生产力,完成任务需要多长时间(PC 的功能有多强大以及它的其他任务负载如何)。所以我认为这不是普遍的想法,适用于任何 PC 和任何情况。但无论如何感谢您的回答。 )
猜你喜欢
  • 2012-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-22
  • 1970-01-01
  • 2012-12-02
  • 2020-07-24
  • 1970-01-01
相关资源
最近更新 更多