MFH
MFH

Reputation: 1734

Get handle to desktop / shell window

In one of my programs, I need to test if the user is currently focusing the desktop/shell window. Currently, I'm using GetShellWindow() from user32.dll and compare the result to GetForegroundWindow().

This approach is working until someone changes the desktop wallpaper, but as soon as the wallpaper is changed the handle from GetShellWindow() doesn't match the one from GetForegroundWindow() anymore and I don't quite get why that is. (OS: Windows 7 32bit)

Is there a better approach to check if the desktop is focused? Preferably one that won't be broken if the user changes the wallpaper?

EDIT: I designed a workaround: I'm testing the handle to have a child of class SHELLDLL_DefView. If it has, the desktop is on focus. Whilst, it's working at my PC that doesn't mean it will work all the time.

Upvotes: 10

Views: 17764

Answers (2)

AJKenny84
AJKenny84

Reputation: 71

Here is a workaround that uses GetClassName() to detect if the desktop is active:

  • When Windows first starts, the desktop's Class is "Progman"
  • After changing the wallpaper, the desktop's Class will be "WorkerW"

You can test against these to see if the desktop is focused.

[DllImport("user32.dll")]
static extern int GetForegroundWindow();

[DllImport("user32.dll")]
static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

public void GetActiveWindow() {
    const int maxChars = 256;
    int handle = 0;
    StringBuilder className = new StringBuilder(maxChars);

    handle = GetForegroundWindow();

    if (GetClassName(handle, className, maxChars) > 0) {
        string cName = className.ToString();
        if (cName == "Progman" || cName == "WorkerW") {
            // desktop is active
        } else {
            // desktop is not active
        }
    }
}

Upvotes: 7

tklepzig
tklepzig

Reputation: 638

The thing changed a little bit since there are slideshows as wallpaper available in Windows 7. You are right with WorkerW, but this works only with wallpaper is set to slideshow effect.

When there is set the wallpaper mode to slideshow, you have to search for a window of class WorkerW and check the children, whether there is a SHELLDLL_DefView. If there is no slideshow, you can use the good old GetShellWindow().

I had the same problem some months ago and I wrote a function for getting the right window. Unfortunately I can't find it. But the following should work. Only the Win32 Imports are missing:

public enum DesktopWindow
{
    ProgMan,
    SHELLDLL_DefViewParent,
    SHELLDLL_DefView,
    SysListView32
}

public static IntPtr GetDesktopWindow(DesktopWindow desktopWindow)
{
    IntPtr _ProgMan = GetShellWindow();
    IntPtr _SHELLDLL_DefViewParent = _ProgMan;
    IntPtr _SHELLDLL_DefView = FindWindowEx(_ProgMan, IntPtr.Zero, "SHELLDLL_DefView", null);
    IntPtr _SysListView32 = FindWindowEx(_SHELLDLL_DefView, IntPtr.Zero, "SysListView32", "FolderView");

    if (_SHELLDLL_DefView == IntPtr.Zero)
    {
        EnumWindows((hwnd, lParam) =>
        {
            if (GetClassName(hwnd) == "WorkerW")
            {
                IntPtr child = FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
                if (child != IntPtr.Zero)
                {
                    _SHELLDLL_DefViewParent = hwnd;
                    _SHELLDLL_DefView = child;
                    _SysListView32 = FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView"); ;
                    return false;
                }
            }
            return true;
        }, IntPtr.Zero);
    }

    switch (desktopWindow)
    {
        case DesktopWindow.ProgMan:
            return _ProgMan;
        case DesktopWindow.SHELLDLL_DefViewParent:
            return _SHELLDLL_DefViewParent;
        case DesktopWindow.SHELLDLL_DefView:
            return _SHELLDLL_DefView;
        case DesktopWindow.SysListView32:
            return _SysListView32;
        default:
            return IntPtr.Zero;
    }
}

In your case you would call GetDesktopWindow(DesktopWindow.SHELLDLL_DefViewParent); to get the top-level window for checking whether it is the foreground window.

Upvotes: 10

Related Questions