【问题标题】:EnumWindows function in Win10 enumerates only desktop appsWin10中的EnumWindows函数只枚举桌面应用
【发布时间】:2016-11-07 09:43:40
【问题描述】:

EnumWindows 上的documentation 下划线:

注意对于 Windows 8 及更高版本,EnumWindows 仅枚举桌面应用程序的顶级窗口。

“桌面应用”“非桌面应用”有什么区别?

这与地铁应用程序有关吗?

我问是因为 EnumWindows 在 Win10 中的行为与 Win7 相比有些不同。

【问题讨论】:

  • 是的,这是设计使然。不枚举属于 Metro/Modern/UWP 应用程序的 Windows。这是一个桌面 API,因此您只能获取桌面应用程序。

标签: c# windows windows-10 pinvoke


【解决方案1】:

你是对的。 EnumWindows 只会找到属于非现代(Metro)应用程序的窗口。它将获得属于传统(桌面)程序的窗口。 FindWindowEx,根据several sources,确实适用于各种窗口,包括来自现代应用程序的窗口。

【讨论】:

  • FindWindowEx 不安全,在调用 FindWindowEx 之间可能会发生更改为现有窗口,从而导致列表不准确甚至是无休止的循环。
【解决方案2】:

另一个解决方案是使用 win32u.dll 中未记录的 api,它有原型:

   NTSTATUS WINAPI NtUserBuildHwndList
   (
     HDESK in_hDesk,
     HWND  in_hWndNext,
     BOOL  in_EnumChildren,
     BOOL  in_RemoveImmersive,
     DWORD in_ThreadID,
     UINT  in_Max,
     HWND *out_List,
     UINT *out_Cnt
   );

将其传递到具有最大条目的 HWND 列表中,将所有其他参数设置为零,输出 Cnt 给出返回条目的数量。如果结果代码是 STATUS_BUFFER_TOO_SMALL 则重新分配更多条目的列表并重试。

与Win10之前的版本相比,增加了参数RemoveImmersive。如果为 TRUE,则返回与 EnumWindows 相同的列表(没有沉浸式窗口)。如果为 FALSE,则返回完整列表。

列表的第一个条目是 0x00000001 作为句柄,必须忽略。

此 api 的优点是在调用 FindWIndowEx 期间不可能更改窗口列表(在构建列表期间设置了锁)

EnumWindows、EnumDesktopWindows、EnumChildWindows、FindWindow、FindWindowEx都使用这个api。

特此请求 Microsoft 添加公共 api EnumWindowsEx 或 EnumAllWindows,以便开发人员有一个安全的方法来枚举所有窗口。我知道他们将过滤器添加到 EnumWindows 以修复自定义任务列表,这些任务列表显示可见但隐藏的沉浸式/metro/uwp 窗口。但是应该支持开发者获取完整列表的方法。

更新:关于如何使用这个api的例子,InitWin32uDLL做一个win32u.dll的运行时加载,lib_NtUserBuildHwndListW10是GetProcAddress指针

/********************************************************/
/* enumerate all top level windows including metro apps */
/********************************************************/

BOOL Gui_RealEnumWindows(WNDENUMPROC in_Proc, LPARAM in_Param)
{
    /* locals */
    INT   lv_Cnt;
    HWND  lv_hWnd;
    BOOL  lv_Result;
    HWND  lv_hFirstWnd;
    HWND  lv_hDeskWnd;
    HWND *lv_List;


  // only needed in Win8 or later
  if (gv_SysInfo.Basic.OsVersionNr < OSVER_WIN8)
    return EnumWindows(in_Proc, in_Param);

  // no error yet
  lv_Result = TRUE;

  // first try api to get full window list including immersive/metro apps
  lv_List = _Gui_BuildWindowList(0, 0, 0, 0, 0, &lv_Cnt);

  // success?
  if (lv_List)
  {
    // loop through list
    while (lv_Cnt-- > 0 && lv_Result)
    {
      // get handle
      lv_hWnd = lv_List[lv_Cnt];

      // filter out the invalid entry (0x00000001) then call the callback
      if (IsWindow(lv_hWnd))
        lv_Result = in_Proc(lv_hWnd, in_Param);
    }

    // free the list
    MemFree(lv_List);
  }
  else
  {
    // get desktop window, this is equivalent to specifying NULL as hwndParent
    lv_hDeskWnd = GetDesktopWindow();

    // fallback to using FindWindowEx, get first top-level window
    lv_hFirstWnd = FindWindowEx(lv_hDeskWnd, 0, 0, 0);

    // init the enumeration
    lv_Cnt  = 0;
    lv_hWnd = lv_hFirstWnd;

    // loop through windows found
    // - since 2012 the EnumWindows API in windows has a problem (on purpose by MS)
    //   that it does not return all windows (no metro apps, no start menu etc)
    // - luckally the FindWindowEx() still is clean and working
    while (lv_hWnd && lv_Result)
    {
      // call the callback
      lv_Result = in_Proc(lv_hWnd, in_Param);

      // get next window
      lv_hWnd = FindWindowEx(lv_hDeskWnd, lv_hWnd, 0, 0);

      // protect against changes in window hierachy during enumeration
      if (lv_hWnd == lv_hFirstWnd || lv_Cnt++ > 10000)
        break;
    }
  }

  // return the result
  return lv_Result;
}



HWND *_Gui_BuildWindowList
(
  HDESK in_hDesk,
  HWND  in_hWnd,
  BOOL  in_EnumChildren,
  BOOL  in_RemoveImmersive,
  UINT  in_ThreadID,
  INT  *out_Cnt
)
{
    /* locals */
    UINT  lv_Max;
    UINT  lv_Cnt;
    UINT  lv_NtStatus;
    HWND *lv_List;


  // is api not supported?
  if (!InitWin32uDLL())
    return NULL;

  // initial size of list
  lv_Max = 512;

  // retry to get list
  for (;;)
  {
    // allocate list
    if ((lv_List = (HWND*)MemAlloc(lv_Max*sizeof(HWND))) == NULL)
      break;

    // call the api
    lv_NtStatus = lib_NtUserBuildHwndListW10(
        in_hDesk, in_hWnd,
        in_EnumChildren, in_RemoveImmersive, in_ThreadID,
        lv_Max, lv_List, &lv_Cnt);

    // success?
    if (lv_NtStatus == NOERROR)
      break;

    // free allocated list
    MemFree(lv_List);

    // clear
    lv_List = NULL;

    // other error then buffersize? or no increase in size?
    if (lv_NtStatus != STATUS_BUFFER_TOO_SMALL || lv_Cnt <= lv_Max)
      break;

    // update max plus some extra to take changes in number of windows into account
    lv_Max = lv_Cnt + 16;
  }

  // return the count
  *out_Cnt = lv_Cnt;

  // return the list, or NULL when failed
  return lv_List;
}

【讨论】:

  • 非常感谢您的解决方案,但您能澄清一下吗?这仅适用于 w8 还是仅适用于 w10 ?代码说明了一件事,您的回复是另一件事......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多