Results 1 to 13 of 13

Thread: C++/WinAPI Finding Top-Level Windows

  1. #1
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default C++/WinAPI Finding Top-Level Windows

    So it's my goal to manage windows on the Window's desktop automatically. To do this I need to select all windows which are "on the desktop." Unfortunately after having referred to this blog post and the documentation I am still left with questions.

    The blog post goes the farthest in doing what I want but in the case of multiwindowed programs like GIMP, the toolbox windows will not be listed but the main image display area will be. If anyone has ideas for what I could try to do I would appreciate it, but at the same time I would like to not grab popups and other miscellaneous windows. My intent is to mimic i3 (https://i3wm.org/) and the blog post may be as close as I can get.


    Regardless of what I do in specific some level of selection needs to take place, as a call to EnumWindows will return around ten times as many windows as are actually visible on the desktop. The majority of them are noninteractive processes or services. If anybody knows how I might filter those out, that might be a more direct way to getting what I am after.
    Last edited by R0b0t1; 05-26-2017 at 05:35 AM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  2. #2
    Join Date
    Dec 2010
    Posts
    483
    Mentioned
    30 Post(s)
    Quoted
    328 Post(s)

    Default

    Code:
    int32_t GWL_STYLE = -16;
    uint64_t WS_VISIBLE = 0x10000000L;
    uint64_t WS_BORDER = 0x00800000L;
    uint64_t TARGET_WS = WS_BORDER | WS_VISIBLE;
    
    //In your EnumWindows callback:
    if ((GetWindowLongA(hwnd, GWL_STYLE) & TARGET_WS) == TARGET_WS)
    {
    // Anything here is visible and a bordered (normal) window
    }
    Specifically, you will use the magic of GetWindowLong to get information on opened windows via its Window Styles. I also defined the actual flags in case you aren't using the appropriate headers yourself.

    Beyond the many possible options of GetWindowLong, you can also do things like enumerating a list pre-filtered based on the existence of window title, etc, to achieve more control.

    Additionally, you can look into other WIN32 capabilities such as IsIconic (and its inverse IsZoomed) to further filter results. The API provides many useful functions for window related information. All of these functions will make use of the hwnd you have access to in the EnumWindows callback.

  3. #3
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    So I tried this with IsWindowVisible instead of checking for WS_VISIBLE, and it seems like that flag is set only if the window isn't obscured. Does the documentation say anything about it?
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  4. #4
    Join Date
    Feb 2012
    Location
    Norway
    Posts
    995
    Mentioned
    145 Post(s)
    Quoted
    596 Post(s)

    Default

    This is how I've done it when i've needed something like it: github link
    But will need some extra tests, like isIconic and such, and check if it's popup window.

    Edit, expanding on @the bank;'s implementation where we only use `GetWindowLong`, much better way to do it:
    pascal Code:
    function IsVisibleOnDesktop(handle: HWND): Boolean;
    var
      style: LongInt;
      TARGET_WS := (WS_BORDER or WS_VISIBLE);
    begin
      style := User32.GetWindowLong(handle, GWL_STYLE);
      Result := (style and TARGET_WS = TARGET_WS) and (style and WS_POPUP = 0);
    end;
    - Updated the test file in my repo to this.
    Last edited by slacky; 05-26-2017 at 09:11 AM.
    !No priv. messages please

  5. #5
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Alright, I was able to test and that does seem to work. I'm not sure what I did differently before. I'll need to do some testing to see what IsRealWindow accepts in addition to WS_BORDER and WS_VISIBLE.

    One minor issue remains - new style Windows Store apps (such as "Settings") seem to persist after close. Somewhere on that blog I referenced earlier I read that they are cached by the OS similar to how Android caches applications. A user has to go out of their way to close them.

    Does anyone know any way to check if a window is a cached application?


    To see what I mean, run this code:

    C++ Code:
    #include <windows.h>
    #include <stdlib.h>

    #include <iostream>
    using namespace std;

    BOOL CALLBACK EnumWindowsCallback(
        _In_ HWND   hWnd,
        _In_ LPARAM lParam
    );

    int
    main(int argc, char *argv[])
    {
        EnumWindows(EnumWindowsCallback, 0);
        return 0;
    }

    BOOL CALLBACK
    EnumWindowsCallback(
        _In_ HWND   hWnd,
        _In_ LPARAM lParam
    )
    {
        int nMax = GetWindowTextLengthW(hWnd) + 1;
        LPWSTR lpStr = new wchar_t[nMax];

        if (!(nMax - 1))
            return TRUE;
       
        if (!GetWindowTextW(hWnd, lpStr, nMax)) {
            // TODO: Call SetLastError.
            // TODO: Return FALSE to stop enumeration.
        }
       
        LONG lStyle;
        if (!(lStyle = GetWindowLong(hWnd, GWL_STYLE))) {
            // TODO: Call SetLastError.
            // TODO: Return FALSE to stop enumeration.
        }
       
        // These conditions are fulfilled by windows with a desktop presence.
        if ((lStyle & (WS_BORDER | WS_VISIBLE)) != (WS_BORDER | WS_VISIBLE))
            return TRUE;
       
        wcout << lpStr << std::endl;
        delete[] lpStr;
       
        return TRUE;
    }

    If you open the Settings program (labelled "Trusted Windows Store app" in the start menu) and then close it, you should still see it in the output of this program.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  6. #6
    Join Date
    Dec 2010
    Posts
    483
    Mentioned
    30 Post(s)
    Quoted
    328 Post(s)

    Default

    The Windows apps you describe I believe are all WPF applications, someone can feel free to correct me if I'm wrong though.

    Therefore, you should be able to scan the process for a handle to any PresentationFramework library and therefore identify WPF applications.

    EDIT:
    Come to think of it, they're probably UWP apps, not WPF. Off the top of my head, I am unsure what libraries UWP needs to uniquely make use of, but I am sure you could do the same process as above with a library unique to UWP applications.

  7. #7
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Simplest solution:

    Check if the window frame is valid OR check if the window is a taskbar icon or system tray icon. `IsIconified` should work or `GetWindowRect`. Otherwise you might have to look into the DWM manager API.
    I am Ggzz..
    Hackintosher

  8. #8
    Join Date
    Feb 2012
    Location
    Norway
    Posts
    995
    Mentioned
    145 Post(s)
    Quoted
    596 Post(s)

    Default

    Do you still see them if you also test `.. (lStyle & WS_POPUP == 0)`? I added that in the above post where I extended on The Bank's answer if you didn't notice, to do just that; avoid all these cached windows.
    Last edited by slacky; 05-28-2017 at 06:13 PM.
    !No priv. messages please

  9. #9
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    Quote Originally Posted by Brandon View Post
    Simplest solution:

    Check if the window frame is valid OR check if the window is a taskbar icon or system tray icon. `IsIconified` should work or `GetWindowRect`. Otherwise you might have to look into the DWM manager API.
    The problem is I do want to match minimized windows. I want to avoid matching all windows with no discernable desktop presence.


    Quote Originally Posted by slacky View Post
    Do you still see them if you also test `.. (lStyle & WS_POPUP == 0)`? I added that in the above post where I extended on The Bank's answer if you didn't notice, to do just that; avoid all these cached windows.
    Checking for WS_POPUP doesn't seem to work. The Settings application has the WS_POPUP style, but it has it at all times. I still want to detect that window if it is actually visible.

    I don't actually know how WPF or UWP styles are implemented. It might just be the design of the application that sets specific styles and there are enough styles to run the gamut of most acceptable window style combinations (that is: there needs to be a really specific test to see if a store application is suspended). If anybody has another API reference page I will trawl through it looking for settings that might be related to background operation but my efforts to this point haven't turned up anything promising - just things like checking if it's minimized, etc.

    I can use the blog's implementation of IsAltTabWindow to filter further, but like I originally mentioned this will skip auxiliary process windows like GIMPs tool panes.
    Last edited by R0b0t1; 05-28-2017 at 07:22 PM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  10. #10
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    @the bank suggested checking the results of GetClientRect to see if they were valid. Unfortunately, they are valid for cached applications. I'm going to see if I can get a list of all running processes or all running background processes, which is where the Settings app appears in the task list.

    My only concern is that I might need to compare a string somewhere. If I have to do that it's possible that locale settings or other quirks could mess up window detection.

    EDIT: Alright, I'm actually getting conflicting output. After forcibly closing the Settings application from task manager it will now exit when the X is clicked. However, it will show up in the window list with a size of 0, 0 and will report being minimized. I don't know what to make of this.

    I guess I'll have to use what I have to filter windows and fix it later.
    Last edited by R0b0t1; 05-29-2017 at 01:17 AM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

  11. #11
    Join Date
    Feb 2011
    Location
    The Future.
    Posts
    5,600
    Mentioned
    396 Post(s)
    Quoted
    1598 Post(s)

    Default

    Quote Originally Posted by R0b0t1 View Post
    @the bank suggested checking the results of GetClientRect to see if they were valid. Unfortunately, they are valid for cached applications. I'm going to see if I can get a list of all running processes or all running background processes, which is where the Settings app appears in the task list.

    My only concern is that I might need to compare a string somewhere. If I have to do that it's possible that locale settings or other quirks could mess up window detection.

    EDIT: Alright, I'm actually getting conflicting output. After forcibly closing the Settings application from task manager it will now exit when the X is clicked. However, it will show up in the window list with a size of 0, 0 and will report being minimized. I don't know what to make of this.

    I guess I'll have to use what I have to filter windows and fix it later.

    If you have Visual Studio installed, run Spy++64.exe or Spy++.exe. Either one should be fine (preferably the 64-bit one of course for 64-bit processes with 64-bit handles).

    Run it and drag the crosshairs on the target window.. This will tell you everything about a window but visibly in a nice compact UI. I have a hunch that you can discern the difference between the windows using their ClassName because StoreApps might use a specific WindowClass. Perhaps it will also display a difference in Window Creation Flags. This is honestly the best option. I'd do it for you if I had VS installed still, but I don't atm..
    Last edited by Brandon; 05-29-2017 at 02:49 AM.
    I am Ggzz..
    Hackintosher

  12. #12
    Join Date
    Dec 2007
    Posts
    2,112
    Mentioned
    71 Post(s)
    Quoted
    580 Post(s)

    Default

    You can also just download the standalone. http://mdb-blog.blogspot.co.uk/2017/...016-06-20.html

    There's a chance it won't run. If it doesn't run, have to get the relevant microsoft visual c++ redist from: https://www.microsoft.com/en-us/down....aspx?id=53840

  13. #13
    Join Date
    Dec 2006
    Location
    Banville
    Posts
    3,914
    Mentioned
    12 Post(s)
    Quoted
    98 Post(s)

    Default

    I don't see the button to click to use the crosshairs, but I still found a window which appears to be the Settings app. Unfortunately there's nothing distinctive - it has WS_SYSMENU style, but that's probably just because of the type of app it is.

    The window class is "ApplicationFrameWindow" which makes me think other WPF or UWP apps will share this class. There might be no way to tell if an application has been suspended. There is definitely a way to find out, because there is a "Background processes" section in Task Manager, but the reading I've done hasn't gotten me a solution yet.

    Help still appreciated,
    Last edited by R0b0t1; 05-30-2017 at 05:14 AM.
    The jealous temper of mankind, ever more disposed to censure than
    to praise the work of others, has constantly made the pursuit of new
    methods and systems no less perilous than the search after unknown
    lands and seas.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •