获取桌面/Shell窗口的句柄

8
在我的一个程序中,我需要测试用户当前是否专注于桌面/Shell窗口。目前我使用user32.dll中的GetShellWindow()函数并将结果与GetForegroundWindow()函数的结果进行比较。
这种方法在桌面壁纸未更改时有效,但是一旦更改了壁纸,GetShellWindow()函数返回的句柄与GetForegroundWindow()函数返回的句柄不匹配,我不太明白为什么会这样。(操作系统:Windows 7 32位)
有没有更好的方法来检查桌面是否处于焦点状态?最好是一种不会因用户更改壁纸而失效的方法?
编辑:我设计了一种解决方法:测试句柄是否具有"SHELLDLL_DefView"类的子项。如果有,则桌面处于焦点状态。虽然在我的PC上可以工作,但不能保证它始终有效...

你尝试过使用GetDesktopWindow吗? - MK.
我想我没有。我可能错了,但我记得曾经读过 ShellWindow 是实际的桌面(自Win95以来存在的那个),而 DesktopWindow 是已经存在并实际位于 ShellWindow 后面的那个。 - MFH
2
是的,你说得对,GetShellWindow 是正确的选择。http://blogs.microsoft.co.il/blogs/pavely/archive/2011/06/18/getshellwindow-vs-getdesktopwindow.aspx 那么在更改壁纸后,你会得到哪个进程窗口呢? - MK.
GetShellWindow -> Progman,这对我来说是正确的。我在桌面上得到的句柄是WorkerW类,没有标题... - MFH
WorkerW 有以下子项:"" SHELLDLL_DefView -> "FolderView" SysListView32 -> "" SysHeader32 - MFH
显示剩余3条评论
2个回答

8
自从Windows 7推出幻灯片壁纸后,事情有了一些改变。你提到的WorkerW是对的,但只适用于壁纸设置为幻灯片效果时。
当壁纸模式设置为幻灯片时,您需要搜索一个类为WorkerW的窗口,并检查子项中是否有SHELLDLL_DefView。如果没有幻灯片,则可以使用老旧的GetShellWindow()
几个月前我也遇到了同样的问题,并编写了一个函数来获取正确的窗口。不幸的是,我找不到它了。但以下内容应该有效。只是缺少Win32导入:
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;
    }
}

在您的情况下,您需要调用 GetDesktopWindow(DesktopWindow.SHELLDLL_DefViewParent); 来获取顶层窗口,以检查它是否为前台窗口。


谢谢,我已经自己想到了一个解决方法。我认为你的答案并不完全正确,因为我还没有幻灯片,所以GetShellWindow()失败了... - MFH
保存和恢复桌面图标位置 - Ella Sharakanski

6

以下是一种使用 GetClassName() 来检测桌面是否处于活动状态的解决方法:

  • 当 Windows 第一次启动时,桌面的类名为 "Progman"
  • 更改壁纸后,桌面的类名将变为 "WorkerW"

您可以根据这些进行测试,以确定桌面是否处于焦点状态。

[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
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接