gpt4 book ai didi

c# - Process.MainWindowHandle 在 .NET Framework 中非零,但在 .NET Core 中为零,除非进行调试

转载 作者:行者123 更新时间:2023-12-02 08:56:28 26 4
gpt4 key购买 nike

在 .NET Core (3.1) 中访问 MainWindowHandle 时,我遇到了 Process 类的奇怪行为。

考虑以下函数:

bool TestProcess()
{
var process = Process.Start("notepad");
try
{
for (var attempt = 0; attempt < 20; ++attempt)
{
process?.Refresh();
if (process?.MainWindowHandle != IntPtr.Zero)
{
return true;
}

Thread.Sleep(100);
}

return false;
}
finally
{
process?.Kill();
}
}

如果该函数在 .NET Framework 中运行,它将返回 true 正如我所期望的。但是,当使用 .NET Core(在我的例子中为 3.1)时,它会返回 false

现在更让我困惑的部分是:

  • 如果我在进程启动之后但在读取 MainWindowHandle 属性之前的任何位置设置断点(或者如果我只是在这些行之间至少跳过一次代码),则该函数将返回 真实
  • 如果我在读取 MainWindowHandle 属性后设置断点,该函数将返回 false。到那时,我是否可以跳过代码、设置更多断点等都不再重要了;结果始终为false

可能发生了什么情况以及如何解决它?

更多一些可能相关或不相关的细节:

  • 只要其他进程有 GUI(我最初是在 WPF 应用程序中发现的),同样的问题也可能发生在其他进程上。例如,尝试使用 dfrgui 来代替。
  • 某些进程(例如 calc)似乎为实际 GUI 生成了一个单独的进程,因此行为略有变化:
    • 在 .NET Core 中,该函数仍返回 false,但 GUI 保持打开状态,并且断点技巧不再起作用。
    • 在 .NET Framework 中,由于进程已退出,MainWindowHandle 行中会引发 InvalidOperationException
  • 我使用的是 Visual Studio 2019 (16.4.5)、ReSharper 2019.3.2、.NET Core SDK 3.1.101 和 Windows 10(内部版本 18363)。
  • 断点技巧也适用于 Rider (2019.3.3)。 但是,仅当您在恢复程序执行之前留出足够的时间让主窗口出现时。 Visual Studio 中也可能出现这种情况,但 IDE 的 react 速度太慢,无法进行测试。

我猜测调试器正在以某种方式改变程序的行为;也许是在列出所有进程属性时偶然发生的,或者可能与其使用的线程有关。但具体如何呢?我可以复制同样的行为吗?

一些我已经尝试过但没有成功的事情:

  • 增加尝试次数或尝试之间的 sleep 时间
  • 重新排序 Refresh()Thread.Sleep()MainWindowHandle
  • await Task.Delay() 替换 Thread.Sleep() 调用(并使函数异步)
  • UseShellExecute 显式设置为 true/false 启动进程
  • 使用[MTAThread][STAThread]属性
  • 创建/刷新后通过反射打印所有流程属性
    • 即使这不起作用,调试器也可能以不同的方式/顺序读取这些属性,因此这可能仍然是调试产生影响的原因。
<小时/>

作为一点背景知识,当我使用 FlaUI 将 UI 测试添加到我的 WPF 应用程序时,我遇到了这个问题(请参阅 the issue I created )。我现在很确定这不是图书馆本身的问题;而是图书馆本身的问题。它只是碰巧使用 Process.MainWindowHandle 来实现其某些方法。

最佳答案

事实证明这是由 issue 引起的与 .NET Core 本身。 MainWindowHandle 属性在第一次尝试后不会重新计算,无论它是否返回 IntPtr.Zero

设置断点时,我唯一实现的就是延迟读取 MainWindowHandle 的时刻。在此之前,我可以通过更长的 Thread.Sleep() 调用来实现相同的效果。事实上,在我的例子中,100 毫秒对于记事本来说已经足够了,但对于我正在测试的原始 WPF 应用程序,我需要大约 1 秒。也许我总体上对调试器过于警惕。

我已经提交了一个拉取请求来解决这个问题。同时,如果有人也受到类似问题的影响,我建议将任何 Refresh() 调用替换为 process = Process.GetProcessById(process.Id)。这将返回一个指向同一进程的新 Process 实例,因此可以毫无问题地重新评估 MainWindowHandle 属性。

在我原来的示例中,它看起来像这样(稍微重新排序以避免初始实例创建):

bool TestProcess()
{
var process = Process.Start("notepad");
try
{
for (var attempt = 0; attempt < 20; ++attempt)
{
if (process?.MainWindowHandle != IntPtr.Zero)
{
return true;
}

Thread.Sleep(100);
process = Process.GetProcessById(process.Id);
}

return false;
}
finally
{
process?.Kill();
}
}

关于c# - Process.MainWindowHandle 在 .NET Framework 中非零,但在 .NET Core 中为零,除非进行调试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60342879/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com