C# Process.MainWindowHandle在.NET Framework中为非零,但在.NET Core中为零,除非进行调试
在访问.NETCore(3.1)中的C# Process.MainWindowHandle在.NET Framework中为非零,但在.NET Core中为零,除非进行调试,c#,wpf,.net-core,C#,Wpf,.net Core,在访问.NETCore(3.1)中的MainWindowHandle时,我面临进程类的一种奇怪行为 考虑以下功能: bool TestProcess() { var process = Process.Start("notepad"); try { for (var attempt = 0; attempt < 20; ++attempt) { process?.Refresh(); i
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();
}
}
bool TestProcess()
{
var process=process.Start(“记事本”);
尝试
{
对于(var尝试=0;尝试<20;++尝试)
{
进程?.Refresh();
if(进程?.MainWindowHandle!=IntPtr.Zero)
{
返回true;
}
睡眠(100);
}
返回false;
}
最后
{
进程?.Kill();
}
}
如果该函数在.NET Framework中运行,它会像我预期的那样返回true
。但是,当使用.NETCore(在我的例子中是3.1)时,它会返回false
现在更让我困惑的是:
- 如果我在进程启动后但在读取
属性之前的任何位置设置断点(或者如果我只是在这些行之间至少跨代码一次),函数将返回MainWindowHandle
true
- 如果在读取
属性后设置断点,函数将返回MainWindowHandle
。在这一点上,如果我跳过代码,设置更多断点,等等,这就不再重要了。;结果总是false
false
- 同样的问题也可能发生在其他进程上,只要它们有GUI(我最初是通过WPF应用程序发现的)。例如,尝试使用
dfrgui
- 一些进程,如
似乎会为实际GUI生成一个单独的进程,因此行为会发生轻微变化:calc
- 在.NETCore中,函数仍然返回
,但GUI保持打开状态,断点技巧不再起作用false
- 在.NET Framework中,由于进程已退出,
行中抛出了一个MainWindowHandle
InvalidOperationException
- 在.NETCore中,函数仍然返回
- 我使用的是Visual Studio 2019(16.4.5)、ReSharper 2019.3.2、.NET Core SDK 3.1.101和Windows 10(Build 18363)
- 断点技巧也适用于Rider(2019.3.3)但是,仅当您在继续执行程序之前留出足够的时间让主窗口出现时。VisualStudio中可能也是这样,但IDE的反应太慢,我无法测试
- 增加尝试次数或尝试之间的睡眠时间
- 重新排列
、Refresh()
和Thread.Sleep()
行MainWindowHandle
- 将
调用替换为线程.Sleep()
(并使函数异步)等待任务.Delay()
- 当
显式设置为UseShellExecute
/true
时,启动流程false
- 使用
或[MTAThread]
属性[STAThread]
- 创建/刷新后通过反射打印所有流程属性
- 即使这不起作用,调试器也可能以不同的方式/顺序读取这些属性,因此这可能仍然是调试产生影响的原因
作为一个背景,我在使用FlaUI向WPF应用程序添加UI测试时遇到了这个问题(请参阅)。我现在很确定这不是图书馆本身的问题;它只是碰巧对一些方法使用了
Process.MainWindowHandle
事实证明,这是由.NET内核本身的错误引起的。MainWindowHandle
属性在第一次尝试后不会重新计算,无论它是否返回IntPtr.Zero
设置断点时,我唯一能做到的就是延迟读取MainWindowHandle
的时间。如果在此之前调用更长的Thread.Sleep()
,我也可以达到同样的效果。事实上,在我的例子中,100毫秒对于记事本来说已经足够了,但是对于我正在测试的原始WPF应用程序,我需要大约1秒的时间。也许我对调试器太过谨慎了
我已经提交了一个请求来解决这个问题。同时,如果任何人也受到类似的影响,我建议将任何Refresh()
调用替换为process=process.GetProcessById(process.Id)
。这将返回一个指向同一进程的新的进程
实例,因此可以重新计算MainWindowHandle
属性而不会出现问题
在我最初的示例中,它看起来是这样的(重新排序以避免初始实例创建):
bool TestProcess()
{
var process=process.Start(“记事本”);
尝试
{
对于(var尝试=0;尝试<20;++尝试)
{
if(进程?.MainWindowHandle!=IntPtr.Zero)
{
返回true;
}
睡眠(100);
process=process.GetProcessById(process.Id);
}
返回false;
}
最后
{
进程?.Kill();
}
}
很有趣,不知道我怎么会错过。不过,我看不到有人就此提出任何问题/公关。我的PR缺少EnsureRate(State.HaveProcessInfo)代码>调用,因此我可能会对此进行更多的研究,并在适用的情况下添加它。
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();
}
}