如何识别具有非显示窗口的Windows 10后台存储进程,这些窗口在程序上是可见和最小化的?

18

我有一个Win32应用程序,用于确定是否有任何可见的、非图标化的、可最小化的窗口正在显示。据我所知,它在Win9x到Win8.1上工作良好,但在Windows 10下,它经常会发现一些实际上并不在屏幕上可见的窗口。

为了尝试确定发生了什么事情,我编写了一个简单的测试应用程序,枚举并记录所有这样的窗口。这里是EnumWindows回调代码的精华:

BOOL CALLBACK EnumFunc( HWND hWnd, LPARAM lParam )
{
  if ( IsWindowVisible( hWnd ) )
  {
    if ( !IsIconic( hWnd ) )
    {
      const LONG style = GetWindowLong( hWnd, GWL_STYLE );

      if ( WS_MINIMIZEBOX & style )
      {
     //      record window info
      }
    }
   }
 return TRUE;
}

大多数在 Windows 10 下出现的幻影窗口属于后台存储应用程序进程,例如 Mail、Calculator 和 Photos。这些进程会在任务管理器的“后台进程”部分列出,如果我使用任务管理器终止这些后台任务,则我的测试应用程序将不再找到它们的幻影窗口。

enter image description here

从我的测试应用程序的上述截图中,您可以看到除了一个窗口外,所有有问题的窗口都属于同一进程 ID 7768 的线程,该进程是 ApplicationFrameHost.exe。具有进程 ID 11808 的最后一个窗口是 explorer.exe。

我已经用 Spy++ 查看了这些幻影窗口,但没有发现任何特定的样式组合可以帮助唯一地识别它们。

有人建议未记录的 Windows“band”可能涉及其中,但我尝试使用(未记录的,因此可能是错误的)API:

BOOL WINAPI GetWindowBand (HWND hWnd, PDWORD pdwBand);
但是它对于任何窗口都返回1的频带,因此不能区分这些虚幻的窗口。
如何可靠地识别这些虚幻的窗口?

这些窗口是否共享相同的窗口类?或者可能设置了相同的属性(请参见EnumProps)?当您说“未记录的API[...]返回1个带”的时候,您正在查看哪个值?是BOOL返回值还是由第二个参数指向的DWORD - IInspectable
许多(并非我遇到的所有)具有相同的窗口类名,但当应用程序正常运行并在屏幕上可见时,它们仍然具有相同的类名。 - David Lowndes
关于 GetWindowBand 调用,BOOL 返回值始终为非零,并且 DWORD 指针参数中的“band”编号始终为1。 - David Lowndes
1
由于所有东西看起来都应该是可见的窗口,但实际上并没有,您是否尝试过查询窗口所属的桌面(GetWindowThreadProcessIdGetThreadDesktop)?与之前密切相关的是,您是否尝试使用EnumDesktopWindows替换EnumWindows - IInspectable
放弃上一个想法,因为使用MS帐户登录 - 我现在已经在我的测试VM中完成了这个操作,尽管我尝试了很多次,但我无法让应用程序显示为后台应用程序。然而,有一个观察结果是最初没有幻影窗口,但是一旦我启动单个应用程序,就会出现1个幻影窗口。 - David Lowndes
显示剩余7条评论
2个回答

16

检测这些幻影窗口的批准方法是使用DwmGetWindowAttribute和DWMWA_CLOAKED。

以下是我使用的代码:

static bool IsInvisibleWin10BackgroundAppWindow( HWND hWnd )
{
    int CloakedVal;
    HRESULT hRes = DwmGetWindowAttribute( hWnd, DWMWA_CLOAKED, &CloakedVal, sizeof( CloakedVal ) );
    if ( hRes != S_OK )
    {
        CloakedVal = 0;
    }
    return CloakedVal ? true : false;
}

感谢来自 MS 的 Scot Br 提供了答案,此处发布。


3
在另一个桌面中的 Windows 窗口也会显示为 DWMWA_CLOAKED。因此,更好的方法是使用这个库:https://github.com/Grabacr07/VirtualDesktop使用 VirtualDesktop.FromHwnd() 方法,如果返回 null,则表示它是 Windows 10 的后台应用程序。return VirtualDesktop.FromHwnd(hWnd) == null; - Afzal N
1
@AfzalivE 谢谢!我在我的项目中使用了Grabacr07的代码。https://github.com/mzomparelli/zVirtualDesktop - Michael Z.
我很想看看你的工具。你在GitHub上有吗? - Michael Z.
不是很想现在就开源它 :) - Afzal N
更多信息:https://social.msdn.microsoft.com/Forums/vstudio/en-US/f8341376-6015-4796-8273-31e0be91da62/difference-between-actually-visible-and-not-visiblewhich-are-there-but-we-cant-see-windows-of?forum=vcgeneral - RickJansen
显示剩余3条评论

2
类为 ApplicationFrameWindow 的顶级窗口是 Windows Store 应用程序的容器。首先,这里是 Spy 中显示的 Mail 窗口:

enter image description here

这是真正可见的(不是幻影)。你可以通过第一个子窗口的类为 Windows.UI.Core.CoreWindow 来确定它。有趣的是,ApplicationFrameWindow 的所有者进程是 APPLICATIONFRAMEHOST,但是 Windows.UI.Core.CoreWindow 的所有者进程是另一个进程:HXMAIL。(我以前从未见过子窗口的所有者进程与父窗口的不同!)

与幻影窗口进行比较(如在您的 RWTool 中标识的):

enter image description here

它缺少类为 Windows.UI.Core.CoreWindow 的子窗口。

这表明了您的问题的答案:如果顶级窗口的类为 ApplicationFrameWindow,请迭代其子窗口。如果第一个子窗口的类为 Windows.UI.Core.CoreWindow,则该窗口是可见的,否则它不可见(即为幻影)。

但是,如果一个老式的非 Windows Store 应用程序恰好具有类为 ApplicationFrameWindow 的顶级窗口呢?它将没有 Windows.UI.Core.CoreWindow 的子窗口。然而,它是可见的。如何确定这是普通应用程序而不是 Windows Store 应用程序?我没有绝对可靠的方法。您还可以检查 Store 应用程序的其他子窗口的存在: ApplicationFrameTitleBarWindowApplicationFrameInputSinkWindow。非 Windows Store 应用程序具有此精确的 Windows 层次结构的可能性微乎其微。

编辑

ApplicationFrameWindow(以及 Windows.UI.Core.CoreWindow)设置了 WS_EX_NOREDIRECTIONBITMAP 样式:

该窗口不会渲染到重定向表面。这适用于没有可见内容或使用其他机制而不是表面来提供其视觉效果的窗口。

最少,你可以检查这个样式而不是特别处理ApplicationFrameWindow。但是要查看任何内容是否真正可见,你仍然需要让它取决于是否有Windows.UI.Core.CoreWindow的子级。


谢谢David,那个模式确实与我在幻影商店应用程序窗口中看到的相匹配,但是我还看到一个幻影出现在Skype进程(最新桌面版本)中,它没有任何子窗口,并且具有类名“TApplication”,因此我不认为这种方法是可靠的解决方案。 - David Lowndes
当然,David, 但我不明白一个使用TApplication作为类名的老式Skype应用程序如何使上述探测“幻像”Metro应用与“真实”的Metro应用无效?另外,我已经下载并安装了最新的桌面版Skype到Win 10上,它是版本7.8.0.102,带有联系人的可见窗口的类名为tSkMainForm。我确实也找到了一个类名为TApplication的窗口,但它的宽度和高度都为0,因此不可见。 - David Ching
这并不是说Skype的行为特别地无效了任何事情,只是它表明解决方案可能不够健壮,因为它仅基于我们看到的经验情况。实际上它确实像你所期望的那样工作。我希望有一些具体的文档可以解释这些东西是什么,为什么它们不可见。顺便问一下,你是怎么让你的Win10系统拥有这些不可见的应用程序窗口的 - 我在虚拟机上无法让它们出现;而在我的主机系统上,它们似乎会在没有我做任何操作的情况下自动出现! - David Lowndes
@DavidLowndes:我很高兴它能够正常工作,这样你就可以为现有的代码推出修复方案了。请看我的关于WS_EX_NOREDIRECTIONBITMAP的编辑说明。我相信会有更详细的解释和解决方案,但这就是作为Windows开发人员的生活——向客户交付可用的软件而没有这些美好的东西!对于幽灵窗口出现的不稳定性,我也是这样。 - David Ching
1
我非常确定系统会提前注册ApplicationFrameWindow窗口类。因此,非商店应用程序随后将无法使用该名称注册窗口类。所以回答你的假设问题:没有一个名为ApplicationFrameWindow的非商店应用程序会存在。 - IInspectable
在我的Win10桌面上,我观察到了4个幽灵窗口 - 两个用于照片和两个用于设置。每对窗口都有一个ApplicationFrameWindow和一个Windows.UI.Core.CoreWindow。因此,可见窗口并不总是由Windows.UI.Core.Window子元素的存在标记。而且,在我看来,这4个窗口中没有一个被隐藏(测试隐藏返回所有4个情况的零)。 - Kevin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接