C#如何确定hwnd是否在托盘图标中

3

我正在尝试获取当前系统托盘图标的句柄。

我所做的是通过使用以下代码来获取系统托盘窗口的句柄:

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName);


[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


static IntPtr GetSystemTrayHandle()
{           
    IntPtr hWndTray = FindWindow("Shell_TrayWnd", null);
    if (hWndTray != IntPtr.Zero)
    {
        hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "TrayNotifyWnd", null);
        if (hWndTray != IntPtr.Zero)
        {
            hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "SysPager", null);
            if (hWndTray != IntPtr.Zero)
            {
                hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "ToolbarWindow32", null);
                return hWndTray;
            }
        }
    }

    return IntPtr.Zero;
}

我从这里获取了以下代码:如何查找系统托盘中列出的应用程序和服务?

然后,我使用以下代码枚举了该hWnd的子窗口:

[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);

public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
    EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
    EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
    if (listHandle.IsAllocated)
    listHandle.Free();
}
return result;
}

private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
    throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
//  You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}

public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);

我从这里获取了以下代码:enumchildwindows (user32)

然后我像这样使用它:

IntPtr temp = GetSystemTrayHandle();
List<IntPtr> tst = GetChildWindows(temp);
MessageBox.Show(tst.Count.ToString());
foreach (IntPtr ip in tst)
{
    MessageBox.Show(ip.ToString());
}

但是 List<IntPtr> tst 是空的... 有什么想法为什么会这样?是我做错了什么吗?

3个回答

7
ToolbarWindow32的“子项”并不是窗口,而是工具栏按钮。您可以使用TB_BUTTONCOUNT消息检索按钮数量,使用TB_GETBUTTONINFO消息检索有关该按钮的信息。由于该窗口属于另一个进程,仅使用SendMessage()很难做到,因为指针无效。最终是徒劳的,这样的按钮不包含有关与图标相关联的进程类型的任何信息。这是在shell中隐藏的信息,您无法访问它。

2
TB_BUTTONCOUNT返回可见图标的数量,我如何获取隐藏的托盘图标呢? - Ron
2
这是另一个窗口,标题为“系统促进通知区域”,是TrayNotifyWnd的子级。至少在Win7上是这样。使用Spy++来查找这些内容。 - Hans Passant

1

没有子句柄。您可以通过Spy++进行验证。

它不是托管子控件,而是直接呈现和处理诸如工具提示之类的内容。

enter image description here


0

我检查了一下,如果窗口在桌面上打开,则具有样式:

WS_VISIBLE=true
WS_MINIMIZE=false

如果窗口在任务栏中:

WS_VISIBLE=true
WS_MINIMIZE=true

如果窗口在系统托盘中:
WS_VISIBLE=false
WS_MINIMIZE=true

因此,您可以使用样式来确定窗口是否在托盘中:

public IsWindowFromTray(hWnd)
{    
    bool isMinimized = Win32Natives.IsIconic(hWnd);
    bool isVisible = Win32Natives.IsWindowVisible(hWnd);

    return isMinimized && !isVisible;
}

对于大多数应用程序,它都可以正常工作。

附注:我使用了PInvoke。

[DllImport("user32.dll")]
public static extern bool IsIconic(IntPtr hWnd);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd);

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