EnumWindows function in Win10 enumerates only desktop apps

后端 未结 2 1676
既然无缘
既然无缘 2020-12-19 12:50

The documentation on EnumWindows underscores:

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

相关标签:
2条回答
  • 2020-12-19 13:29

    You are correct. EnumWindows will only find windows that belong to programs that aren't Modern (Metro) apps. It will get windows that belong to traditional (desktop) programs. FindWindowEx, according to several sources, does work all kinds of windows, including those from Modern apps.

    0 讨论(0)
  • 2020-12-19 13:41

    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;
    }
    
    0 讨论(0)
提交回复
热议问题