Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EnumWindows function in Win10 enumerates only desktop apps

The documentation on EnumWindows underscores:

Note For Windows 8 and later, EnumWindows enumerates only top-level windows of desktop apps.

What is the difference between "desktop apps" and "non desktop apps"?

Is this related to metro apps?

I ask because EnumWindows is behaving somewhat different in Win10 compared with Win7.

like image 847
JobaDiniz Avatar asked Jul 05 '16 13:07

JobaDiniz


1 Answers

Another solution is to use the undocumented api from win32u.dll, it has the prototype:

   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
   );

Pass it in a HWND list with Max entries, set all other parameters to zero, the output Cnt gives the number of returned entries. If the resultcode is STATUS_BUFFER_TOO_SMALL then reallocate the list with more entries and try again.

Compared to pre-Win10 versions a parameter RemoveImmersive is added. If TRUE then the same list is returned as EnumWindows (without immersive windows). If FALSE then the full list is returned.

The first entry of the list is 0x00000001 as a handle and must be ignored.

The advantage of this api is that the is no posibility of changing of the window list during calls to FindWIndowEx (a lock is set during building of the list)

The EnumWindows, EnumDesktopWindows, EnumChildWindows, FindWindow, FindWindowEx all use this api.

Hereby a request to Microsoft to add a public api EnumWindowsEx or EnumAllWindows so developers have a safe method to enumerate all windows. I understand they added the filter to EnumWindows to fix custom tasklists out there which display visible but cloaked immersive/metro/uwp windows. But a method should be supported for developers to get the full list.

UPDATE: Example on how to use this api, InitWin32uDLL does a runtime load of win32u.dll, and lib_NtUserBuildHwndListW10 is the GetProcAddress pointer

/********************************************************/
/* 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;
}
like image 123
Arnoud Mulder Avatar answered Oct 05 '22 22:10

Arnoud Mulder