Win10中的EnumWindows函数只枚举桌面应用
EnumWindows function in Win10 enumerates only desktop apps
EnumWindows 上的 documentation 下划线:
Note For Windows 8 and later, EnumWindows enumerates only top-level windows of desktop apps.
"desktop apps"和"non desktop apps"有什么区别?
这与 metro 应用程序有关吗?
我问是因为 EnumWindows 在 Win10 中与 Win7 相比表现有些不同。
你是对的。 EnumWindows
只会找到 windows 属于非现代 (Metro) 应用程序的程序。它将获得属于传统(桌面)程序的 windows。 FindWindowEx
, according to several sources,确实适用于各种 windows,包括来自现代应用程序的那些。
另一种解决方案是使用 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 相同的列表(没有沉浸式 windows)。如果为 FALSE,则返回完整列表。
列表的第一个条目是 0x00000001 作为句柄,必须忽略。
此 api 的优点是在调用 FindWIndowEx 期间无法更改 window 列表(在构建列表期间设置了锁)
EnumWindows、EnumDesktopWindows、EnumChildWindows、FindWindow、FindWindowEx 都用这个api。
特此请求 Microsoft 添加 public api EnumWindowsEx 或 EnumAllWindows,以便开发人员可以安全地枚举所有 windows。我知道他们将过滤器添加到 EnumWindows 以修复显示可见但隐藏的自定义任务列表 immersive/metro/uwp windows。但是应该支持开发者获取完整列表的方法。
更新:有关如何使用此 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;
}
EnumWindows 上的 documentation 下划线:
Note For Windows 8 and later, EnumWindows enumerates only top-level windows of desktop apps.
"desktop apps"和"non desktop apps"有什么区别?
这与 metro 应用程序有关吗?
我问是因为 EnumWindows 在 Win10 中与 Win7 相比表现有些不同。
你是对的。 EnumWindows
只会找到 windows 属于非现代 (Metro) 应用程序的程序。它将获得属于传统(桌面)程序的 windows。 FindWindowEx
, according to several sources,确实适用于各种 windows,包括来自现代应用程序的那些。
另一种解决方案是使用 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 相同的列表(没有沉浸式 windows)。如果为 FALSE,则返回完整列表。
列表的第一个条目是 0x00000001 作为句柄,必须忽略。
此 api 的优点是在调用 FindWIndowEx 期间无法更改 window 列表(在构建列表期间设置了锁)
EnumWindows、EnumDesktopWindows、EnumChildWindows、FindWindow、FindWindowEx 都用这个api。
特此请求 Microsoft 添加 public api EnumWindowsEx 或 EnumAllWindows,以便开发人员可以安全地枚举所有 windows。我知道他们将过滤器添加到 EnumWindows 以修复显示可见但隐藏的自定义任务列表 immersive/metro/uwp windows。但是应该支持开发者获取完整列表的方法。
更新:有关如何使用此 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;
}