首页 > 解决方案 > Process.MainWindowHandle 在 .NET Framework 中不为零,但在 .NET Core 中为零,除非调试

问题描述

在 .NET Core (3.1) 中Process访问时,我面临着类的奇怪行为。MainWindowHandle

考虑以下函数:

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

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

可能发生了什么,我该如何解决?

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

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

我已经尝试过但没有奏效的一些事情:


作为背景知识,当我使用 FlaUI 向我的 WPF 应用程序添加 UI 测试时遇到了这个问题(请参阅我创建的问题)。我现在很确定这不是图书馆本身的问题。它只是碰巧用于它Process.MainWindowHandle的一些方法。

标签: c#wpf.net-core

解决方案


事实证明,这是由.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();
    }
}

推荐阅读