在.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
。现在让我更加困惑的部分是:
如果在过程启动之后但在读取
MainWindowHandle
属性之前的任何地方设置了断点(或者如果我只是在这些行之间至少至少跳过了一次代码),则该函数将返回true
。如果在读取
MainWindowHandle
属性后设置断点,该函数将返回false
。在那一点上,如果我单步执行代码,设置更多断点等都不再重要了;结果始终为false
。可能会发生什么,我该如何解决?
一些可能不相关的详细信息:
只要其他进程具有GUI(我最初是通过WPF应用程序发现的),就会出现相同的问题。例如,尝试使用
dfrgui
。诸如
calc
之类的某些进程似乎为实际的GUI生成了一个单独的进程,因此行为略有变化:在.NET Core中,该函数仍返回
false
,但GUI保持打开状态,并且断点技巧不再起作用。在.NET Framework中,由于进程已经退出,在
InvalidOperationException
行中引发了MainWindowHandle
。我正在使用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反应太慢,无法测试。
我猜想调试器正在以某种方式改变程序的行为。列出所有进程属性时可能是偶然,或者可能与其使用的线程有关。但是到底如何呢?我可以复制相同的行为吗?
我已经尝试过但无法正常工作的一些事情:
增加尝试次数或两次尝试之间的睡眠时间
重新排序
Refresh()
,Thread.Sleep()
和MainWindowHandle
行用
Thread.Sleep()
代替await Task.Delay()
调用(并使函数异步)将
UseShellExecute
明确设置为true
/ false
来启动该过程使用
[MTAThread]
或[STAThread]
属性创建/刷新后,通过反射打印所有过程属性
即使这行不通,也许调试器也会以不同的方式/顺序读取这些属性,所以这仍然可能是调试有所作为的原因。
作为背景知识,当我使用FlaUI将UI测试添加到WPF应用程序时遇到了这个问题(请参见the issue I created)。我现在很确定这不是图书馆本身的问题;它只是碰巧将
Process.MainWindowHandle
用于某些方法。 最佳答案
原来,这是由.NET Core本身的issue引起的。第一次尝试后,无论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# - .NET Framework中的Process.MainWindowHandle非零,但.NET Core中的Process.MainWindowHandle非零,除非进行调试,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60342879/